From b4f7613f81f673b928eeecc0aa81420aaaf94eb4 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 10 Mar 2011 00:58:11 +0100 Subject: [PATCH 001/496] Asciidoced documentation for rfc.sh and put it into the docs --- admin/rfc.sh | 113 +++++++++++++++++++--------------- doc/technical/infra/index.txt | 2 +- doc/technical/infra/rfcsh.txt | 73 ++++++++++++++++++++++ 3 files changed, 138 insertions(+), 50 deletions(-) create mode 100644 doc/technical/infra/rfcsh.txt diff --git a/admin/rfc.sh b/admin/rfc.sh index 06f050613..0c2c37338 100755 --- a/admin/rfc.sh +++ b/admin/rfc.sh @@ -4,64 +4,79 @@ shopt -s extglob function usage() { - less <<"EOF" -Script to maintain Lumiera RFC's + less -F <<"EOF" +Lumiera RFC maintenance script +============================== + +// Note: the source of this documentation is maintained +// directly admin/rfc.sh in its usage() function +// edit it only there and then regenerate +// doc/devel/technical/infra/ with: +// ./admin/rfc.sh help >doc/technical/infra/rfcsh.txt + +Usage +----- -usage: ./admin/rfc.sh [options] -options: - title - Quoted string used as RFC title - rfc - Name of the RFC, smart matched, unique - rfcs - Name of the RFC, smart matched, multiple - regex - Regex matched against the content of a RFC - chapter - Heading of a section +Commands (with and [optional] parameters) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -commands (with and [optional] parameters): - find [regex] - List all matching RFC's (matching 'regex') - show [regex] - Read RFC's (matching 'regex') - create - Create a new RFC - edit <rfc> [chapter] - Edit RFC at chapter - asciidoc <rfc> - pass the rfc.txt to 'asciidoc' command - comment <rfc> - Add a new comment to a RFC - draft <rfc> - Change RFC to Draft state - final <rfc> - Change RFC to Final state - park <rfc> - Change RFC to Parked state - drop <rfc> - Change RFC to Dropped state - supersede <rfc> <new> - Supersede RFC with a new RFC - discard <rfc> - Delete an RFC - help - Show this help - process - Do automatic maintenance work - wrap <rfcs> - canonical reformatting + find <rfcs> [regex]:: List all matching RFC's (matching `regex`) + show <rfcs> [regex]:: Read RFC's (matching `regex`) + create <title>:: Create a new RFC + edit <rfc> [chapter]:: Edit RFC at chapter + asciidoc <rfc>:: pass the rfc.txt to `asciidoc` command + comment <rfc>:: Add a new comment to a RFC + draft <rfc>:: Change RFC to Draft state + final <rfc>:: Change RFC to Final state + park <rfc>:: Change RFC to Parked state + drop <rfc>:: Change RFC to Dropped state + supersede <rfc> <new>:: Supersede RFC with a new RFC + discard <rfc>:: Delete an RFC + help:: Show this help + process:: Do automatic maintenance work + wrap <rfcs>:: canonical reformatting -Smart matching: - RFC names don't need to be given exactly, they use a globbing pattern. - This is: - - case insensitive - - whitespaces are ignored - - '*' stands for any number of parameters - - '?' is any single character - - when starting with '/' they are matched against the begin of the name - - some regex operators work too - 'find' and 'show' can operate on many matches so the given rfc name doesn't - need to be unique. The other commands will complain when the RFC name given - doesn't resolve to one unique RFC. +Option types +~~~~~~~~~~~~ -Notes: - When less presents multiple files one can go forth and back with the ':n' - and ':p' commands. + title:: Quoted string used as RFC title + rfc:: Name of the RFC, smart matched, unique + rfcs:: Name of the RFC, smart matched, multiple + regex:: Regex matched against the content of a RFC + chapter:: Heading of a section - The tile for 'create' should be a normal document title. Possibly quoted - since it may contain spaces and not too long. The filename is this title - in CamelCase with all spaces and special characters removed. - Chapter machching single lines containing this word, special asciidoc - comments in the form '//word:.*' and asciidoc block attributes '[word.*]' - on a single line. When a chapter pattern is not unique, the last one is - picked. +Smart matching +-------------- - rfc.sh executes git add/rm/mv commands, but never commits. One should do a - commit as soon he finished editing. +RFC names don't need to be given exactly, they use a globbing pattern. +This is: + - case insensitive + - whitespaces are ignored + - `*` stands for any number of parameters + - `?` is any single character + - when starting with `/` they are matched against the begin of the name + - some regex operators work too +`find` and `show` can operate on many matches so the given rfc name doesn't +need to be unique. The other commands will complain when the RFC name given +doesn't resolve to one unique RFC. + +When `less` presents multiple files one can go forth and back with the `:n` +and `:p` commands. + +The tile for `create` should be a normal document title. Possibly quoted +since it may contain spaces and not too long. The filename is this title +in CamelCase with all spaces and special characters removed. + +Chapter machching single lines containing this word, special asciidoc +comments in the form `//word:.*` and asciidoc block attributes `[word.*]` +on a single line. When a chapter pattern is not unique, the last one is +picked. + +rfc.sh executes git add/rm/mv commands, but never commits. One should do a +commit as soon he finished editing. EOF } diff --git a/doc/technical/infra/index.txt b/doc/technical/infra/index.txt index f1c903af9..0f677e635 100644 --- a/doc/technical/infra/index.txt +++ b/doc/technical/infra/index.txt @@ -8,6 +8,6 @@ used for building and maintaining documentation and website. * see link:../build/index.html[separate page for the Buildsystem] * generating the link:MenuGen.html[navigation menu] for the website - + * how to use the link:rfcsh.html[rfc.sh] script for maintaining RFC's diff --git a/doc/technical/infra/rfcsh.txt b/doc/technical/infra/rfcsh.txt new file mode 100644 index 000000000..40732fed2 --- /dev/null +++ b/doc/technical/infra/rfcsh.txt @@ -0,0 +1,73 @@ +Lumiera RFC maintenance script +============================== + +// Note: the source of this documentation is maintained +// directly admin/rfc.sh in its usage() function +// edit it only there and then regenerate +// doc/devel/technical/infra/ with: +// ./admin/rfc.sh help >doc/technical/infra/rfcsh.txt + +Usage +----- + + ./admin/rfc.sh <command> [options] + +Commands (with <mandatory> and [optional] parameters) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + find <rfcs> [regex]:: List all matching RFC's (matching `regex`) + show <rfcs> [regex]:: Read RFC's (matching `regex`) + create <title>:: Create a new RFC + edit <rfc> [chapter]:: Edit RFC at chapter + asciidoc <rfc>:: pass the rfc.txt to `asciidoc` command + comment <rfc>:: Add a new comment to a RFC + draft <rfc>:: Change RFC to Draft state + final <rfc>:: Change RFC to Final state + park <rfc>:: Change RFC to Parked state + drop <rfc>:: Change RFC to Dropped state + supersede <rfc> <new>:: Supersede RFC with a new RFC + discard <rfc>:: Delete an RFC + help:: Show this help + process:: Do automatic maintenance work + wrap <rfcs>:: canonical reformatting + +Option types +~~~~~~~~~~~~ + + title:: Quoted string used as RFC title + rfc:: Name of the RFC, smart matched, unique + rfcs:: Name of the RFC, smart matched, multiple + regex:: Regex matched against the content of a RFC + chapter:: Heading of a section + + +Smart matching +-------------- + +RFC names don't need to be given exactly, they use a globbing pattern. +This is: + - case insensitive + - whitespaces are ignored + - `*` stands for any number of parameters + - `?` is any single character + - when starting with `/` they are matched against the begin of the name + - some regex operators work too +`find` and `show` can operate on many matches so the given rfc name doesn't +need to be unique. The other commands will complain when the RFC name given +doesn't resolve to one unique RFC. + +When `less` presents multiple files one can go forth and back with the `:n` +and `:p` commands. + +The tile for `create` should be a normal document title. Possibly quoted +since it may contain spaces and not too long. The filename is this title +in CamelCase with all spaces and special characters removed. + +Chapter machching single lines containing this word, special asciidoc +comments in the form `//word:.*` and asciidoc block attributes `[word.*]` +on a single line. When a chapter pattern is not unique, the last one is +picked. + +rfc.sh executes git add/rm/mv commands, but never commits. One should do a +commit as soon he finished editing. + From 7e17839c911d7b330966b961aacaef54ee545e8a Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Thu, 10 Mar 2011 01:31:44 +0100 Subject: [PATCH 002/496] Add some short description about the tools and scripts in the ./admin dir --- doc/technical/infra/index.txt | 58 +++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/doc/technical/infra/index.txt b/doc/technical/infra/index.txt index 0f677e635..a47abbf95 100644 --- a/doc/technical/infra/index.txt +++ b/doc/technical/infra/index.txt @@ -11,3 +11,61 @@ used for building and maintaining documentation and website. * how to use the link:rfcsh.html[rfc.sh] script for maintaining RFC's + +Other tools in the ./admin folder +--------------------------------- + +There are various other small tools and scripts in the admin folder which are +not yet fully documented (Read the Source!). + +.headercheck +This is a small tool which checks that all headers are sufficiently standalone +and include anything they need. In future we might extend this to find out +bogus includes by predefining (-D) headerguards they include, effectively +disableing this include for a test. + +.render_icon.py +used by the buildsystem to translate svg icons to bitmaps (png) + +.treeinfo.sh +Generates a report about the projects directory structure by parsing the +DIR_INFO files (which have to be maintained manually) + +A DIR_INFO is just a small text file where the first line shall give a short +summary about the purpose of this directory followed by arbitary text giving +more details. + +.git_hooks +A directory containing example git hooks for signing and pushing on commit. +Only use them when you know what you are doing. + +.lumiera_c_skeleton +A script which instantiates a new pair of .c/.h files. + +.vg-run.sh +convenience wrapper script to run an executable with valgrind +using "typical options"; output goes to a logfile, which is +fed to less afterwards. + +A valgrind suppression file is generated automatically, in +case there is an executable "vgsuppression" located in the same +directory as the target executable. + +.gnu-indent +call the "indent" tool with parameters suitable for our (GNU derrived) coding style + +.testrunner +Script driving make and running a test, possibly under valgrind. Works only +for autotools build. + +.vgsuppression_gen.sh +Use an existing 'vgsupression' executable to generate a valgrind supression file. + +Bitrot +------ + +Scripts which are not in use and need some makeover to be removed + + testfilter.sh + rsync_docs_to_lumieraorg.sh + From 5bb9250ddb649413003edcbced11ccbcc52ae589 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 13 Mar 2011 08:35:04 +0100 Subject: [PATCH 003/496] page to document the debian packaging and depot management --- doc/technical/infra/debianDepot.txt | 98 +++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 doc/technical/infra/debianDepot.txt diff --git a/doc/technical/infra/debianDepot.txt b/doc/technical/infra/debianDepot.txt new file mode 100644 index 000000000..555b0956f --- /dev/null +++ b/doc/technical/infra/debianDepot.txt @@ -0,0 +1,98 @@ +Lumiera Debian Package and Depot maintainance +============================================= +:Author: Hermann Voßeler deb@ichthyostega.de +:Date: 3/2011 + +//Menu: label Debian Depot + +This Debian-Depot is part of the Lumiera build infrastructure. +It is managed automatically, based on the link:http://mirrorer.alioth.debian.org/[reprepro] tool by Bernhard Link + +WARNING: Page under construction + +The Lumiera debian package +-------------------------- +As our whole infrastructure relies heavily on the Git version management tool, it's only natural +also to organise the (debian) packaging with the help of Git. Fortunatlely, there is a nifty tool +called link:https://honk.sigxcpu.org/piki/projects/git-buildpackage/[git-buildpackage], written +by Guido Günther exactly for this purpuse: It treats the _debianisation_ as a branch in the +Git repository, forking off the mainline at the release point. + +For Lumiera, this _debianisation branch_ is called 'deb' and can be found in the +link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=summary[git:/git.lumiera.org/lumiera/debian] repository. + +To (re)build the debian package + +. +git clone git:/git.lumiera.org/lumiera/debian+ +. +mkdir pack.deb+ +. +cd debian+ +. +git-buildpackage --git-upstream-branch=+_RELEASE-TAG_ + +-- here _RELEASE-TAG_ denotes the point in the Git history, which should become +the reference source and be packaged into the *.orig.tar.gz. Usually, it's just +sufficient to use 'master' for that purpose. + + +Debian-Depot for installation via Apt +------------------------------------- +While it is easy just to build a debian package and then install it locally through ++dpkg -i+, if we want to support automated installation and upgrades using the Apt +package manager, we need to build up more infrastructure. The primary reason is that +for any source package, a multitude of binary packages might be built for different +architectures; moreover, we want to support several different distributions. +For this purpose,the Apt package manager requires a precisely defined folder structure, +accessible online via 'http:' or 'ftp:'. For maintaining this folder structure, there +are three concievable levels of complexy: + +minimal layout, manual maintainance:: + place everything in a single directory accessible online. Use only one pseudo-distribution + and call the (single) component \'.\' -- just dump packages into the directory and update + the packages list + +multiple distributions and components, pool directory:: + this setup is already a full-blown debian depot, just there are some limitations + on the flexibility: there might be only _one_ version of each package per distribution, + and all different distributions use a shared pool (and thus require different packages + to be labeled stringently) + +major distribution or mirror site:: + similar to the above, this is a complete debian depot, but it is backed by a database + and allows for much more flexible configuration and is highly automated. + + +For Lumiera, it seems the middle option is the right choice: while it still keeps the +infrastructure simple, it allows for multiple _distributions_ (Debian/stable, Ubuntu....) +and multiple _components_ (experimental, development, release). The mentioned 'reprepro' +tool provides this degree of functionality: Basically, this is a fixed directory structure +with some control files; the frontent is a commandline tool with a plethora of options +and quite some automation- and extension hooks. Internally, it uses an embedded BerkelyDB. + +Using 'reprepro', existing source, binary or comined packages can be _added_ (imported) +into the structure with a single command; the files comprising the package are then +added automatically to the pool directory and all the indices, directories and GPG +signatures are created and updated automatically. Previous versions of the same +package are purged, when not needed by any existing package anymore + +NOTE: *todo* show an configuraion example and list the most common handling commands + +Configuration +~~~~~~~~~~~~~ +The current configuration and the state of the Debian Depot index files is tracked +is a separate branch in the +link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=shortlog;h=refs/heads/depot[lumiera/debian] +repository. (Note: this is a somewhat unconventional setup, insofar the 'depot' branch tracks +a directory tree completely separete from the main lumiera source tree tracked by the deb branch; +I choose this layout because of the marginal relevance of this depot management; I just didn't +want to create _yet another not so useful Git repository..._) + + * primary link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=blob;f=conf/distributions;hb=refs/heads/depot[configuration] + * Logfile of imports: link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=blob;f=log/lenny.log;hb=refs/heads/depot[for Lenny] + +current setup 3/2011 +^^^^^^^^^^^^^^^^^^^^ +While later we want to automate most of this packaging business, currently it's done semi-manual. +Mostly, Ichthyo builds the packages on his local PC (or a VM) and then adds/imports them to the +'reprepro' -- changes are then propagated to lumiera.org via rsync; as kind of a backup, the +index files are also pushed to Git. + + From 5e4d3a3ca08b2e7b10beab040afa3b906ca4ae18 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 14 Mar 2011 02:24:49 +0100 Subject: [PATCH 004/496] tutorial for building from a debian source package Signed-off-by: Ichthyostega <prg@ichthyostega.de> --- doc/user/tutorials/DebianBuilding.txt | 88 +++++++++++++++++++++++++++ doc/user/tutorials/index.txt | 3 + 2 files changed, 91 insertions(+) create mode 100644 doc/user/tutorials/DebianBuilding.txt diff --git a/doc/user/tutorials/DebianBuilding.txt b/doc/user/tutorials/DebianBuilding.txt new file mode 100644 index 000000000..1409e7b1a --- /dev/null +++ b/doc/user/tutorials/DebianBuilding.txt @@ -0,0 +1,88 @@ +building from source -- the Debian Way +====================================== +:Author: Ichthyostega +:Date: 3/2011 + +When you're using a Debian-based system (e.g. Ubuntu), instead of compiling +Lumiera from source in the classical (hard) way, you might prefer building +from the Debian source package. + +Why -- what are the benefits? + + * rely on the Debian/Ubuntu package manager for sorting out the library + and build dependencies + * install Lumiera as a first-class package, like all the other software + you're using regularily. This way, the package manager ``knows'' about + the library dependencies and can't mix up things due to a system upgrade. + +Under some circumstances, these very benefits might be a drawback, though. +Sometimes you don't want to install; or you might have a version of Lumiera +installed, but want to try out a (maybe newer / development) version... + +No problem -- basically it's allways possible to run Lumiera _without installation._ +It is deliberately made such as to find its components actively, within the standard +directory structure created by the buildsystem. While, thus, Lumiera can be just +run directly from such a folder tree, the software still relies on some other +libraries, which somehow need to be installed on your system. + + +anatomy of a debian source package +---------------------------------- + +NOTE: to be written.... give a short summary of the parts making up such a package + + +building from source package +---------------------------- +Generally speaking, operations which _modify_ the installation/configuration of your +linux system require root permissions. To the contrary, just _building_ a package +should _not_ be done as root. + +. add a suitable source line to your *Apt configuration* ('/etc/apt/sources.lst') ++ +---- +deb-src http://lumiera.org/debian/ lenny experimental +---- +. get all the *build dependencies* ++ +---- +sudo apt-get build-dep nobug-dev +sudo apt-get build-dep libgdl-lum-dev +sudo apt-get build-dep lumiera +---- +. *build* using the source package. ++ +---- +apt-get source --compile nobug-dev +---- + + * After building went through without error, it might happen that you're be + prompted for signing with your GPG key. But because you're not going to upload + the created binary packages anywhere, this step can be considered optional. + + * installing the created package. ++ +Finally, you'll find several new +*.deb+ packages in the directory where you started +the build. You need to _install_ these packages then using the basic debian package +manager 'dpkg' ++ +---- +sudo dpkg -i libnobug2_201008.1-1_i386.deb nobug-dev_201008.1-1_i386.deb +---- ++ +of course, the package names, versions and architecture will vary, depending on your +situation. + + * repeat those steps to work your way up to the +lumiera+ package; build and install + Nobug, build and install libgdl-lum and finally build and install Lumiera + +. clean up. ++ +You can delete the source tree used for compiling. If you never intend to +re-install the package, you could also delete the created package and source packge +components after installing it. But especially when trying out development versions +it might be a good idea to file those packages somewhere, as we're not keeping _every_ +package in the online Lumiera debian depot. While every package could be reproduced +exactly with a bit of Git knowledge, just keeping the +*.deb+ might be more convenient. + + diff --git a/doc/user/tutorials/index.txt b/doc/user/tutorials/index.txt index d8f4fd075..f878cf4bc 100644 --- a/doc/user/tutorials/index.txt +++ b/doc/user/tutorials/index.txt @@ -1,7 +1,10 @@ Tutorials ========= +//Menu: sort children + This page contains tutorials for beginners interested in Lumiera development. * link:building.html[Building Lumiera from source] + * link:DebianBuilding.html[Building from Debian source package] * link:contributing.html[Contributing to Lumiera] From 90caeaee6c87bada1a903263a0dc9ad7603026f5 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 28 Mar 2011 04:02:27 +0200 Subject: [PATCH 005/496] Create new section for architecture design docs Signed-off-by: Ichthyostega <prg@ichthyostega.de> --- .../architecture/ArchitectureSummary.txt | 34 +++++++++++++++++++ doc/design/architecture/index.txt | 13 +++++++ doc/design/index.txt | 9 +++++ 3 files changed, 56 insertions(+) create mode 100644 doc/design/architecture/ArchitectureSummary.txt create mode 100644 doc/design/architecture/index.txt diff --git a/doc/design/architecture/ArchitectureSummary.txt b/doc/design/architecture/ArchitectureSummary.txt new file mode 100644 index 000000000..c8ceff2af --- /dev/null +++ b/doc/design/architecture/ArchitectureSummary.txt @@ -0,0 +1,34 @@ +Lumiera Architecture: Executive Summary +======================================= +:Author: Hermann Voßeler +:Email: <Ichthyostega@web.de> +:Date: Sept 2010 + +.Three Layers +Besides the (obvious) separation in GUI frontend and render engine, editing core and application logic +are treated separately. Besides enforcing clearly defined interfaces, the ability for script driven +``headless'' operation and extensibility by plug-ins are deeply rooted in the overall design. + +.Strong Separation +High-level and low-level realm remain cleanly separated, relying on a compiler-like translation mechanism. +This way, each part can be optimised for its purpose. The GUI exposes a feature-rich session model close +to the problem domain, while the engine operates on a performance optimised render nodes graph. + +.Scheduling Micro Operations +High performance calculations in the engine are organised as micro tasks to be scheduled, +allowing for adaptation to current and future hardware, expected to operate increasingly parallelised. + +.Intelligent IO +The engine is backed by an sophisticated frame cache and self adjusting I/O manager for maximising throughput +and bandwidth use. + +.Stream Type System +Instead of relying on a single media framework, data streams are abstracted and classified, allowing to +handle most wiring, detection of possible connections and conversion automatically. + +.Rules Based Configuration +Rather than driven by hard wired default behaviour or tedious manual operation, control flow is guided by +a set of rules shaped configuration, which scales better to large and complicated projects. Queries are +issued at decision points, allowing to build up a work environment customised and tailored for the needs +of specific projects. + diff --git a/doc/design/architecture/index.txt b/doc/design/architecture/index.txt new file mode 100644 index 000000000..0d6996b8e --- /dev/null +++ b/doc/design/architecture/index.txt @@ -0,0 +1,13 @@ +Design Documents: Lumiera Architecture +====================================== + +This section will feature some rather high-level considerations regarding +the architecture of the application. + +.Executive Summary +See link:ArchitectureSummary.html[here] for a short summary of the core architecture +ideas of the envisioned Lumiera Application. + + + + diff --git a/doc/design/index.txt b/doc/design/index.txt index 8ff8a6a22..1b8114d54 100644 --- a/doc/design/index.txt +++ b/doc/design/index.txt @@ -1,6 +1,15 @@ Lumiera Design Documents ======================== +// Menu : prepend child design/backend +// Menu : prepend child engine +// Menu : prepend child model +// Menu : prepend child design/gui +// Menu : prepend child architecture + +// Menu : append child plugins +// Menu : append child workflow + The goal of Lumiera is to provide a professional tool for video editing on GNU/Linux systems. The vision of the development team defines a modern design for the core of a Non-Linear Editing software. One of the key aspect of Lumiera From ef4545bc55e906bfcf844f557f602bc5d886a4bf Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 28 Mar 2011 07:01:12 +0200 Subject: [PATCH 006/496] augment the debian packaging and repository documentation --- doc/technical/infra/MenuGen.txt | 1 + doc/technical/infra/debianDepot.txt | 38 +++++++- doc/user/tutorials/DebianBuilding.txt | 128 ++++++++++++++++++++++++-- 3 files changed, 154 insertions(+), 13 deletions(-) diff --git a/doc/technical/infra/MenuGen.txt b/doc/technical/infra/MenuGen.txt index 7691f8528..060f178e4 100644 --- a/doc/technical/infra/MenuGen.txt +++ b/doc/technical/infra/MenuGen.txt @@ -204,6 +204,7 @@ supported placement directives commandline options ^^^^^^^^^^^^^^^^^^^ The behaviour of the +menugen+ script can be influenced by some options: + predefined:: using the built-in predefined nodes scan:: discover nodes debug:: dump data structure after discovery diff --git a/doc/technical/infra/debianDepot.txt b/doc/technical/infra/debianDepot.txt index 555b0956f..f599950b2 100644 --- a/doc/technical/infra/debianDepot.txt +++ b/doc/technical/infra/debianDepot.txt @@ -8,7 +8,6 @@ Lumiera Debian Package and Depot maintainance This Debian-Depot is part of the Lumiera build infrastructure. It is managed automatically, based on the link:http://mirrorer.alioth.debian.org/[reprepro] tool by Bernhard Link -WARNING: Page under construction The Lumiera debian package -------------------------- @@ -73,7 +72,20 @@ added automatically to the pool directory and all the indices, directories and G signatures are created and updated automatically. Previous versions of the same package are purged, when not needed by any existing package anymore -NOTE: *todo* show an configuraion example and list the most common handling commands +everyday usage +~~~~~~~~~~~~~~ + +import a package:: + +reprepro -V -C experimental include lenny lumiera_0.pre.01-1+maverick_i386.changes+ ++ +this adds the given binary lumiera package, together with all sources and the original +tarball to the 'lenny' repository, into the 'experimental' section + +dump out an entire repository:: + +reprepro -V export lenny+ ++ +this will __re__generate all of the indices, signatures and metadata of the 'lenny' repository + Configuration ~~~~~~~~~~~~~ @@ -88,11 +100,29 @@ want to create _yet another not so useful Git repository..._) * primary link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=blob;f=conf/distributions;hb=refs/heads/depot[configuration] * Logfile of imports: link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=blob;f=log/lenny.log;hb=refs/heads/depot[for Lenny] +[NOTE] +.some special details to note in our setup +======================================================================================================================= +- each block in the 'distributions' file defines a repository for a ``distribution'' (e.g. Lenny, Lucid, Maverick). + Within such a repo, there are sections named 'Components'. +- The _override_ files mentioned in the configuration allow to overwrite / replace arbitrary fields in the metadata of + all packages added to that distribution. +- In this setup, we enabled the 'tracking' function: thereby reprepro will keep track of the dependencies between + binary packages, signatures, debianisation patches and original upstream tarballs. Never packages overwirte older + ones -- _at any time there is at most one version of a package in the repository._ Parts not referred to anymore + are automatically discareded. In our configuration, they are moved into the 'morguedir' +- Please make sure the *gpg signing key* is in proper order, because it protects against evil spirited manipulations. + +======================================================================================================================= + + current setup 3/2011 ^^^^^^^^^^^^^^^^^^^^ While later we want to automate most of this packaging business, currently it's done semi-manual. Mostly, Ichthyo builds the packages on his local PC (or a VM) and then adds/imports them to the 'reprepro' -- changes are then propagated to lumiera.org via rsync; as kind of a backup, the -index files are also pushed to Git. - +index files are also pushed to link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=shortlog;h=refs/heads/depot[Git]. +---- +rsync -rclvz --progress --partial --delete /local/filesys/path/to/Lumirep/depot/ ichthyo@www.lumiera.org:/var/local/www_debian +---- diff --git a/doc/user/tutorials/DebianBuilding.txt b/doc/user/tutorials/DebianBuilding.txt index 1409e7b1a..5e33957b6 100644 --- a/doc/user/tutorials/DebianBuilding.txt +++ b/doc/user/tutorials/DebianBuilding.txt @@ -19,19 +19,13 @@ Under some circumstances, these very benefits might be a drawback, though. Sometimes you don't want to install; or you might have a version of Lumiera installed, but want to try out a (maybe newer / development) version... -No problem -- basically it's allways possible to run Lumiera _without installation._ +No problem -- basically it's always possible to run Lumiera _without installation._ It is deliberately made such as to find its components actively, within the standard directory structure created by the buildsystem. While, thus, Lumiera can be just run directly from such a folder tree, the software still relies on some other libraries, which somehow need to be installed on your system. -anatomy of a debian source package ----------------------------------- - -NOTE: to be written.... give a short summary of the parts making up such a package - - building from source package ---------------------------- Generally speaking, operations which _modify_ the installation/configuration of your @@ -74,15 +68,131 @@ of course, the package names, versions and architecture will vary, depending on situation. * repeat those steps to work your way up to the +lumiera+ package; build and install - Nobug, build and install libgdl-lum and finally build and install Lumiera + Nobug, maybe build and install libgdl-lum and finally build and install Lumiera . clean up. + You can delete the source tree used for compiling. If you never intend to -re-install the package, you could also delete the created package and source packge +re-install the package, you could also delete the created package and source package components after installing it. But especially when trying out development versions it might be a good idea to file those packages somewhere, as we're not keeping _every_ package in the online Lumiera debian depot. While every package could be reproduced exactly with a bit of Git knowledge, just keeping the +*.deb+ might be more convenient. +Required and recommended Debian packages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +What follows is a note about configuration for advanced users. You can safely skip and +ignore this section if in doubt. + +The Debian package manager stores for each package not only the required prerequisites, +but also some additional _recommended_ packages: Software likely to make using the given +package more convenient or improve the usage in some way. In addition, it also stores +a list of _suggested_ additional things to try. Now, since some time, the 'Apt' package +management tool by default automatically installs also the _recommended_ software, not +only the bare required prerequisites. + +While this is certainly fine for users not into the details of package management, it +has the downside of installing sometimes _a lot_ of additional software no one asked +about. Plus, all these installed packages are upgraded from time to time. An impressive +example of this kind of _bloat_ is the *asciidoc* package, which recommends to install +dockbook, an complete XML toolchain plus a TeX distribution, giving you an average of +additional 500MB to download and install. + +Of course, this behaviour can be changed. Just add the following line to your Apt configuration +---- +APT::Install-Recommends "false"; +---- +^_Disclaimer:_ please be sure you understand the consequences...^ + + +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +What follows are some more in-depth explanations and background informations + + +anatomy of a debian source package +---------------------------------- + +Debian source packages provide a standardised way of compiling software. These +packages are not intended to be _installed_ on your system; rather you just download +them and use them to build the software. + +Each Debian source package is based on an original _upstream_ tarball. In addition to +this '\*.orig.tar.gz', there is a '\*.diff.gz' which contains all the _modifications_ +done to the original upstream sources in order to compile it with Debian. Especially, +this so called ``debianisation patch'' adds a 'debian/' subdirectory to the source tree, +which then holds all the control files used by Debian package management. Finally, the +third part of each Debian source package is a _manifest_ file ('*.dsc'), which states +that this is a Debian package and contains the primary control informations, the +name and version number. Of course, these three files are named with a common name +prefix, which matches the 'official' package name and version. + +Quite frequently, a given source package generates several binary packages, e.g. the +main program, maybe a library and a documentation bundle. Because of this, often the +source package is named slightly different then the binary package, which can be a +problem sometimes. If in doubt, use the link:http://packages.debian.org[Debian package +search] or the link:http://packages.ubuntu.com[equivalent for Ubuntu] to locate packages, +versions, even individual files, or to get at the bug tracker for a package. + +interesting files in the debian subdirectory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Most of the files in that subdirectory are relevant only for the people maintaining +the package. With the exception of the following: + +- the 'debian/control' file defines the properties and *metadata* of the package. Here you'll + find the definition of the prerequisites, the name of the packager, the explanatory text. +- the top entry in the 'debian/changelog' defines the *actual package name and version* +- the actual build process is conducted by invoking several pre defined targets in the + 'debian/rules' *makefile*. But modern debian packages often make use of the ``common + debian build system'' -- basically a set of macros allowing to write these 'rules' + in a very short and concise fashion + + +commands for handling source packages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For all package building tasks, you can use an arbitrary directory of your choice. +It is recommended to build with normal user permissions (not as root). + +- 'apt-get source' PACKAGENAME downloads the three parts of the source package. You may + use both the name of a binary or the source package in this command. After downloading, + the original tarball is extracted and the debianisation patch is applied. That is, + the tree is ready for building +- 'apt-get source \--compile' PACKAGENAME does even more: it starts off the build + of a binary package after downloading and preparing the sources... +- 'dpkg-source -x' SRCPACKAGE.dsc just extracts a source package and applies the diff +- 'sudo apt-get buld-dep' PACKAGENAME fetches and installs all the prerequisites + necessary for building the denoted package. (however, it does _not_ download the + source package itself) +- 'sudo dpkg -i' BIN-PACKAGE.deb finally installs the result of your building efforts. + Note, if several packages depend on each other, you need to give them all as list + in a single command invocation. + +After having prepared the sources thusly, you need to step into the root of the +source tree, if you want to build the whole package, or even want to tweak and +modify parts. + +- 'dpkg-checkbuilddeps' checks that all the requirements of the package are satisfied. + It just prints out what is missing; this is especially useful if the fully automated + install did not work entirely, so you have to fix / reinstall parts manually +- 'dpkg-buildpackage -rfakeroot' starts the full build. This includes re-generating + the source package (and especially the diff). If you only want to build the binary + package, you can skip the diff- / source-package generation with '-b' + +prerequisites for building +~~~~~~~~~~~~~~~~~~~~~~~~~~ +For performing any compiling and packaging tasks, you need some additional software, +which by default isn't installed on a desktop system -- most notably the GNU C compiler. +On any Debian based system, you get these basic tooling by +---- +sudo apt-get install build-essential +---- + +library development packages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When building software linked against some library, we need additional 'header files' +provided together with the library. Conventionally, these headers are packaged separately +from the library and shipped as a package with a name like 'LIBNAME-dev'. You can safely +__de__install these development packages when done with building. + + + From b843546922f37b70c47242b313d2dc012dfc32b6 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 31 Mar 2011 18:43:50 +0200 Subject: [PATCH 007/496] cumulated build/release fixes up to corresponding debian/0.pre.01-3 - compile issue (digxel.hpp) - SCons missing config dependency on test-only - 32/64bit fixes --- SConstruct | 6 +- src/gui/widgets/timeline/timeline-ruler.cpp | 12 ++-- src/lib/time.cpp | 6 +- src/lib/time.h | 2 +- src/lib/time/digxel.hpp | 3 +- src/lib/time/timevalue.hpp | 9 +++ src/lib/util.hpp | 20 +++++- src/proc/asset/meta/time-grid.cpp | 2 +- tests/43session.tests | 2 +- tests/SConscript | 1 + .../proc/asset/meta/time-grid-basics-test.cpp | 6 +- tests/lib/time/quantiser-basics-test.cpp | 68 ++++++++++--------- tests/lib/time/time-formats-test.cpp | 6 +- tests/lib/time/time-quantisation-test.cpp | 4 +- tests/lib/time/time-value-test.cpp | 18 ++--- tests/lib/util-floordiv-test.cpp | 23 +++++++ tests/test.conf | 5 +- tests/test.sh | 1 + 18 files changed, 129 insertions(+), 65 deletions(-) diff --git a/SConstruct b/SConstruct index 6419b5cb7..9665854f4 100644 --- a/SConstruct +++ b/SConstruct @@ -347,10 +347,12 @@ def defineBuildTargets(env, artifacts): artifacts['corelib'] = core artifacts['support'] = lLib - artifacts['lumiera'] = ( env.Program('lumiera', ['src/lumiera/main.cpp'], LIBS=core, install=True) - + env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') + 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'], LIBS=core, install=True) + + artifacts['config'] + ) # building Lumiera Plugins artifacts['plugins'] = [] # currently none diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp index 3d2d73cf3..6c79d6af7 100644 --- a/src/gui/widgets/timeline/timeline-ruler.cpp +++ b/src/gui/widgets/timeline/timeline-ruler.cpp @@ -511,12 +511,12 @@ TimelineRuler::calculate_major_spacing() const 15l * GAVL_TIME_SCALE, 30l * GAVL_TIME_SCALE, 60l * GAVL_TIME_SCALE, - 2l * 60l * GAVL_TIME_SCALE, - 5l * 60l * GAVL_TIME_SCALE, - 10l * 60l * GAVL_TIME_SCALE, - 15l * 60l * GAVL_TIME_SCALE, - 30l * 60l * GAVL_TIME_SCALE, - 60l * 60l * GAVL_TIME_SCALE + 2ll * 60ll * GAVL_TIME_SCALE, + 5ll * 60ll * GAVL_TIME_SCALE, + 10ll * 60ll * GAVL_TIME_SCALE, + 15ll * 60ll * GAVL_TIME_SCALE, + 30ll * 60ll * GAVL_TIME_SCALE, + 60ll * 60ll * GAVL_TIME_SCALE }; for(i = 0; i < sizeof(major_spacings) / sizeof(gavl_time_t); i++) diff --git a/src/lib/time.cpp b/src/lib/time.cpp index a7efb8009..7cbe53004 100644 --- a/src/lib/time.cpp +++ b/src/lib/time.cpp @@ -96,7 +96,7 @@ lumiera_frame_duration (FrameRate const& fps) namespace { // implementation helper - inline long + inline long long calculate_quantisation (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) { time -= origin; @@ -105,9 +105,11 @@ namespace { // implementation helper } -long +int64_t lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) { + ENSURE (sizeof(int64_t) <= sizeof(long long)); + return calculate_quantisation (time, origin, grid); } diff --git a/src/lib/time.h b/src/lib/time.h index 521b5658d..fb1a7992a 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -103,7 +103,7 @@ lumiera_tmpbuf_print_time (gavl_time_t time); * @return number of the grid interval containing the given time. * @warning the resulting value is limited to (Time::Min, Time::MAX) */ -long +int64_t lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid); /** diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index 53aac3456..d49e81af4 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -72,6 +72,7 @@ #include <boost/lexical_cast.hpp> #include <tr1/functional> #include <string> +#include <cstdio> #include <cmath> using std::string; @@ -126,7 +127,7 @@ namespace time { { size_t space = std::snprintf (printbuffer_, bufsiz, formatSpec_, val); REQUIRE (space < bufsiz, "Digxel value exceeded available buffer size. " - "For showing %s, %lu+1 chars instead of just %lu+1 would be required." + "For showing %s, %zu+1 chars instead of just %zu+1 would be required." , cStr(lexical_cast<string>(val)), space, len); ///////////TICKET #197 } ENSURE (!empty()); diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index f0d7fc9c8..7e3d370ad 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -256,6 +256,9 @@ namespace time { /// direct assignment prohibited Time& operator= (Time const); + /// suppress possible direct conversions + Time(int); + public: static const Time MAX ; static const Time MIN ; @@ -269,6 +272,7 @@ namespace time { : TimeValue(calcResult) { } + explicit Time (FSecs const& fractionalSeconds); Time ( long millis @@ -314,6 +318,11 @@ namespace time { : Offset(Offset(timeSpec).abs()) { } + explicit + Duration (FSecs const& timeSpan_in_secs) + : Offset(Offset(Time(timeSpan_in_secs)).abs()) + { } + Duration (TimeSpan const& interval); Duration (ulong count, FrameRate const& fps); diff --git a/src/lib/util.hpp b/src/lib/util.hpp index ce3755c52..3fc2cb97b 100644 --- a/src/lib/util.hpp +++ b/src/lib/util.hpp @@ -59,7 +59,16 @@ namespace util { /** helper to treat int or long division uniformly */ template<typename I> - struct IDiv; + struct IDiv + { + I quot; + I rem; + + IDiv (I num, I den) + : quot(num/den) + , rem(num - quot*den) + { } + }; template<> struct IDiv<int> @@ -79,6 +88,15 @@ namespace util { { } }; + template<> + struct IDiv<long long> + : lldiv_t + { + IDiv<long long> (long long num, long long den) + : lldiv_t(lldiv (num,den)) + { } + }; + /** floor function for integer arithmetics. * Unlike the built-in integer division, this function diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp index ad3599a80..767daa41c 100644 --- a/src/proc/asset/meta/time-grid.cpp +++ b/src/proc/asset/meta/time-grid.cpp @@ -154,7 +154,7 @@ namespace meta { PGrid TimeGrid::build (Symbol gridID, FrameRate frames_per_second) { - return build (gridID,frames_per_second, Time(0)); + return build (gridID,frames_per_second, Time(0,0)); } diff --git a/tests/43session.tests b/tests/43session.tests index 85242cd65..dd86bcc31 100644 --- a/tests/43session.tests +++ b/tests/43session.tests @@ -100,7 +100,7 @@ out: TestSubMO1\(ID= [0-9]{3}\) out: TestSubMO2\(ID= [0-9]{3}\) out: TestSubMO21\(ID= [0-9]{3}\) out: specialAPI() -out: pID\(\w{8,16}\) +out: pID\(\w{6,16}\) END diff --git a/tests/SConscript b/tests/SConscript index 6920e912c..8a09b547a 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -59,6 +59,7 @@ artifacts['testsuite'] = ts = ( [ testExecutable(env, dir) for dir in ['lib','co + [ 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'] ) diff --git a/tests/components/proc/asset/meta/time-grid-basics-test.cpp b/tests/components/proc/asset/meta/time-grid-basics-test.cpp index ae6f70d53..c40ac7cbb 100644 --- a/tests/components/proc/asset/meta/time-grid-basics-test.cpp +++ b/tests/components/proc/asset/meta/time-grid-basics-test.cpp @@ -85,7 +85,7 @@ namespace test { GridBuilder spec = asset::Meta::create (myGrID); CHECK ( spec.fps_ == 1); - CHECK ( spec.origin_ == Time(0)); + CHECK ( spec.origin_ == TimeValue(0)); CHECK (!spec.predecessor_); spec.fps_ = testFps; @@ -118,8 +118,8 @@ namespace test { CHECK (!util::isnil (simplePALGrid->ident.name)); // note: name-ID filled in automatically cout << "simple PAL Grid: " << simplePALGrid->ident << endl; - CHECK (Time(2) == simplePALGrid->timeOf(50)); - CHECK (Time(2) == simplePALGrid->timeOf(FSecs(2))); + CHECK (Time(0,2) == simplePALGrid->timeOf(50)); + CHECK (Time(0,2) == simplePALGrid->timeOf(FSecs(2))); } }; diff --git a/tests/lib/time/quantiser-basics-test.cpp b/tests/lib/time/quantiser-basics-test.cpp index e790d5f8b..b503e3d29 100644 --- a/tests/lib/time/quantiser-basics-test.cpp +++ b/tests/lib/time/quantiser-basics-test.cpp @@ -42,7 +42,13 @@ namespace test{ const uint MAX_FRAMES = 25*500; const uint DIRT_GRAIN = 50; - const FSecs F25(1,25); // duration of one PAL frame + const FSecs F25(1,25); // duration of one PAL frame + + inline Time + secs (int seconds) + { + return Time(FSecs(seconds)); + } } @@ -84,7 +90,7 @@ namespace test{ uint frames = (rand() % MAX_FRAMES); FSecs dirt = (F25 / (rand() % DIRT_GRAIN)); - Time rawTime = dirt + frames*F25; + Time rawTime (dirt + frames*F25); CHECK (Time( frames *F25) <= rawTime); CHECK (Time((frames+1)*F25) > rawTime); @@ -152,51 +158,51 @@ namespace test{ { // origin at lower end of the time range FixedFrameQuantiser case1 (1, Time::MIN); - CHECK (Time(0) == case1.gridAlign(Time::MIN )); - CHECK (Time(0) == case1.gridAlign(Time::MIN +TimeValue(1) )); - CHECK (Time(1) == case1.gridAlign(Time::MIN +Time(1) )); - CHECK (Time::MAX -Time(1) > case1.gridAlign( Time(-1) )); - CHECK (Time::MAX -Time(1) <= case1.gridAlign( Time (0) )); - CHECK (Time::MAX > case1.gridAlign( Time (0) )); - CHECK (Time::MAX == case1.gridAlign( Time(+1) )); - CHECK (Time::MAX == case1.gridAlign( Time(+2) )); + CHECK (secs(0) == case1.gridAlign(Time::MIN )); + CHECK (secs(0) == case1.gridAlign(Time::MIN +TimeValue(1) )); + CHECK (secs(1) == case1.gridAlign(Time::MIN +secs(1) )); + CHECK (Time::MAX -secs(1) > case1.gridAlign( secs(-1) )); + CHECK (Time::MAX -secs(1) <= case1.gridAlign( secs (0) )); + CHECK (Time::MAX > case1.gridAlign( secs (0) )); + CHECK (Time::MAX == case1.gridAlign( secs(+1) )); + CHECK (Time::MAX == case1.gridAlign( secs(+2) )); // origin at upper end of the time range FixedFrameQuantiser case2 (1, Time::MAX); - CHECK (Time( 0) == case2.gridAlign(Time::MAX )); - CHECK (Time(-1) == case2.gridAlign(Time::MAX -TimeValue(1) )); // note: next lower frame - CHECK (Time(-1) == case2.gridAlign(Time::MAX -Time(1) )); // i.e. the same as a whole frame down - CHECK (Time::MIN +Time(1) < case2.gridAlign( Time(+2) )); - CHECK (Time::MIN +Time(1) >= case2.gridAlign( Time(+1) )); - CHECK (Time::MIN < case2.gridAlign( Time(+1) )); - CHECK (Time::MIN == case2.gridAlign( Time( 0) )); // note: because of downward truncating, - CHECK (Time::MIN == case2.gridAlign( Time(-1) )); // resulting values will already exceed - CHECK (Time::MIN == case2.gridAlign( Time(-2) )); // allowed range and thus will be clipped + CHECK (secs( 0) == case2.gridAlign(Time::MAX )); + CHECK (secs(-1) == case2.gridAlign(Time::MAX -TimeValue(1) )); // note: next lower frame + CHECK (secs(-1) == case2.gridAlign(Time::MAX -secs(1) )); // i.e. the same as a whole frame down + CHECK (Time::MIN +secs(1) < case2.gridAlign( secs(+2) )); + CHECK (Time::MIN +secs(1) >= case2.gridAlign( secs(+1) )); + CHECK (Time::MIN < case2.gridAlign( secs(+1) )); + CHECK (Time::MIN == case2.gridAlign( secs( 0) )); // note: because of downward truncating, + CHECK (Time::MIN == case2.gridAlign( secs(-1) )); // resulting values will already exceed + CHECK (Time::MIN == case2.gridAlign( secs(-2) )); // allowed range and thus will be clipped // maximum frame size is half the time range Duration hugeFrame(Offset(Time::MAX)); FixedFrameQuantiser case3 (hugeFrame); CHECK (Time::MIN == case3.gridAlign(Time::MIN )); CHECK (Time::MIN == case3.gridAlign(Time::MIN +TimeValue(1) )); - CHECK (Time::MIN == case3.gridAlign( Time(-1) )); - CHECK (Time(0) == case3.gridAlign( Time( 0) )); - CHECK (Time(0) == case3.gridAlign( Time(+1) )); - CHECK (Time(0) == case3.gridAlign(Time::MAX -TimeValue(1) )); + CHECK (Time::MIN == case3.gridAlign( secs(-1) )); + CHECK (TimeValue(0) == case3.gridAlign( secs( 0) )); + CHECK (TimeValue(0) == case3.gridAlign( secs(+1) )); + CHECK (TimeValue(0) == case3.gridAlign(Time::MAX -TimeValue(1) )); CHECK (Time::MAX == case3.gridAlign(Time::MAX )); // now displacing this grid by +1sec.... - FixedFrameQuantiser case4 (hugeFrame, Time(1)); + FixedFrameQuantiser case4 (hugeFrame, secs(1)); CHECK (Time::MIN == case4.gridAlign(Time::MIN )); CHECK (Time::MIN == case4.gridAlign(Time::MIN +TimeValue(1) )); // clipped... - CHECK (Time::MIN == case4.gridAlign(Time::MIN +Time(1) )); // but now exact (unclipped) - CHECK (Time::MIN == case4.gridAlign( Time(-1) )); - CHECK (Time::MIN == case4.gridAlign( Time( 0) )); - CHECK (Time(0) == case4.gridAlign( Time(+1) )); //.....now exactly the frame number zero - CHECK (Time(0) == case4.gridAlign(Time::MAX -TimeValue(1) )); - CHECK (Time(0) == case4.gridAlign(Time::MAX )); //.......still truncated down to frame #0 + CHECK (Time::MIN == case4.gridAlign(Time::MIN +secs(1) )); // but now exact (unclipped) + CHECK (Time::MIN == case4.gridAlign( secs(-1) )); + CHECK (Time::MIN == case4.gridAlign( secs( 0) )); + CHECK (TimeValue(0) == case4.gridAlign( secs(+1) )); //.....now exactly the frame number zero + CHECK (TimeValue(0) == case4.gridAlign(Time::MAX -TimeValue(1) )); + CHECK (TimeValue(0) == case4.gridAlign(Time::MAX )); //.......still truncated down to frame #0 // larger frames aren't possible - Duration not_really_larger(Time(10000) + hugeFrame); + Duration not_really_larger(secs(10000) + hugeFrame); CHECK (hugeFrame == not_really_larger); // frame sizes below the time micro grid get trapped diff --git a/tests/lib/time/time-formats-test.cpp b/tests/lib/time/time-formats-test.cpp index daf7f6ca1..eab218d58 100644 --- a/tests/lib/time/time-formats-test.cpp +++ b/tests/lib/time/time-formats-test.cpp @@ -148,7 +148,7 @@ namespace test{ // Extended SMPTE: extension of the axis beyond origin towards negative values smpte.hours -= 6; CHECK ("- 0:21:59:24"== string(smpte)); // representation is symmetrical to origin - CHECK (tx - Time(6*60*60) == smpte.getTime()); // Continuous time axis + CHECK (tx - Time(0,0,0,6) == smpte.getTime()); // Continuous time axis CHECK (-1 == smpte.sgn); // Note: for these negative (extended) SMPTE... CHECK (smpte.mins > 0); // ...the representation is really flipped around zero @@ -161,13 +161,13 @@ namespace test{ ++smpte; // but the orientation of the increment on the *whole* TC values is unaltered CHECK ("- 0:21:59:24"== string(smpte)); // so this actually *advanced* time by one frame CHECK (tx == smpte.getTime()); - CHECK (tx < Time(0)); + CHECK (tx < TimeValue(0)); smpte.mins -= 2*60; // now lets flip the representation again... CHECK (" 1:38:00:01"== string(smpte)); CHECK (+1 == smpte.sgn); CHECK (smpte.getTime() > 0); - CHECK (tx + Time(2*60*60) == smpte.getTime()); + CHECK (tx + Time(0,0,0,2) == smpte.getTime()); smpte.secs -= 2*60*60; // and again... CHECK (tx == smpte.getTime()); CHECK ("- 0:21:59:24"== string(smpte)); diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp index 237ce7f8e..a22988f1b 100644 --- a/tests/lib/time/time-quantisation-test.cpp +++ b/tests/lib/time/time-quantisation-test.cpp @@ -75,8 +75,8 @@ namespace test{ virtual void run (Arg arg) { - Time ref (random_or_get(arg)); - CHECK (Time(0) < ref); + Time ref (0,random_or_get(arg),0,0); + CHECK (TimeValue(0) < ref); checkSimpleUsage (ref); check_theFullStory (ref); diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index 9a1383e8b..e73354ea8 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -167,8 +167,8 @@ namespace test{ CHECK (o1 == o2); CHECK (o1 == org); - // integer interpreted as second - Time t1(1); + // time in seconds + Time t1(FSecs(1)); CHECK (t1 == TimeValue(GAVL_TIME_SCALE)); // create from fractional seconds @@ -190,7 +190,7 @@ namespace test{ CHECK (th+th == t1); CHECK (t1-th == th); CHECK (((t1-th)*=2) == t1); - CHECK (th-th == Time(0)); + CHECK (th-th == TimeValue(0)); // that was indeed a temporary and didn't affect the originals CHECK (t1 == TimeValue(GAVL_TIME_SCALE)); @@ -252,14 +252,14 @@ namespace test{ CHECK (distance == backwards.abs()); Duration len1(Time(23,4,5,6)); - CHECK (len1 == Time(FSecs(23,1000)) + Time(4 + 5*60 + 6*3600)); + CHECK (len1 == Time(FSecs(23,1000)) + Time(0, 4 + 5*60 + 6*3600)); - Duration len2(Time(-10)); // negative specs... - CHECK (len2 == Time(10));// - CHECK (len2 > zero); // will be taken absolute + Duration len2(Time(FSecs(-10))); // negative specs... + CHECK (len2 == Time(FSecs(10)));// + CHECK (len2 > zero); // will be taken absolute Duration unit(50, FrameRate::PAL); - CHECK (Time(2) == unit); // duration of 50 frames at 25fps is... (guess what) + CHECK (Time(0,2,0,0) == unit); // duration of 50 frames at 25fps is... (guess what) CHECK (FrameRate::PAL.duration() == Time(FSecs(1,25))); CHECK (FrameRate::NTSC.duration() == Time(FSecs(1001,30000))); @@ -296,7 +296,7 @@ namespace test{ CHECK (theLength == Offset(org,five).abs()); Time endpoint = interval.end(); - TimeSpan successor (endpoint, Duration(Time(2))); + TimeSpan successor (endpoint, Duration(Time(0,2,0,0))); CHECK (Offset(interval,endpoint) == Offset(org,five).abs()); CHECK (Offset(endpoint,successor.end()) == Duration(successor)); diff --git a/tests/lib/util-floordiv-test.cpp b/tests/lib/util-floordiv-test.cpp index 015faaf64..dad2a40b7 100644 --- a/tests/lib/util-floordiv-test.cpp +++ b/tests/lib/util-floordiv-test.cpp @@ -90,6 +90,7 @@ namespace test { /********************************************************************** * @test Evaluate a custom built integer floor function. + * Also known as Knuth's floor division. * This function is crucial for Lumiera's rule of quantisation * of time values into frame intervals. This rule requires time * points to be rounded towards the next lower frame border always, @@ -111,6 +112,12 @@ namespace test { { verifyBehaviour (); + verifyIntegerTypes<int>(); + verifyIntegerTypes<long>(); + verifyIntegerTypes<short>(); + verifyIntegerTypes<int64_t>(); + verifyIntegerTypes<long long int>(); + if (!isnil (arg)) runPerformanceTest(); } @@ -147,6 +154,22 @@ namespace test { } + template<typename I> + void + verifyIntegerTypes () + { + I n,d,expectedRes; + + for (int i=-12; i <= 12; ++i) + { + n = i; + d = 4; + expectedRes = floordiv (i,4); + CHECK (floordiv(n,d) == expectedRes); + } + } + + /** @test timing measurements to compare implementation details. * This test uses a sequence of random integers, where the values diff --git a/tests/test.conf b/tests/test.conf index c3abbce1c..d7109e910 100755 --- a/tests/test.conf +++ b/tests/test.conf @@ -1,4 +1,5 @@ LOGSUPPRESS='^\(\*\*[0-9]*\*\* \)\?[0-9]\{10,\}[:!] \(TRACE\|INFO\|NOTICE\|WARNING\|ERR\|TODO\|PLANNED\|FIXME\|DEPRECATED\|UNIMPLEMENTED\|RESOURCE_ANNOUNCE\|RESOURCE_ENTER\|RESOURCE_STATE\|RESOURCE_LEAVE\):' -LIMIT_VG_CPU=55 -LIMIT_VG_TIME=60 +LIMIT_CPU=120 +LIMIT_VG_CPU=115 +LIMIT_VG_TIME=120 diff --git a/tests/test.sh b/tests/test.sh index 6b4944d55..214b7641c 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -134,6 +134,7 @@ fi #valgrind false positives in 'vgsuppression'. Care must be taken that this file is simple and does #valgrind not generate true positives. #valgrind +echo "NOTE: CPU time limit: $LIMIT_CPU sec" ulimit -S -t ${LIMIT_CPU:-5} -v ${LIMIT_VSZ:-524288} valgrind="" LIMIT_TIME_REAL="$LIMIT_TIME" From 8e15e1dec0c5117501167e826c277e048c687763 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 31 Mar 2011 19:53:56 +0200 Subject: [PATCH 008/496] update the compiling-from-source tutorial Signed-off-by: Ichthyostega <prg@ichthyostega.de> --- doc/user/tutorials/building.txt | 64 ++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/doc/user/tutorials/building.txt b/doc/user/tutorials/building.txt index f7eb94bfc..0092da1cc 100644 --- a/doc/user/tutorials/building.txt +++ b/doc/user/tutorials/building.txt @@ -2,15 +2,26 @@ Building Lumiera from source ============================ Currently, after building the application, you can try out the Lumiera GUI -and you can run the test suite. +and you can run the test suite. This tutorial details the basic procedure +to compile Lumiera on a Linux (or comparable) system. We'll assume that +the reader has acquired basic _commandline survival skills_ and is able +to figure out the usage of the Git version management system from the +documentation (but we'll mention each actually required command +explicitly) -For building Lumiera, besides the GNU C/C++ compiler (Version 4.X), you will -need: +NOTE: just compiling Lumieara on a _Debian-based_ system (e.g. Ubuntu) + is much simpler using the Debian source package. See the separate + link:DebianBuilding.html[tutorial page] for this (or the general + link:/debian/[instructions for installing on Debian/Ubuntu]). + The purpose of this tutorial here is to show you the elementary + and generic steps to compile Lumiera from source. - * http://git-scm.com/[git] - * http://www.gnu.org/software/libtool/[libtool] - * http://www.boost.org/[Boost libraries] - * http://gmerlin.sourceforge.net/[GAVL library] +For building Lumiera, besides the GNU C/C++ compiler (Version 4.X), you will need: + + * link:http://git-scm.com/[Git VCS] + * link:http://www.scons.org/[SCons build system] + * link:http://www.boost.org/[Boost libraries] + * link:http://gmerlin.sourceforge.net/[GAVL library] * *NoBug* (see below) The GUI depends on: @@ -22,17 +33,17 @@ TIP: Generally speaking, when you want to build software, you need the _development_ version of the packages, containing the headers and pre-built libraries to link against. Usually, these packages are named `-devel` or `-dev` -For most Debian based systems, e.g. Ubuntu, you can install these dependencies -with: +For Debian based systems, e.g. Ubuntu, you can install these dependencies with: ------------------------------------------------------------- -sudo apt-get install build-essential autoconf git-core libboost-dev \ -libboost-program-options-dev libboost-regex-dev libgavl-dev libgdl-1-dev \ -libgtkmm-2.4-dev librsvg2-dev libtool libxv-dev scons valgrind ------------------------------------------------------------- +------------------------------------------------------------------------------------- +sudo apt-get install build-essential autoconf scons valgrind libtool git-core \ +libboost-dev libboost-program-options-dev libboost-regex-dev libboost-filesystem-dev \ +libgavl-dev libgdl-1-dev libgtkmm-2.4-dev librsvg2-dev libxv-dev +------------------------------------------------------------------------------------- Optionally, you may also want to install the *gtk2-engines* package. + Build directory --------------- @@ -85,8 +96,9 @@ Installing GDL The *GNOME Docking library* is generally available through your package manager, but we contributed some improvements, which are only available in the very -recent development versions of GDL. Thus, for now we created a special package, -which doesn't interfere with an existing (older) installation of GDL. +recent development versions of GDL. If your distribution provides a GDL >= 2.27, +you're fine. Otherwise we've created a special package, which doesn't interfere +with an existing (older) installation of GDL. Ubuntu 9.04 note:: intltool-update is not patched, you must add +/usr/share/intltool-debian/+ @@ -121,7 +133,7 @@ variable. Either way, check that all libraries are accessible and OK: ------------------------------------------------------------ -sudo ldconfig -v | grep 'gdl-lum\|nobug' +sudo ldconfig -v | grep 'nobug' ------------------------------------------------------------ and you should get a list of the libraries, part of which should look like this: @@ -132,7 +144,9 @@ and you should get a list of the libraries, part of which should look like this: libgdl-lum.so.0 -> /usr/local/lib/libgdl-lum.so.0.0.0 ------------------------------------------------------------ -or similar. If any if this libs are not listed, investigate why before continuing. +or similar. The same holds true for 'libgdl-lum.so.0' if you needed to build it +explicitly for your system. If any if these libraries are not listed, +investigate why before continuing. Building Lumiera @@ -175,20 +189,14 @@ maybe build and run the test suite by issuing `scons check` or `make check` This will take some time. -NOTE: you can not _install_ Lumiera currently. Do not try it, it will not work. -Just run it from the build directory. +NOTE: you do not need to _install_ Lumiera. It will find its required files +relative to the generated directory structure, which is freely relocatable +as a whole. Just invoke the +target/lumiera+ executable. The current +working directory is not relevant. After the build has finished successfully, you should be able to start Lumiera. Currently, this will bring up the GUI, without any further functionality - * for autotools build: issue `./lumiera` (from within the `build` subdirectory) - * for scons build: -+ -------------------- -cd bin -./lumiera -------------------- - You should see something like: image:{l}/images/lumiera_gui_small.png[Current Lumiera GUI Screenshot] From 012ed9ab36b8ae65919936d4d2de0c965951a0ee Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 31 Mar 2011 20:44:03 +0200 Subject: [PATCH 009/496] update intro, fix image links Signed-off-by: Ichthyostega <prg@ichthyostega.de> --- doc/devel/images/lumiera_big_graph.png | Bin 0 -> 48959 bytes doc/user/intro/intro.txt | 132 ++++++++--------- doc/user/intro/lumiera_big_graph.svg | 195 +++++++++++++++---------- 3 files changed, 181 insertions(+), 146 deletions(-) create mode 100644 doc/devel/images/lumiera_big_graph.png diff --git a/doc/devel/images/lumiera_big_graph.png b/doc/devel/images/lumiera_big_graph.png new file mode 100644 index 0000000000000000000000000000000000000000..ec08d77fd102b64b6c138983619b130c5ddbbc05 GIT binary patch literal 48959 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 literal 0 HcmV?d00001 diff --git a/doc/user/intro/intro.txt b/doc/user/intro/intro.txt index 1bc008b7c..0e32a454b 100644 --- a/doc/user/intro/intro.txt +++ b/doc/user/intro/intro.txt @@ -119,7 +119,7 @@ Processing of Video (and audio) data can be generalized as graph processing (more precisely ``directed acyclic graphs''). Data flows on the edges of these graphs and is processed in the nodes. -image:lumiera_big_graph.svg[Example for a graph] +image:{imgd}/lumiera_big_graph.png[Example for a graph] When we look at this model we discover that we only need to build xref:builder[->] such graphs, the nodes themselves can be seen as black boxes @@ -162,7 +162,7 @@ The visible Universe Now its time to take a look at the prelimary Lumiera GUI: -image:lumiera_screenshot.png[Screenshot of Lumiera] +image:{l}/images/lumiera_gui_small.png[Current Lumiera GUI Screenshot] The GUI is a plugin by itself and only one way to work Lumiera, it will become possible to create special-purpose GUIs or control Lumiera in different ways, @@ -178,6 +178,7 @@ xref:screenconcept[->] to adapt to different workplaces and workflows. Viewer ~~~~~~ +[red]#to be written# // only one viewer type used for everything // how is audio integrated in the viewer @@ -194,21 +195,20 @@ Viewer Transport Controls ~~~~~~~~~~~~~~~~~~ -// current gui is not final (transport controls attached to the timeline) -// It is not finally decided where transport controls will be integrated -// possibly as its own gui element +The layout in current gui is rather preliminary -- it is not finally decided +where transport controls will be integrated; possibly as its own gui element -// This are devices either controlled by widgets or by some input device (midi -// etc) so their gui may look differently. -// Either way they connect to a Play Controler xref.. in the core which -// manages playing and cursor positioning. +This are devices either controlled by widgets or by some input device +(MIDI, control surface, etc) so their gui may look differently. +Either way they connect to a Play Controler xref.. in the core which +manages playing and cursor positioning. -// thus there will be some gui facility to attach Transport controls to Play -// Controllers. Transport controls are ganged when they attach to the same -// Play Controler. +thus there will be some gui facility to attach Transport controls to Play +Controllers. Transport controls are ganged when they attach to the same +Play Controler. -// just playing some footage for preview creates a simple internal timeline, -// no magic here. +just playing some footage for preview creates a simple internal timeline, +no magic here. // TODO: bit unrelated, think about how ganging controls in general should // work, also for faders, masks and so on @@ -220,12 +220,13 @@ Transport Controls Timeline View ~~~~~~~~~~~~~ -// hierarchical tracks, not just a stack +hierarchical tracks, not just a list + +Format Independent Timeline, one can put anything on the timeline. +the busses constrain what kind of data is pulled out and in turn the +builder creates a processing graph which does the necessary conversions and +stuff. -// Format Independent Timeline, one can put anything on the timeline. -// the busses constrain what kind of data is pulled out and in turn the -// builder creates a processing graph which does the necessary conversions and -// stuff. // Q: how to handle interaction, for example when some conversion can only be // done in a lossy way and some conversion node may or may not be inserted // (i mean gui wise)? @@ -241,19 +242,23 @@ Timeline View // a special case of a selection. -// Busses -// ~~~~~~ -// How will the gui define busses? +Busses +~~~~~~ +The GUI provides a separate _bus view_, showing the master busses (subgroups) +in a manner similar to an audio mixing desk. Any bus is just a means to collect +and sum up the output of a specific kind of media (video, audio, number of channels), +produced by various processing elements and other busses. Conceptionally, these global +busses are considered a part of the timeline Asset View ~~~~~~~~~~ -// Manages all assets available in one project. -// * source media/footage/soundfiles -// * prepared clips, known subprojects -// * all available effects and transitions -// * internal artefacts like sequences and automation data sets +Manages all assets available in one project. + * source media/footage/soundfiles + * prepared clips, known subprojects + * all available effects and transitions + * internal artefacts like sequences and automation data sets // First this will be simply implemented showing data loaded into the session // and all available plugins/effects @@ -272,18 +277,23 @@ Asset View // the contents of a clip bin can be played like a storyboard +'''''''' + Dark Matter ----------- -// coarse overview about things the user does not see but have some contact -// with, details later... +[red]#to be written# +coarse overview about things the user does not see but have some contact +with, details later... -Now lets take a look under the hood. Lumiera + +Now lets take a look under the hood. Session storage ~~~~~~~~~~~~~~~ +[red]#to be written# // not generateable data @@ -314,15 +324,19 @@ output destination, layering, fade control, audio pan,... Rendering Engine ~~~~~~~~~~~~~~~~ -// rendering +[red]#to be written# +rendering... [[builder]] +[red]#to be written# -// rules system - +rules system +[red]#to be written# I/O Subsystem ~~~~~~~~~~~~~ +[red]#to be written# + // file handling // vault, work, cache // repositories @@ -331,12 +345,16 @@ I/O Subsystem Configuration ~~~~~~~~~~~~~ +[red]#to be written# + // configuration system // serves defaults, actual data are stored in the session Plugins/Interfaces ~~~~~~~~~~~~~~~~~~ +[red]#to be written# + // explain whats it is // portability // versioning @@ -345,6 +363,7 @@ Plugins/Interfaces Rendering Video --------------- +[red]#to be written# // describe the flow of data to render a frame @@ -354,6 +373,7 @@ Rendering Video Pulling a Frame ~~~~~~~~~~~~~~~ +[red]#to be written# // special cases, // case studies, @@ -372,45 +392,19 @@ Pulling a Frame // show some case-studies that someone gets a feel how plugins work -[[pluginstack]] - -//Audio +[red]#TODO# Consider integrating the following things into the document above - -// TODO Following things need to be integrated into the document above - - -* [[plugins]] -* [[timeline]] -* [[demultiplexer]] -* [[multiplexer]] -* [[encoder]] -* [[pull]] -bus defines rendering format -* [[caching]] -* [[profiling]] -* [[rendernode]] -* [[frameserver]] -* [[screenconcept]] -* [[busses]] - -// gui/screen concepts -// perspectives - -// Automation - - - - -// 3 layered model - - -such as that we only need to pull exactly what we need, -posibly down to the pixel, this also allows efficient caching xref:caching[->] -for intermediate data, to be reused later. - +* plugins +* timeline +* pull +* bus defines rendering format +* caching +* frameserver +* screenconcept / perspectives +* automation +* 3 layered model diff --git a/doc/user/intro/lumiera_big_graph.svg b/doc/user/intro/lumiera_big_graph.svg index 67cf01f66..d995d913f 100644 --- a/doc/user/intro/lumiera_big_graph.svg +++ b/doc/user/intro/lumiera_big_graph.svg @@ -33,20 +33,21 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1.0518407" - inkscape:cx="564.81009" - inkscape:cy="277.96054" + inkscape:zoom="1.4875274" + inkscape:cx="444.97376" + inkscape:cy="280.84291" inkscape:document-units="px" - inkscape:current-layer="layer2" + inkscape:current-layer="layer4" showgrid="true" - inkscape:window-width="1274" - inkscape:window-height="774" + inkscape:window-width="1668" + inkscape:window-height="1020" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="0" inkscape:snap-to-guides="false" inkscape:snap-grids="true" - inkscape:snap-global="true"> + inkscape:snap-global="true" + gridtolerance="5"> <inkscape:grid type="xygrid" id="grid2854" @@ -63,8 +64,36 @@ <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title /> + <dc:title>Lumiera Data Flow Overview</dc:title> + <cc:license + rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" /> + <dc:date>Summer 2010</dc:date> + <dc:creator> + <cc:Agent> + <dc:title>Christian Thäter</dc:title> + </cc:Agent> + </dc:creator> + <dc:publisher> + <cc:Agent> + <dc:title>Lumiera.org</dc:title> + </cc:Agent> + </dc:publisher> </cc:Work> + <cc:License + rdf:about="http://creativecommons.org/licenses/by-sa/3.0/"> + <cc:permits + rdf:resource="http://creativecommons.org/ns#Reproduction" /> + <cc:permits + rdf:resource="http://creativecommons.org/ns#Distribution" /> + <cc:requires + rdf:resource="http://creativecommons.org/ns#Notice" /> + <cc:requires + rdf:resource="http://creativecommons.org/ns#Attribution" /> + <cc:permits + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /> + <cc:requires + rdf:resource="http://creativecommons.org/ns#ShareAlike" /> + </cc:License> </rdf:RDF> </metadata> <g @@ -130,10 +159,10 @@ x="279.23798" y="309.81433" /> <rect - style="fill:#ffffff;stroke:#b3b3b3;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" + style="fill:#ffffff;stroke:#b3b3b3;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect4737" width="110" - height="80" + height="94.736069" x="230" y="354.40942" /> <rect @@ -158,19 +187,16 @@ style="display:inline"> <path style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 502,426.40942 40,0" + d="M 500,424.40942 L 540,424.40942" id="path3467" /> - <path - style="fill:none;stroke:#b3b3b3;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 502,456.40942 40,0" - id="path3471" /> <path style="fill:none;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 502,486.40942 38.57143,-0.51948" - id="path3473" /> + d="M 500,464.40942 L 540,464.40942" + id="path3473" + sodipodi:nodetypes="cc" /> <path - style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 2;stroke-dashoffset:0" - d="m 502,441.40942 40,0" + style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2, 2;stroke-dashoffset:1;stroke-opacity:1" + d="M 500,444.40942 L 540,444.40942" id="path3475" /> <path style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 2;stroke-dashoffset:0" @@ -272,6 +298,11 @@ d="M 365,74.580418 L 474.92023,194.40942" id="path2552" sodipodi:nodetypes="cc" /> + <path + id="path3336" + d="M 345,336.82264 L 475,289.40942 L 475,194.40942" + style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 2;stroke-dashoffset:0" + sodipodi:nodetypes="ccc" /> </g> <g inkscape:label="Elements" @@ -668,10 +699,10 @@ <g style="display:inline" id="g3629-3" - transform="translate(15,140)"> + transform="translate(15,149.9825)"> <path transform="translate(0,527.95272)" - d="m 265,284.40942 a 15,5 0 1 1 -30,0 15,5 0 1 1 30,0 z" + d="M 265,284.40942 A 15,5 0 1 1 235,284.40942 A 15,5 0 1 1 265,284.40942 z" sodipodi:ry="5" sodipodi:rx="15" sodipodi:cy="284.40942" @@ -681,7 +712,7 @@ sodipodi:type="arc" /> <path transform="translate(0,527.95272)" - d="m 265,279.40942 a 15,5 0 1 1 -30,0 15,5 0 1 1 30,0 z" + d="M 265,279.40942 A 15,5 0 1 1 235,279.40942 A 15,5 0 1 1 265,279.40942 z" sodipodi:ry="5" sodipodi:rx="15" sodipodi:cy="279.40942" @@ -691,7 +722,7 @@ sodipodi:type="arc" /> <path transform="translate(0,527.95272)" - d="m 265,274.40942 a 15,5 0 1 1 -30,0 15,5 0 1 1 30,0 z" + d="M 265,274.40942 A 15,5 0 1 1 235,274.40942 A 15,5 0 1 1 265,274.40942 z" sodipodi:ry="5" sodipodi:rx="15" sodipodi:cy="274.40942" @@ -701,7 +732,7 @@ sodipodi:type="arc" /> <path transform="translate(0,527.95272)" - d="m 265,269.40942 a 15,5 0 1 1 -30,0 15,5 0 1 1 30,0 z" + d="M 265,269.40942 A 15,5 0 1 1 235,269.40942 A 15,5 0 1 1 265,269.40942 z" sodipodi:ry="5" sodipodi:rx="15" sodipodi:cy="269.40942" @@ -892,10 +923,10 @@ <g style="display:inline" id="g3629-3-2" - transform="translate(70,140)"> + transform="translate(70,149.9825)"> <path transform="translate(0,527.95272)" - d="m 265,284.40942 a 15,5 0 1 1 -30,0 15,5 0 1 1 30,0 z" + d="M 265,284.40942 A 15,5 0 1 1 235,284.40942 A 15,5 0 1 1 265,284.40942 z" sodipodi:ry="5" sodipodi:rx="15" sodipodi:cy="284.40942" @@ -905,7 +936,7 @@ sodipodi:type="arc" /> <path transform="translate(0,527.95272)" - d="m 265,279.40942 a 15,5 0 1 1 -30,0 15,5 0 1 1 30,0 z" + d="M 265,279.40942 A 15,5 0 1 1 235,279.40942 A 15,5 0 1 1 265,279.40942 z" sodipodi:ry="5" sodipodi:rx="15" sodipodi:cy="279.40942" @@ -915,7 +946,7 @@ sodipodi:type="arc" /> <path transform="translate(0,527.95272)" - d="m 265,274.40942 a 15,5 0 1 1 -30,0 15,5 0 1 1 30,0 z" + d="M 265,274.40942 A 15,5 0 1 1 235,274.40942 A 15,5 0 1 1 265,274.40942 z" sodipodi:ry="5" sodipodi:rx="15" sodipodi:cy="274.40942" @@ -925,7 +956,7 @@ sodipodi:type="arc" /> <path transform="translate(0,527.95272)" - d="m 265,269.40942 a 15,5 0 1 1 -30,0 15,5 0 1 1 30,0 z" + d="M 265,269.40942 A 15,5 0 1 1 235,269.40942 A 15,5 0 1 1 265,269.40942 z" sodipodi:ry="5" sodipodi:rx="15" sodipodi:cy="269.40942" @@ -1108,12 +1139,12 @@ <text xml:space="preserve" style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial" - x="285" + x="296.88394" y="319.40942" id="text3434"><tspan sodipodi:role="line" id="tspan3436" - x="285" + x="296.88394" y="319.40942">Busses</tspan></text> <text xml:space="preserve" @@ -1128,73 +1159,74 @@ <text xml:space="preserve" style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial" - x="374" + x="383.98251" y="364.40942" id="text3430-0"><tspan sodipodi:role="line" id="tspan3432-2" - x="374" + x="383.98251" + y="364.40942">Viewer</tspan></text> + <text + xml:space="preserve" + style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;text-anchor:start;text-align:start;writing-mode:lr;line-height:90%" + x="460" + y="174.40942" + id="text3434-2" + sodipodi:linespacing="90%"><tspan + sodipodi:role="line" + id="tspan2554">Switch-</tspan><tspan + sodipodi:role="line" + id="tspan2556">board</tspan></text> + <text + xml:space="preserve" + style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial" + x="550" + y="304.40942" + id="text4506"><tspan + sodipodi:role="line" + id="tspan4508" + x="550" + y="304.40942">Data on Disk</tspan></text> + <text + xml:space="preserve" + style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial" + x="550" + y="364.40942" + id="text4510"><tspan + sodipodi:role="line" + id="tspan4512" + x="550" y="364.40942">Viewer</tspan></text> <text xml:space="preserve" style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial" - x="460" - y="174.40942" - id="text3434-2"><tspan - sodipodi:role="line" - id="tspan3436-9" - x="460" - y="174.40942">Busses</tspan></text> - <text - xml:space="preserve" - style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial" - x="548.41583" - y="296.5054" - id="text4506"><tspan - sodipodi:role="line" - id="tspan4508" - x="548.41583" - y="296.5054">Data on Disk</tspan></text> - <text - xml:space="preserve" - style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial" - x="550.85516" - y="348.0954" - id="text4510"><tspan - sodipodi:role="line" - id="tspan4512" - x="550.85516" - y="348.0954">Viewer</tspan></text> - <text - xml:space="preserve" - style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial" - x="545.9765" - y="397.85779" + x="545" + y="399.40942" id="text4514"><tspan sodipodi:role="line" id="tspan4516" - x="545.9765" - y="397.85779">Node/Plugin</tspan></text> + x="545" + y="399.40942">Node/Plugin</tspan></text> <text xml:space="preserve" style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial" - x="549.39154" - y="426.15408" + x="545" + y="424.40942" id="text4518"><tspan sodipodi:role="line" id="tspan4520" - x="549.39154" - y="426.15408">Build Connection between Nodes</tspan></text> + x="545" + y="424.40942">Build Connection between Nodes</tspan></text> <text xml:space="preserve" style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial" - x="549.39154" - y="442.74155" + x="545" + y="444.40942" id="text4522"><tspan sodipodi:role="line" id="tspan4524" - x="549.39154" - y="442.74155">Temporary Connection between node</tspan></text> + x="545" + y="444.40942">Temporary Connection between node</tspan></text> <text xml:space="preserve" style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial" @@ -1208,12 +1240,21 @@ <text xml:space="preserve" style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial" - x="255" + x="290" y="109.40942" id="text4711"><tspan sodipodi:role="line" id="tspan4713" - x="255" + x="290" y="109.40942">Preprocessing</tspan></text> + <text + id="text3328" + y="464.40942" + x="545" + style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%" + xml:space="preserve" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan3332">External Connection</tspan></text> </g> </svg> From 7275720812d4092e258e59801ae4983fcc534962 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 31 Mar 2011 20:50:33 +0200 Subject: [PATCH 010/496] fix menu entry --- doc/design/architecture/ArchitectureSummary.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/design/architecture/ArchitectureSummary.txt b/doc/design/architecture/ArchitectureSummary.txt index c8ceff2af..4ddd3b4ba 100644 --- a/doc/design/architecture/ArchitectureSummary.txt +++ b/doc/design/architecture/ArchitectureSummary.txt @@ -4,6 +4,8 @@ Lumiera Architecture: Executive Summary :Email: <Ichthyostega@web.de> :Date: Sept 2010 +//Menu: label Summary + .Three Layers Besides the (obvious) separation in GUI frontend and render engine, editing core and application logic are treated separately. Besides enforcing clearly defined interfaces, the ability for script driven From de07ca03f4fab02edf7bf8607c5ea432f987b346 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 1 Apr 2011 20:44:03 +0200 Subject: [PATCH 011/496] fix some Title levels --- doc/index.txt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/doc/index.txt b/doc/index.txt index ad0c5d3ee..60dbc239d 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -1,14 +1,14 @@ -== Documentation += Documentation = This documentation section contains documentation for both users and developers. -=== User +== User == The user manual containing the full reference for using Lumiera : * link:user/manual.html[User Manual] _(planned)_ -=== Developer +== Developer == Informations about the structure of Lumiera, they give an overview on the main components of the application : * link:design/index.html[Design Documents] @@ -17,7 +17,11 @@ Programming-oriented details about the functional layers of Lumiera as well as a * link:technical/index.html[Technical Documents] -=== Presentation documents -Documents that you can use as you wish to present or promote Lumiera : - * link:/media/index.html[Whitepapers and Presentations] +== Media and Presentations == + +This section holds documents and materials used to present and promote Lumiera, +articles, summaries and whitepapers + -> link:/media/index.html[Index] + + From 1e1c3706dcf9ed600354f79a58f11ef256afac83 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 5 Apr 2011 00:44:30 +0200 Subject: [PATCH 012/496] update some DIR_INFO entries --- DIR_INFO | 2 +- data/DIR_INFO | 2 +- doc/design/DIR_INFO | 1 + doc/technical/DIR_INFO | 1 + doc/user/DIR_INFO | 2 +- src/DIR_INFO | 2 +- src/backend/DIR_INFO | 2 +- src/common/DIR_INFO | 8 +------- src/gui/DIR_INFO | 1 + src/lumiera/DIR_INFO | 2 +- src/tool/DIR_INFO | 2 +- target/DIR_INFO | 2 +- target/modules/DIR_INFO | 2 +- tests/DIR_INFO | 2 +- tests/components/DIR_INFO | 2 +- tests/lib/DIR_INFO | 1 + wiki/DIR_INFO | 2 +- 17 files changed, 17 insertions(+), 19 deletions(-) create mode 100644 doc/design/DIR_INFO create mode 100644 doc/technical/DIR_INFO create mode 100644 src/gui/DIR_INFO create mode 100644 tests/lib/DIR_INFO diff --git a/DIR_INFO b/DIR_INFO index f05e9a881..53261a52b 100644 --- a/DIR_INFO +++ b/DIR_INFO @@ -1 +1 @@ -The root dir for the Lumiera project +Root of the main Lumiera project tree diff --git a/data/DIR_INFO b/data/DIR_INFO index fc5c22ea2..f691b6d45 100644 --- a/data/DIR_INFO +++ b/data/DIR_INFO @@ -1 +1 @@ -Additional configuration, icons, graphics etc. to be installed with Lumiera +Additional configuration, icons, graphics for install diff --git a/doc/design/DIR_INFO b/doc/design/DIR_INFO new file mode 100644 index 000000000..4f75272b5 --- /dev/null +++ b/doc/design/DIR_INFO @@ -0,0 +1 @@ +design and concepts diff --git a/doc/technical/DIR_INFO b/doc/technical/DIR_INFO new file mode 100644 index 000000000..307341317 --- /dev/null +++ b/doc/technical/DIR_INFO @@ -0,0 +1 @@ +technical documentation diff --git a/doc/user/DIR_INFO b/doc/user/DIR_INFO index 61d585d2f..9bffac5af 100644 --- a/doc/user/DIR_INFO +++ b/doc/user/DIR_INFO @@ -1 +1 @@ -user documentation in texinfo +user documentation and manual diff --git a/src/DIR_INFO b/src/DIR_INFO index b6c7308d3..b7c705736 100644 --- a/src/DIR_INFO +++ b/src/DIR_INFO @@ -1 +1 @@ -root of sourcecode tree +Lumiera applilcation sourcecode tree diff --git a/src/backend/DIR_INFO b/src/backend/DIR_INFO index 0c5dfc094..c046837b6 100644 --- a/src/backend/DIR_INFO +++ b/src/backend/DIR_INFO @@ -1,2 +1,2 @@ -data and caching backend +Lumiera backend layer sourcecode diff --git a/src/common/DIR_INFO b/src/common/DIR_INFO index e3b6d818a..8b648aa43 100644 --- a/src/common/DIR_INFO +++ b/src/common/DIR_INFO @@ -1,7 +1 @@ -Common application services (liblumieracommon) - -contains: - * the config system - * commandline parsing - * interfaces and plugin loading - * lua interpreter (planned) +Common application services diff --git a/src/gui/DIR_INFO b/src/gui/DIR_INFO new file mode 100644 index 000000000..cad7376e2 --- /dev/null +++ b/src/gui/DIR_INFO @@ -0,0 +1 @@ +Lumiera GTK GUI diff --git a/src/lumiera/DIR_INFO b/src/lumiera/DIR_INFO index fc536d0ed..2a31026e8 100644 --- a/src/lumiera/DIR_INFO +++ b/src/lumiera/DIR_INFO @@ -1 +1 @@ -The Lumiera application shell +The Lumiera application diff --git a/src/tool/DIR_INFO b/src/tool/DIR_INFO index c8933be3e..db1f8c765 100644 --- a/src/tool/DIR_INFO +++ b/src/tool/DIR_INFO @@ -1 +1 @@ -source code of supplementary tools to be built alongside with Lumiera +source code of supplementary tools diff --git a/target/DIR_INFO b/target/DIR_INFO index 8764b3989..7a85b70c2 100644 --- a/target/DIR_INFO +++ b/target/DIR_INFO @@ -1 +1 @@ -Lumiera program package tree, holding executable(s) and libraries to be built +Lumiera Application binary tree, executable(s) and modules diff --git a/target/modules/DIR_INFO b/target/modules/DIR_INFO index ffdc4efcb..21643380f 100644 --- a/target/modules/DIR_INFO +++ b/target/modules/DIR_INFO @@ -1 +1 @@ -Lumiera subsystems and other dynamically loadable application components +Lumiera subsystems and dynamically loadable components diff --git a/tests/DIR_INFO b/tests/DIR_INFO index cd5d69231..f6bbbc963 100644 --- a/tests/DIR_INFO +++ b/tests/DIR_INFO @@ -1 +1 @@ -test suite +Lumiera test suite sourcecode diff --git a/tests/components/DIR_INFO b/tests/components/DIR_INFO index 55fe55def..a01bda331 100644 --- a/tests/components/DIR_INFO +++ b/tests/components/DIR_INFO @@ -1 +1 @@ -Testsuite sourcecode tree +core components testsuite diff --git a/tests/lib/DIR_INFO b/tests/lib/DIR_INFO new file mode 100644 index 000000000..7855cfb57 --- /dev/null +++ b/tests/lib/DIR_INFO @@ -0,0 +1 @@ +support library testsuite diff --git a/wiki/DIR_INFO b/wiki/DIR_INFO index 2bbb2fa6b..c9eb35989 100644 --- a/wiki/DIR_INFO +++ b/wiki/DIR_INFO @@ -1 +1 @@ -tiddlywiki for semi-persistent documentation +TiddlyWiki to document ongoing work From 7a45d6df13610e16d7dac3a83c316955a539e3f1 Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Thu, 14 Apr 2011 02:40:19 +0200 Subject: [PATCH 013/496] FIX: filename processing and git handling in admin/rfs.sh --- admin/rfc.sh | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/admin/rfc.sh b/admin/rfc.sh index 0c2c37338..19e6ff41a 100755 --- a/admin/rfc.sh +++ b/admin/rfc.sh @@ -223,7 +223,7 @@ function find_chapter() -function process() +function process_file() { local file="$1" local path="${1%/*}" @@ -233,18 +233,27 @@ function process() *Final*) if [[ "$path" != "./doc/devel/rfc" ]]; then git mv "$file" "./doc/devel/rfc" + else + git add "$file" fi ;; *Idea*|*Draft*) if [[ "$path" != "./doc/devel/rfc_pending" ]]; then git mv "$file" "./doc/devel/rfc_pending" + else + git add "$file" fi ;; *Parked*|*Dropped*) if [[ "$path" != "./doc/devel/rfc_dropped" ]]; then git mv "$file" "./doc/devel/rfc_dropped" + else + git add "$file" fi ;; + *) + git add "$file" + ;; esac } @@ -334,7 +343,7 @@ function change_state() local comment=".State -> $state$nl//add reason$nl $(date +%c) $(git config --get user.name) <$(git config --get user.email)>$nl" edit_state "$name" "$state" "$comment" edit "$name" -4 "endof_comments" - process "$name" + process_file "$name" } @@ -347,7 +356,7 @@ process) for file in $(find ./doc/devel/rfc* -name '*.txt'); do echo "process $file" - process "$file" + process_file "$file" done : ;; @@ -375,15 +384,14 @@ create) else source ./doc/devel/template/new_rfc.sh >"./doc/devel/rfc_pending/${name}.txt" edit "./doc/devel/rfc_pending/${name}.txt" 2 abstract - git add "./doc/devel/rfc_pending/${name}.txt" - process "./doc/devel/rfc_pending/${name}.txt" + process_file "./doc/devel/rfc_pending/${name}.txt" fi ;; edit) name=$(unique_name "$1") if [[ "$name" ]]; then edit "${name}" 2 "$2" - git add "$name" + process_file "$name" fi ;; asciidoc) @@ -396,21 +404,18 @@ draft) name=$(unique_name "$1") if [[ "$name" ]]; then change_state "$name" Draft - git add "$name" fi ;; park) name=$(unique_name "$1") if [[ "$name" ]]; then change_state "$name" Parked - git add "$name" fi ;; final) name=$(unique_name "$1") if [[ "$name" ]]; then change_state "$name" Final - git add "$name" fi ;; supersede) @@ -420,21 +425,19 @@ supersede) newname="${newname%.txt}" if [[ "$name" && "$newname" ]]; then change_state "$name" "Superseded by $newname" - git add "$name" fi ;; drop) name=$(unique_name "$1") if [[ "$name" ]]; then change_state "$name" Dropped - git add "$name" fi ;; comment) name=$(unique_name "$1") if [[ "$name" ]]; then add_comment "${name}" - git add "$name" + process_file "$name" fi ;; discard) @@ -446,6 +449,7 @@ discard) wrap) find_rfc "$1" | while read file; do smart_wrap "$file" 80 + process_file "$file" done ;; smart_wrap) From 5beeba0b7fccd08f5890673be592599aa33a58f9 Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Thu, 14 Apr 2011 02:51:00 +0200 Subject: [PATCH 014/496] create a new dir for 'parked' RFC's --- admin/rfc.sh | 9 ++++++++- doc/devel/rfc_parked/DIR_INFO | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 doc/devel/rfc_parked/DIR_INFO diff --git a/admin/rfc.sh b/admin/rfc.sh index 19e6ff41a..65fdd76c6 100755 --- a/admin/rfc.sh +++ b/admin/rfc.sh @@ -244,7 +244,14 @@ function process_file() git add "$file" fi ;; - *Parked*|*Dropped*) + *Parked*) + if [[ "$path" != "./doc/devel/rfc_parked" ]]; then + git mv "$file" "./doc/devel/rfc_parked" + else + git add "$file" + fi + ;; + *Dropped*) if [[ "$path" != "./doc/devel/rfc_dropped" ]]; then git mv "$file" "./doc/devel/rfc_dropped" else diff --git a/doc/devel/rfc_parked/DIR_INFO b/doc/devel/rfc_parked/DIR_INFO new file mode 100644 index 000000000..2025e64bd --- /dev/null +++ b/doc/devel/rfc_parked/DIR_INFO @@ -0,0 +1 @@ +design proposals which are deferred From 88d59a9e1bd35778e24cbfa547ca80e54c536e1d Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Thu, 14 Apr 2011 02:52:06 +0200 Subject: [PATCH 015/496] moved parked RFC's into the new rfc_parked dir --- doc/devel/{rfc_dropped => rfc_parked}/GitSubmoduleTransistion.txt | 0 doc/devel/{rfc_dropped => rfc_parked}/SkillsCollection.txt | 0 doc/devel/{rfc_dropped => rfc_parked}/TodoLists.txt | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename doc/devel/{rfc_dropped => rfc_parked}/GitSubmoduleTransistion.txt (100%) rename doc/devel/{rfc_dropped => rfc_parked}/SkillsCollection.txt (100%) rename doc/devel/{rfc_dropped => rfc_parked}/TodoLists.txt (100%) diff --git a/doc/devel/rfc_dropped/GitSubmoduleTransistion.txt b/doc/devel/rfc_parked/GitSubmoduleTransistion.txt similarity index 100% rename from doc/devel/rfc_dropped/GitSubmoduleTransistion.txt rename to doc/devel/rfc_parked/GitSubmoduleTransistion.txt diff --git a/doc/devel/rfc_dropped/SkillsCollection.txt b/doc/devel/rfc_parked/SkillsCollection.txt similarity index 100% rename from doc/devel/rfc_dropped/SkillsCollection.txt rename to doc/devel/rfc_parked/SkillsCollection.txt diff --git a/doc/devel/rfc_dropped/TodoLists.txt b/doc/devel/rfc_parked/TodoLists.txt similarity index 100% rename from doc/devel/rfc_dropped/TodoLists.txt rename to doc/devel/rfc_parked/TodoLists.txt From 4972ca2af893c6a79c11aa197afaf2c4893d0150 Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Thu, 14 Apr 2011 03:21:14 +0200 Subject: [PATCH 016/496] FIX: rfc.sh git mv/add logic --- admin/rfc.sh | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/admin/rfc.sh b/admin/rfc.sh index 65fdd76c6..ae0b8255e 100755 --- a/admin/rfc.sh +++ b/admin/rfc.sh @@ -227,41 +227,29 @@ function process_file() { local file="$1" local path="${1%/*}" + local basename="${1##*/}" + local destpath="$path" local state=$(grep '^\*State\* *' "$file") case "$state" in *Final*) - if [[ "$path" != "./doc/devel/rfc" ]]; then - git mv "$file" "./doc/devel/rfc" - else - git add "$file" - fi + destpath="./doc/devel/rfc" ;; *Idea*|*Draft*) - if [[ "$path" != "./doc/devel/rfc_pending" ]]; then - git mv "$file" "./doc/devel/rfc_pending" - else - git add "$file" - fi + destpath="./doc/devel/rfc_pending" ;; *Parked*) - if [[ "$path" != "./doc/devel/rfc_parked" ]]; then - git mv "$file" "./doc/devel/rfc_parked" - else - git add "$file" - fi + destpath="./doc/devel/rfc_parked" ;; *Dropped*) - if [[ "$path" != "./doc/devel/rfc_dropped" ]]; then - git mv "$file" "./doc/devel/rfc_dropped" - else - git add "$file" - fi - ;; - *) - git add "$file" + destpath="./doc/devel/rfc_dropped" ;; esac + + if [[ "$path" != "$destpath" ]]; then + git mv "$file" "$destpath" + fi + git add "$destpath/$basename" } From 69786d2b2ec8244dd1687d3e5ed911141cf751f1 Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Thu, 14 Apr 2011 03:47:16 +0200 Subject: [PATCH 017/496] Changes/Updates on the RFC documents as decided on the Developer meeting --- .../{rfc_pending => rfc}/LumieraForwardIterator.txt | 9 ++++++++- doc/devel/{rfc_pending => rfc}/MarbleMode.txt | 12 +++++++++++- .../{rfc_pending => rfc}/ProcHighLevelModel.txt | 10 +++++++++- .../{rfc_pending => rfc}/ProcPlacementMetaphor.txt | 8 +++++++- doc/devel/{rfc_pending => rfc}/Roadmap-first.txt | 2 +- .../ThreadsSignalsAndImportantManagementTasks.txt | 9 +++++++-- .../{rfc_pending => rfc}/TimelineSequenceOutput.txt | 6 +++++- .../{rfc_pending => rfc}/VersionNumberScheme.txt | 6 +++++- .../ClipCatalogingSystem.txt | 10 +++++++++- .../DelectusShotEvaluator.txt | 11 ++++++++++- .../DesignRenderNodesInterface.txt | 11 +++++++++-- .../FeatureBundle_PluggableModules.txt | 8 +++++++- .../NormalizedDeviceCoordinates.txt | 10 +++++++++- .../{rfc_pending => rfc_parked}/RenderOptimizer.txt | 13 ++++++++++++- doc/devel/{rfc_pending => rfc_parked}/UseCases.txt | 13 ++++++++++++- doc/devel/rfc_pending/StreamTypeSystem.txt | 9 +++++++++ 16 files changed, 130 insertions(+), 17 deletions(-) rename doc/devel/{rfc_pending => rfc}/LumieraForwardIterator.txt (98%) rename doc/devel/{rfc_pending => rfc}/MarbleMode.txt (95%) rename doc/devel/{rfc_pending => rfc}/ProcHighLevelModel.txt (98%) rename doc/devel/{rfc_pending => rfc}/ProcPlacementMetaphor.txt (99%) rename doc/devel/{rfc_pending => rfc}/Roadmap-first.txt (99%) rename doc/devel/{rfc_pending => rfc}/ThreadsSignalsAndImportantManagementTasks.txt (94%) rename doc/devel/{rfc_pending => rfc}/TimelineSequenceOutput.txt (99%) rename doc/devel/{rfc_pending => rfc}/VersionNumberScheme.txt (97%) rename doc/devel/{rfc_pending => rfc_parked}/ClipCatalogingSystem.txt (96%) rename doc/devel/{rfc_pending => rfc_parked}/DelectusShotEvaluator.txt (99%) rename doc/devel/{rfc_pending => rfc_parked}/DesignRenderNodesInterface.txt (95%) rename doc/devel/{rfc_pending => rfc_parked}/FeatureBundle_PluggableModules.txt (98%) rename doc/devel/{rfc_pending => rfc_parked}/NormalizedDeviceCoordinates.txt (95%) rename doc/devel/{rfc_pending => rfc_parked}/RenderOptimizer.txt (92%) rename doc/devel/{rfc_pending => rfc_parked}/UseCases.txt (98%) diff --git a/doc/devel/rfc_pending/LumieraForwardIterator.txt b/doc/devel/rfc/LumieraForwardIterator.txt similarity index 98% rename from doc/devel/rfc_pending/LumieraForwardIterator.txt rename to doc/devel/rfc/LumieraForwardIterator.txt index 5994865bd..e6075db4f 100644 --- a/doc/devel/rfc_pending/LumieraForwardIterator.txt +++ b/doc/devel/rfc/LumieraForwardIterator.txt @@ -3,7 +3,7 @@ Design Process: Lumiera Forward Iterator [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Final_ *Date* _2009-11-01_ *Proposed by* link:Ichthyostega[] ------------------------------------- @@ -135,4 +135,11 @@ Comments -------- +Final +~~~~~ +Accepted on developer meeting + + Do 14 Apr 2011 02:52:30 CEST Christian Thaeter + + Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/MarbleMode.txt b/doc/devel/rfc/MarbleMode.txt similarity index 95% rename from doc/devel/rfc_pending/MarbleMode.txt rename to doc/devel/rfc/MarbleMode.txt index a77afdd85..53d337afc 100644 --- a/doc/devel/rfc_pending/MarbleMode.txt +++ b/doc/devel/rfc/MarbleMode.txt @@ -1,6 +1,6 @@ [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Final_ *Date* _2008-10-10_ *Proposed by* link:Ichthyostega[] ------------------------------------- @@ -133,5 +133,15 @@ should actively support both working styles. Comments -------- +Final +~~~~~ + +Everyone likes this and we want to have this. But this is rather a concept +which needs a lot more discussion for the implementation. Further details may +follow where these thingsare worked out. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + + Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/ProcHighLevelModel.txt b/doc/devel/rfc/ProcHighLevelModel.txt similarity index 98% rename from doc/devel/rfc_pending/ProcHighLevelModel.txt rename to doc/devel/rfc/ProcHighLevelModel.txt index 408c020a0..958ef95b3 100644 --- a/doc/devel/rfc_pending/ProcHighLevelModel.txt +++ b/doc/devel/rfc/ProcHighLevelModel.txt @@ -1,6 +1,6 @@ [grid="all"] `------------`----------------------- -*State* _Draft_ +*State* _Final_ *Date* _2008-08-16_ *Proposed by* link:Ichthyostega[] ------------------------------------- @@ -240,4 +240,12 @@ Comments -------- +Final +~~~~~ +Description of the Lumiera high level model as is. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + + + Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/ProcPlacementMetaphor.txt b/doc/devel/rfc/ProcPlacementMetaphor.txt similarity index 99% rename from doc/devel/rfc_pending/ProcPlacementMetaphor.txt rename to doc/devel/rfc/ProcPlacementMetaphor.txt index 7617b97e8..562a95dfe 100644 --- a/doc/devel/rfc_pending/ProcPlacementMetaphor.txt +++ b/doc/devel/rfc/ProcPlacementMetaphor.txt @@ -1,6 +1,6 @@ [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Final_ *Date* _2008-03-06_ *Proposed by* link:Ichthyostega[] ------------------------------------- @@ -228,4 +228,10 @@ benchmark in more ways than one, for some time. . --link:Tree[][[DateTime(2008-08-23T12:54:00NZ)]]. +Final +~~~~~ + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + + + Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/Roadmap-first.txt b/doc/devel/rfc/Roadmap-first.txt similarity index 99% rename from doc/devel/rfc_pending/Roadmap-first.txt rename to doc/devel/rfc/Roadmap-first.txt index 57aea6d97..7f095ee58 100644 --- a/doc/devel/rfc_pending/Roadmap-first.txt +++ b/doc/devel/rfc/Roadmap-first.txt @@ -1,6 +1,6 @@ [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Final_ *Date* _2009-01-30_ *Proposed by* link:Ichthyostega[] ------------------------------------- diff --git a/doc/devel/rfc_pending/ThreadsSignalsAndImportantManagementTasks.txt b/doc/devel/rfc/ThreadsSignalsAndImportantManagementTasks.txt similarity index 94% rename from doc/devel/rfc_pending/ThreadsSignalsAndImportantManagementTasks.txt rename to doc/devel/rfc/ThreadsSignalsAndImportantManagementTasks.txt index 69ca53595..a6f32e9e1 100644 --- a/doc/devel/rfc_pending/ThreadsSignalsAndImportantManagementTasks.txt +++ b/doc/devel/rfc/ThreadsSignalsAndImportantManagementTasks.txt @@ -5,7 +5,7 @@ Threads, Signals and important management tasks [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Final_ *Date* _Sat Jul 24 21:59:02 2010_ *Proposed by* Christian Thaeter <ct@pipapo.org> ------------------------------------- @@ -29,7 +29,7 @@ having one dedicated signal handling thread. Moreover it makes sense that the initial thread does this signal handling. For Lumiera I propose to follow this practice and extend it a little by -dedicating the initial thread to some management tasks. These are: +dedicate a thread to some management tasks. These are: * signal handling, see below. * resource management (resource-collector), waiting on a condition variable or message queue to execute actions. @@ -155,5 +155,10 @@ Comments -------- //comments: append below +.State -> Final +ichthyo wants this to be a dedicated thread (own subsystem) instead running in +the initial thread. Fixed this in the proposal above, this makes this accepted. + Do 14 Apr 2011 03:40:41 CEST Christian Thaeter <ct@pipapo.org> + //endof_comments: diff --git a/doc/devel/rfc_pending/TimelineSequenceOutput.txt b/doc/devel/rfc/TimelineSequenceOutput.txt similarity index 99% rename from doc/devel/rfc_pending/TimelineSequenceOutput.txt rename to doc/devel/rfc/TimelineSequenceOutput.txt index d94071cb8..cf704b28a 100644 --- a/doc/devel/rfc_pending/TimelineSequenceOutput.txt +++ b/doc/devel/rfc/TimelineSequenceOutput.txt @@ -1,6 +1,6 @@ [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Final_ *Date* _2008-11-03_ *Proposed by* link:Ichthyostega[] ------------------------------------- @@ -266,5 +266,9 @@ GUI handling could make use of expanded view features like ; Tree 2008-12-19 22:58:30 +Final +~~~~~ + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/VersionNumberScheme.txt b/doc/devel/rfc/VersionNumberScheme.txt similarity index 97% rename from doc/devel/rfc_pending/VersionNumberScheme.txt rename to doc/devel/rfc/VersionNumberScheme.txt index 1894ab1e0..93843d73b 100644 --- a/doc/devel/rfc_pending/VersionNumberScheme.txt +++ b/doc/devel/rfc/VersionNumberScheme.txt @@ -5,7 +5,7 @@ VersionNumberScheme [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Final_ *Date* _Mi 09 Mär 2011 02:00:41 CET_ *Proposed by* Ichthyostega <prg@ichthyostega.de> ------------------------------------- @@ -152,6 +152,10 @@ Comments -------- //comments: append below +.State -> Final +common practice + Do 14 Apr 2011 03:46:07 CEST Christian Thaeter <ct@pipapo.org> + //endof_comments: diff --git a/doc/devel/rfc_pending/ClipCatalogingSystem.txt b/doc/devel/rfc_parked/ClipCatalogingSystem.txt similarity index 96% rename from doc/devel/rfc_pending/ClipCatalogingSystem.txt rename to doc/devel/rfc_parked/ClipCatalogingSystem.txt index 8d9ca5fa5..8149c23ac 100644 --- a/doc/devel/rfc_pending/ClipCatalogingSystem.txt +++ b/doc/devel/rfc_parked/ClipCatalogingSystem.txt @@ -3,7 +3,7 @@ Design Process : Clip Cataloging System [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Parked_ *Date* _2008-07-26_ *Proposed by* link:JordanN[] ------------------------------------- @@ -117,4 +117,12 @@ without even having to "drive" the development.. --link:Tree[][[DateTime(2008-08-27T20:38:00NZ)]]. '''' + +Parked until someone cares +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Decided on Developer meeting + + Do 14 Apr 2011 02:52:30 CEST Christian Thaeter + + Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/DelectusShotEvaluator.txt b/doc/devel/rfc_parked/DelectusShotEvaluator.txt similarity index 99% rename from doc/devel/rfc_pending/DelectusShotEvaluator.txt rename to doc/devel/rfc_parked/DelectusShotEvaluator.txt index fa8d2df12..0d5b55a88 100644 --- a/doc/devel/rfc_pending/DelectusShotEvaluator.txt +++ b/doc/devel/rfc_parked/DelectusShotEvaluator.txt @@ -1,6 +1,6 @@ [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Parked_ *Date* _2008-09-21_ *Proposed by* link:nasa[] ------------------------------------- @@ -385,4 +385,13 @@ very expensive. I'm a sound guy, so I feel your pain... - link:NicholasSA[] 2009-01-04 + +Parked +~~~~~~ + +Decided on Developer meeting, until someone wants to investigate this further. + + Do 14 Apr 2011 02:52:30 CEST Christian Thaeter + + Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/DesignRenderNodesInterface.txt b/doc/devel/rfc_parked/DesignRenderNodesInterface.txt similarity index 95% rename from doc/devel/rfc_pending/DesignRenderNodesInterface.txt rename to doc/devel/rfc_parked/DesignRenderNodesInterface.txt index 19bae98ff..201c34a72 100644 --- a/doc/devel/rfc_pending/DesignRenderNodesInterface.txt +++ b/doc/devel/rfc_parked/DesignRenderNodesInterface.txt @@ -1,6 +1,6 @@ [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Parked_ *Date* _2008-03-06_ *Proposed by* link:Ichthyostega[] ------------------------------------- @@ -62,7 +62,7 @@ the invocation interface this is intended to be a rather simple "call-style" interface, without much possibilites to influence the way things are happening. You pull a node and will find the results in a provided buffer or the cache, but you can't even -change the frame data type type of the result. Besides the node invocation, +change the frame data type type of the result. Besides the node invocation, functions for collecting statistics will be accessible here too (Probably these functions will be ''implemented'' in a classic-OO fashion by virtual functions, but that's another story) @@ -118,4 +118,11 @@ Comments -------- +Parked +~~~~~~ +We park this until we have time to revisit the details. It is accepted that we +need to design this interfaces. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/FeatureBundle_PluggableModules.txt b/doc/devel/rfc_parked/FeatureBundle_PluggableModules.txt similarity index 98% rename from doc/devel/rfc_pending/FeatureBundle_PluggableModules.txt rename to doc/devel/rfc_parked/FeatureBundle_PluggableModules.txt index 7124b01b1..06b9fe814 100644 --- a/doc/devel/rfc_pending/FeatureBundle_PluggableModules.txt +++ b/doc/devel/rfc_parked/FeatureBundle_PluggableModules.txt @@ -1,6 +1,6 @@ [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Parked_ *Date* _2008-09-03_ *Proposed by* link:Ichthyostega[] ------------------------------------- @@ -186,4 +186,10 @@ link:Plugin/Interface[] system self-describing this will also be used to bootstrap a session on itself (by the serializer which is tightly integrated) -- link:ct[] [[DateTime(2008-09-04T09:28:37Z)]] 2008-09-04 09:28:37 +Parked +~~~~~~ +Needs to ne reviewed some time later. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/NormalizedDeviceCoordinates.txt b/doc/devel/rfc_parked/NormalizedDeviceCoordinates.txt similarity index 95% rename from doc/devel/rfc_pending/NormalizedDeviceCoordinates.txt rename to doc/devel/rfc_parked/NormalizedDeviceCoordinates.txt index e5bbd7cdb..b594cc3b2 100644 --- a/doc/devel/rfc_pending/NormalizedDeviceCoordinates.txt +++ b/doc/devel/rfc_parked/NormalizedDeviceCoordinates.txt @@ -1,6 +1,6 @@ [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Parked_ *Date* _2009-01-14_ *Proposed by* link:ct[] ------------------------------------- @@ -101,4 +101,12 @@ framerate" and a fixed resolution in pixels. But we certainly can do better. -- Ichthyostega 18:09:50 +Parked +~~~~~~ +deferred for later, generally accepted. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + + + Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/RenderOptimizer.txt b/doc/devel/rfc_parked/RenderOptimizer.txt similarity index 92% rename from doc/devel/rfc_pending/RenderOptimizer.txt rename to doc/devel/rfc_parked/RenderOptimizer.txt index 956aa89a4..74604e77e 100644 --- a/doc/devel/rfc_pending/RenderOptimizer.txt +++ b/doc/devel/rfc_parked/RenderOptimizer.txt @@ -1,6 +1,6 @@ [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Parked_ *Date* _2007-06-07_ *Proposed by* link:ct[] ------------------------------------- @@ -86,4 +86,15 @@ The good news is, that: the matrix can be transformed from e.g. RGB to YUV, so these filters can always work in both colorspaces directly + +Parked +~~~~~~ +Generally this is accepted but needs some more polishing when we go over it. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + + + + + Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/UseCases.txt b/doc/devel/rfc_parked/UseCases.txt similarity index 98% rename from doc/devel/rfc_pending/UseCases.txt rename to doc/devel/rfc_parked/UseCases.txt index 423263e87..1ac83c325 100644 --- a/doc/devel/rfc_pending/UseCases.txt +++ b/doc/devel/rfc_parked/UseCases.txt @@ -1,6 +1,6 @@ [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Parked_ *Date* _2008-10-31_ *Proposed by* link:Ichthyostega[] ------------------------------------- @@ -291,4 +291,15 @@ credit roll for each episode. Add in stopmotion, and 3D model animations with vocal commentaries. Gather together separate items from "outworkers". Tree (@)SIG(@) + +Parked +~~~~~~ +We have to revisit this, possibly someone (or a group) who wants to work on +the workflow. For now its parked until revisited. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + + + + Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/StreamTypeSystem.txt b/doc/devel/rfc_pending/StreamTypeSystem.txt index 2b6adbc06..ea07777c2 100644 --- a/doc/devel/rfc_pending/StreamTypeSystem.txt +++ b/doc/devel/rfc_pending/StreamTypeSystem.txt @@ -231,4 +231,13 @@ frameinfo* get_frame(unsigned num)+ where +struct frameinfo+ (not yet defined) is something like +{ void* data; size_t size; struct metadata* meta; ...}+ -- link:ct[] 2008-10-06 +Needs Work +~~~~~~~~~~ +There are a lot details to be worked out for an actual implementation but we +agreed that we want this concept as proposed here. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + + + Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] From be6a60dd25a2d143a17ff60a1d6546f5f8e52d05 Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Thu, 14 Apr 2011 04:16:32 +0200 Subject: [PATCH 018/496] add menu entry for new rfc_parked dir --- doc/devel/rfc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/devel/rfc.txt b/doc/devel/rfc.txt index 06873b7db..7f7a542a4 100644 --- a/doc/devel/rfc.txt +++ b/doc/devel/rfc.txt @@ -2,6 +2,7 @@ Design Process ============== //Menu: include rfc +//Menu: include rfc_parked //Menu: include rfc_pending //Menu: include rfc_dropped From 8b6ce261101a641e48c33c377b40495c2ca9fae8 Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Thu, 14 Apr 2011 04:25:43 +0200 Subject: [PATCH 019/496] FIX: next attempt to tame the menugen script (I promise I RTFM next time) --- doc/devel/rfc_dropped/index.txt | 7 ------- doc/devel/rfc_parked/index.txt | 13 +++++++++++++ doc/devel/rfc_pending/index.txt | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 doc/devel/rfc_parked/index.txt diff --git a/doc/devel/rfc_dropped/index.txt b/doc/devel/rfc_dropped/index.txt index fd3958f2d..8ce4ddb6d 100644 --- a/doc/devel/rfc_dropped/index.txt +++ b/doc/devel/rfc_dropped/index.txt @@ -9,10 +9,3 @@ Design Proposals not accepted The RfC entries listed here where discussed, but then concluded not to be applicable for some reason. Either the discussion turned up a completely different solution, or maybe they got just superseded by changing circumstances. - -Besides that, sometimes we find an entry impossible to care for and discuss in detail -currently, because we know to be occupied with other stuff. In that case, we put that -entry ``on hold'' (state _*Parked*_). Currently (as of 2/2011) these entries show -up in this category here too. - - diff --git a/doc/devel/rfc_parked/index.txt b/doc/devel/rfc_parked/index.txt new file mode 100644 index 000000000..0775f1f47 --- /dev/null +++ b/doc/devel/rfc_parked/index.txt @@ -0,0 +1,13 @@ +Parked Design Proposals +======================= + +//Menu: label parked +//Menu: sort children + +-> read link:../rfc.html[more about Lumiera RfC] and the Design Process + +The RfC entries listed here where proposed but not finally decided or agreed on, +moreover parked RfC's are put here until someone has time to review and improve +them further towards an final decision. + + diff --git a/doc/devel/rfc_pending/index.txt b/doc/devel/rfc_pending/index.txt index c553617c7..011b3a167 100644 --- a/doc/devel/rfc_pending/index.txt +++ b/doc/devel/rfc_pending/index.txt @@ -15,7 +15,7 @@ The RfC entries listed here where proposed but not finally decided or agreed on but sometimes they are kept _on hold_ -- maybe there is something controversial or some details need to be investigated further. * sometimes we just decide to postpone the discussion -> see - link:../rfc_dropped/index.html[dropped and parked entries] + link:../rfc_parked/index.html[parked entries] From dc8dd983082eab527995da792073e22d927988a7 Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Thu, 14 Apr 2011 19:14:03 +0200 Subject: [PATCH 020/496] FIX: asciidoc formating glitches, rfc.sh documentation --- admin/rfc.sh | 14 ++++++++------ doc/technical/infra/rfcsh.txt | 14 ++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/admin/rfc.sh b/admin/rfc.sh index ae0b8255e..c7d4dcb79 100755 --- a/admin/rfc.sh +++ b/admin/rfc.sh @@ -53,12 +53,14 @@ Smart matching RFC names don't need to be given exactly, they use a globbing pattern. This is: - - case insensitive - - whitespaces are ignored - - `*` stands for any number of parameters - - `?` is any single character - - when starting with `/` they are matched against the begin of the name - - some regex operators work too + + * case insensitive + * whitespaces are ignored + * `*` stands for any number of parameters + * `?` is any single character + * when starting with `/` they are matched against the begin of the name + * some regex operators work too + `find` and `show` can operate on many matches so the given rfc name doesn't need to be unique. The other commands will complain when the RFC name given doesn't resolve to one unique RFC. diff --git a/doc/technical/infra/rfcsh.txt b/doc/technical/infra/rfcsh.txt index 40732fed2..07790f7c2 100644 --- a/doc/technical/infra/rfcsh.txt +++ b/doc/technical/infra/rfcsh.txt @@ -46,12 +46,14 @@ Smart matching RFC names don't need to be given exactly, they use a globbing pattern. This is: - - case insensitive - - whitespaces are ignored - - `*` stands for any number of parameters - - `?` is any single character - - when starting with `/` they are matched against the begin of the name - - some regex operators work too + + * case insensitive + * whitespaces are ignored + * `*` stands for any number of parameters + * `?` is any single character + * when starting with `/` they are matched against the begin of the name + * some regex operators work too + `find` and `show` can operate on many matches so the given rfc name doesn't need to be unique. The other commands will complain when the RFC name given doesn't resolve to one unique RFC. From 207fa7c13c41d20ea43de6596ea4f7e455f638b4 Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Thu, 14 Apr 2011 19:22:04 +0200 Subject: [PATCH 021/496] Add link to the rfc.sh documentation --- doc/devel/rfc.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/devel/rfc.txt b/doc/devel/rfc.txt index 7f7a542a4..9879727e2 100644 --- a/doc/devel/rfc.txt +++ b/doc/devel/rfc.txt @@ -10,7 +10,7 @@ Design Process How it Works ------------ -Design Process entries (RfC) can be created by anyone. +Design Process entries (Request for Comment) can be created by anyone. Each entry goes through several stages until it's accepted (or rejected). All our RfC entries are filed here, either in the link:rfc/index.html[RfC accepted] section, or as link:rfc_pending/index.html[pending RfC] or as link:rfc_dropped/index.html[RfC dropped]. @@ -38,6 +38,6 @@ Handling of RfC entries in practice .Adding a new Proposal: . Check if there is no similar proposal below! If yes, contact it's author and contribute to that one. . Think of a good/descriptive _Name-ID_ for the Proposal. It will be used to create a RfC sub-page, so no embedded spaces please. -. [red,yellow]#!TODO!# _please describe how to use the +rfc.sh+ here_. +. Use the './admin/rfc.sh' helper script to maintain the RFC's. Check out its link:../technical/infra/rfcsh.html[documentation]. From eb087b98b1d1f0ecc8f02f1e3cfc3e33d8e7c74a Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 16 Apr 2011 02:47:42 +0200 Subject: [PATCH 022/496] re-read some of the RfC, fix small markup issues --- ...741.png => ProjectTimelineSequenceUML.png} | Bin doc/devel/rfc/CodingStyle.txt | 25 ++---- doc/devel/rfc/LumieraForwardIterator.txt | 20 ++++- doc/devel/rfc/TimelineSequenceOutput.txt | 77 ++++++++-------- doc/devel/rfc/VersionNumberScheme.txt | 3 +- doc/devel/rfc_parked/UseCases.txt | 83 ++++++++++-------- doc/devel/rfc_parked/index.txt | 8 +- 7 files changed, 121 insertions(+), 95 deletions(-) rename doc/devel/images/{fig132741.png => ProjectTimelineSequenceUML.png} (100%) diff --git a/doc/devel/images/fig132741.png b/doc/devel/images/ProjectTimelineSequenceUML.png similarity index 100% rename from doc/devel/images/fig132741.png rename to doc/devel/images/ProjectTimelineSequenceUML.png diff --git a/doc/devel/rfc/CodingStyle.txt b/doc/devel/rfc/CodingStyle.txt index a353bc6c0..64afac562 100644 --- a/doc/devel/rfc/CodingStyle.txt +++ b/doc/devel/rfc/CodingStyle.txt @@ -52,30 +52,14 @@ Tasks * footers (or headers) to configure common editors to use this style by default -Pros -^^^^ - - -Cons -^^^^ - - -Alternatives -^^^^^^^^^^^^ - - - - -Rationale -~~~~~~~~~ - Conclusion ---------- we agreed on GNU style - -- link:ct[] [[DateTime(2007-07-03T04:04:22Z)]] + +CT:: '2007-07-03 04:04' @@ -88,6 +72,8 @@ think all other reasons will lead us to nowhere! Although I'm used to a BSD/KNF-like coding style I will try the GNU one. After all, the wikipedia page mentions no disadvantages of that style :) +MichaelPloujnikov:: '2007-06-27 17:17' + I just proposed K&R because it is widely accepted. Personally, I was never very fond of K&R style, I always prefered putting opening braces to the left. I @@ -96,5 +82,8 @@ ECLIPSE comes with presets for all this styles :-P ). Anyhow, I can adapt to most any style. The only thing I really dislike is using tabs (with the exeption of database DDLs and CSound files, where tab are actually helpful) :) +Ichthyo:: '2007-06-27 20:55' + + '''' Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/LumieraForwardIterator.txt b/doc/devel/rfc/LumieraForwardIterator.txt index e6075db4f..e93ca8861 100644 --- a/doc/devel/rfc/LumieraForwardIterator.txt +++ b/doc/devel/rfc/LumieraForwardIterator.txt @@ -94,7 +94,8 @@ relies on that interface for discovering session contents. Besides that, we need more implementation experience. Some existing iterators or collection-style interfaces should be retro-fitted. -See http://issues.lumiera.org/ticket/349[Ticket #349]. + Moreover, we need to +See http://issues.lumiera.org/ticket/349[Ticket #349]. + +Moreover, we need to gain experience about mapping this concept down into a flat C-style API. @@ -133,6 +134,23 @@ compromising the clean APIs. Comments -------- +//comments: append below + +Now in use since more then a year, without turning up any serious problems. +The only _minor concern_ I can see is that this concept, as such, doesn't solve +the problem with exposing implementation details of the underlying container on the API. +Similar to STL Iterators, the actual implementation representation is only disguised +behind a 'typedef'. But, generally speaking, this is an inevitable consequence of the +``zero overhead'' abstraction. For the cases when an indirection (via VTable) is feasible, +I've created the 'IterSource' template, which sticks to this Lumiera Forward Iterator +concept, but provides an opaque frontend, allowing to decouple completely from the +actual implementation. Besides that, over time I've written several standard adapters +for the most common STL containers, plus Map, key and value extractors. + +Ichthyostega:: 'Sa 16 Apr 2011 00:20:13 CEST' + + +//endof_comments: Final diff --git a/doc/devel/rfc/TimelineSequenceOutput.txt b/doc/devel/rfc/TimelineSequenceOutput.txt index cf704b28a..b1f043a1d 100644 --- a/doc/devel/rfc/TimelineSequenceOutput.txt +++ b/doc/devel/rfc/TimelineSequenceOutput.txt @@ -6,51 +6,58 @@ ------------------------------------- +.Summary +**************************************************************************** +The Session or Project contains multiple top-level *Timeline* elements. +These provide an (output)configuration and global busses, while the actual +content _and the tree of tracks_ is contained in *Sequences*. +These can also be used as *meta clips* and thus nested arbitrarily. +**************************************************************************** + Relation of Project, Timeline(s), Sequence(s) and Output generation ------------------------------------------------------------------- In the course of our discussions it meanwhile became clear, that Lumiera will -show multiple ''timeline-like'' views within one project. Similarly it's clear -now that we will support [wiki:self:../EDLsAreMetaClips nested Sequences as -meta-clips]. The purpose of this entry is to try to settle on some definitions +show multiple _timeline-like_ views within one project. Similarly it's clear +now that we will support link:EDLsAreMetaClips.html[nested Sequences as meta-clips]. +The purpose of this entry is to try to settle on some definitions and clarify the relationships between these concepts. - Definitions ~~~~~~~~~~~ - Project:: the top-level context in which all edit work is done over an +Project:: the top-level context in which all edit work is done over an extended period of time. The Project can be saved and re-opened. It is comprised of the collection of all things the user is working on, it contains all informations, assets, state and objects to be edited. - Session:: the current in-memory representation of the Project when opened +Session:: the current in-memory representation of the Project when opened within an instance of Lumiera. This is an implementation-internal term. For the GUI and the users POV we should always prefer the term "Project" for the general concept. - Timeline:: the top level element within the Project. It is visible within a - ''timeline view'' in the GUI and represents the effective (resulting) +Timeline:: the top level element within the Project. It is visible within a + _timeline view_ in the GUI and represents the effective (resulting) arrangement of media objects, resolved to a finite time axis, to be rendered for output or viewed in a Monitor (viewer window). Timeline(s) are top-level and may not be further combined. A timeline is comprised of: * a time axis in abolute time (WIP: not clear if this is an entity or just a conceptual definition) - * a ''!PlayController'' - * a list of global ''Pipes'' representing the possible outputs (master + * a _PlayController_ + * a list of global _Pipes_ representing the possible outputs (master busses) - * exactly one top-level ''Sequence,'' which in turn may contain further + * exactly one top-level _Sequence_, which in turn may contain further nested Sequences - Timeline View:: a view in the GUI featuring a given timeline. There might be +Timeline View:: a view in the GUI featuring a given timeline. There might be multiple views of the same timeline, all sharing the same !PlayController. A - proposed extension is the ability to ''focus'' a timeline view to a + proposed extension is the ability to _focus_ a timeline view to a sub-Sequence contained within the top-level sequence of the underlying Timeline. (Intended for editing meta-clips) - Sequence:: A collection of ''MObjects'' placed onto a tree of tracks. (this - entity was former named ''EDL'' -- an alternative name would be - ''Arrangement'' ). By means of this placement, the objects could be anchored +Sequence:: A collection of _MObjects_ placed onto a tree of tracks. (this + entity was former named _EDL_ -- an alternative name would be + _Arrangement_ ). By means of this placement, the objects could be anchored relative to each other, relative to external objects, absolute in time. Placement and routing information can be inherited down the track tree, and missing information is filled in by configuration rules. This way, a sequence @@ -60,26 +67,26 @@ Definitions just a single root track and sends directly to the master busses of the timeline. - Pipe:: the conceptual building block of the high-level model. It can be - thought of as simple linear processing chain. A stream can be ''sent to'' a - pipe, in which case it will be mixed in at the input, and you can ''plug'' the +Pipe:: the conceptual building block of the high-level model. It can be + thought of as simple linear processing chain. A stream can be _sent to_ a + pipe, in which case it will be mixed in at the input, and you can _plug_ the output of a pipe to another destination. Further, effects or processors can be attached to the pipe. Besides the global pipes (busses) in each Timeline, each clip automatically creates N pipes (one for each distinct content stream, i.e. normally N=2, namely video and audio) - link:PlayController[]:: coordinating playback, cueing and rewinding of a - ''!PlayheadCursor'' (or multiple in case there are multiple views and or +PlayController:: coordinating playback, cueing and rewinding of a + _PlayheadCursor_ (or multiple in case there are multiple views and or monitors), and at the same time directing a render process to deliver the media data needed for playback. Actually, the implementation of the - !PlayController(s) is assumed to live in the backend. + PlayController(s) is assumed to live in the backend. - link:RenderTask[]:: basically a !PlayController, but collecting output +RenderTask:: basically a !PlayController, but collecting output directly, without moving a !PlayheadCursor (maybe a progress indicator) and not operating in a timed fashion, but freewheeling or in background mode - Monitor:: a viewer window to be attached to a timeline. When attached, a - monitor reflects the state of the timeline's !PlayController, and it attaches +Monitor/Viewer:: a viewer window to be attached to a timeline. When attached, a + monitor reflects the state of the timeline's PlayController, and it attaches to the timeline's global pipes by stream-type match, showing video as monitor image and sending audio to the system audio port (Alsa or Jack). Possible extensions are for a monitor to be able to attach to probe points within the @@ -91,7 +98,7 @@ Definitions Relations ~~~~~~~~~ -image:images/fig132741.png[Relation of Project Timeline Sequence Output] +image:{imgd}/ProjectTimelineSequenceUML.png[UML: Relation of Project, Timeline, Sequence and Output] @@ -99,7 +106,7 @@ image:images/fig132741.png[Relation of Project Timeline Sequence Output] * this UML shows the relation of concepts, not so much their implementation * within one Project, we may have multiple independent timelines and at the same time we may have multiple views of the same timeline. -* all playhead displays within different views linked to the ''same'' +* all playhead displays within different views linked to the _same_ underlying timeline are effectively linked together, as are all GUI widgets representing the same !PlayController owned by a single timeline. * I am proposing to do it this way per default, because it seems to be a best @@ -108,7 +115,7 @@ image:images/fig132741.png[Relation of Project Timeline Sequence Output] * the timeline view is modeled to be a sub-concept of "timeline" and thus can stand-in. Thus, to start with, for the GUI it doesn't make any difference if it talks to a timeline view or a timeline. -* each timeline ''refers'' to a (top-level) sequence. I.e. the sequences +* each timeline _refers_ to a (top-level) sequence. I.e. the sequences themselves are owned by the project, and theoretically it's possible to refer to the same sequence from multiple timelines directly and indirectly. * besides, it's also possible to create multiple independent timelines -— in @@ -116,11 +123,11 @@ image:images/fig132741.png[Relation of Project Timeline Sequence Output] configuration gives the ability to play the same arrangement in parallel with multiple independent play controllers (and thus independent playhead positions) -* to complement this possibilities, I'd propose to give the ''timeline view'' +* to complement this possibilities, I'd propose to give the _timeline view_ the possibility to be focussed (re-linked) to a sub-sequence. This way, it would stay connected to the main play control, but at the same time show a - sub-sequence ''in the way it will be treated as embedded within the top-level - sequence.'' This would be the default operation mode when a meta-clip is + sub-sequence _in the way it will be treated as embedded within the top-level + sequence._ This would be the default operation mode when a meta-clip is opened (and showed in a separate tab with such a linked timeline view). The reason for this proposed handling is again to give the user the least surprising behaviour. Because, when -— on the contrary -— the @@ -129,8 +136,8 @@ image:images/fig132741.png[Relation of Project Timeline Sequence Output] reserved for advanced use, e.g. when multiple editors cooperate on a single project and a sequence has to be prepared in isolation prior to being integrated in the global sequence (featuring the whole movie). -* one rather unconventional feature to be noted is that the ''tracks'' are - within the ''sequences'' and not on the level of the global busses as in most +* one rather unconventional feature to be noted is that the _tracks_ are + within the _sequences_ and not on the level of the global busses as in most other video and audio editors. The rationale is that this allows for fully exploiting the tree-structure, even when working with large and compound projects, it allows for sequences being local clusters of objects including @@ -141,10 +148,10 @@ image:images/fig132741.png[Relation of Project Timeline Sequence Output] Tasks ^^^^^ - * Interfaces on the link:GUI/Proc[] level need to be fully specified. + * Interfaces on the GUI and Proc level need to be fully specified. Especially, "Timeline" is now promoted to be a new top-level entity within the Session - * communication between the !PlayController(s) and the GUI need to be worked + * communication between the PlayController(s) and the GUI need to be worked out * the stream type system, which is needed to make this default connection scheme work, currently is just planned and drafted. Doing a exemplaric diff --git a/doc/devel/rfc/VersionNumberScheme.txt b/doc/devel/rfc/VersionNumberScheme.txt index 93843d73b..450ff27b6 100644 --- a/doc/devel/rfc/VersionNumberScheme.txt +++ b/doc/devel/rfc/VersionNumberScheme.txt @@ -153,7 +153,8 @@ Comments //comments: append below .State -> Final -common practice +considered common practice + Do 14 Apr 2011 03:46:07 CEST Christian Thaeter <ct@pipapo.org> diff --git a/doc/devel/rfc_parked/UseCases.txt b/doc/devel/rfc_parked/UseCases.txt index 1ac83c325..7f1b96d5c 100644 --- a/doc/devel/rfc_parked/UseCases.txt +++ b/doc/devel/rfc_parked/UseCases.txt @@ -13,16 +13,16 @@ The only way to defeat "featuritis" is to build upon a coherent design -- + which in turn relies upon a more or less explicit understanding what the application should be like, and the way the prospective user is thought to work -with the program. Today, a generally accepted ''method'' for building up such -an understanding is to do a ''use case analysis.'' Such a formal analysis would +with the program. Today, a generally accepted 'method' for building up such +an understanding is to do a *use case analysis*. Such a formal analysis would require to identify all usage scenarios with the involved actors and parts of the system, and then to refine them in detail and break them down into distinct use cases. Here, I'll try a rather informal variant of such an analysis. I'll restrain myself to describing the most important usage situations. -''please participate in the discussion. It well may be that everything detailed - here is self-evident, but I doubt so. At least the grouping and the omissions - reflect sort-of a focus of the project'' +'please participate in the discussion. It well may be that everything detailed + here is self-evident, but I doubt so. At least the grouping and the omissions + kind-of reflect a certain focus of the project' Describing basic Lumiera usage situations @@ -57,7 +57,7 @@ You build up a simple linear cut sequence. Either by of) unwanted parts - playing source media and spilling over (insert, overwrite) some parts into the final assembly - - dragging over pre-organised clips from clip folders to build up the + - dragging over the pre-organised clips from clip folders to build up the assembly. Sound is either used immediately as-is (the soundtrack attached to the media), @@ -71,16 +71,16 @@ Scenario (3) : Augmenting an assembly Without the intention to rework it from scratch, an already existing simple assembly is augmented, beautified and polished, maybe to conform with -professional standards. This includes the "rescue" of a somewhat questionable +professional standards. This includes the ``rescue'' of a somewhat questionable assembly by repairing localized technical problems, but also shortening and re-arranging, and in extreme cases even changing the narrative structure. A distinctive property of this usage scenario is that work happens rather in the -context of ''tasks'' (passes) -- not so much isolated operations: +context of 'tasks' (passes) -- not so much isolated operations: - the task may be to get the rhythm or overall tempo right, and thus you go over the sequence and do trim, roll, shuffle or slide edits. - - you may want to "fold-out" parts of the sound, thus interweaving o-sound and - music + - you may want to ``fold-out'' parts of the sound, thus interweaving o-sound + and music - there may be a sound overdubbing and replacing pass - you may want to walk certain automation curves and adjust levels (sound volume or tone, fade, brightness/contrast/colour) @@ -91,7 +91,7 @@ context of ''tasks'' (passes) -- not so much isolated operations: Scenario (4) : Compositional work ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Here I define ''compositional work'' as a situation where you deal with +Here I define *compositional work* as a situation where you deal with multiple more or less independent sequences going on in parallel, similar to a musical score. Frequently, we encounter compositional parts embedded in a otherwise linear work, and often those parts evolve when Scenario (3) is driven @@ -102,29 +102,29 @@ to the extreme. - a movie with a complex narrative structure may induce compositional work on a very large scale (and existing applications frequently fall short on supporting such) - - compositing often leads to compositional work. Special FX, masked objects + - _compositing_ often leads to compositional work. Special FX, masked objects being arranged, artificial elements to be integrated. - - similarly any collage like or heavily layered arrangements lead themselves + - similarly any collage-like or heavily layered arrangements lead themselves to requiring compositional work. The common distinctive property of all those situations is: objects are embedded into a primary context and have to obey the rules of this context, and at the same time have a close correlation to other objects which are embedded -in a completely different ("orthogonal") context. (To give a catchy example: +in a completely different (``orthogonal'') context. (To give a catchy example: assume, a CG monster has to be integrated. Besides the masked monster object, you have several colouring and blurring layers at completely different levels in the layering order, and at the same time you have correlated sound objects, -which need to be integrated into the general soundscape. And now your primary +which need to be integrated into the general sound-scape. And now your primary job is to get the movement and timings of the monster right in relation to the primary timing grid established by the existing edit) The working style and thus the tool support necessary for compositional work is -completely different to Scenario (3). After an innitial buildup (which often is +completely different to Scenario (3). After an initial build-up (which often is very systematic), the working profile can be characterized by tweaks to various parameters to be done in-sync at widely separated sites within the session, -together with repeated cycles of "do it", "assess the result", "undo all and do -some small detail differently". Typically there is the need for much navigation -(contrast this to Scenario (3) where you work in "passes") +together with repeated cycles of ``do it'', ``assess the result'', ``undo all and +do some small detail differently''. Typically there is the need for much navigation +(contrast this to Scenario (3) where you work in _tasks_ or _passes_) Scenario (5) : Working with Sound @@ -150,7 +150,7 @@ operations: While clearly some of those tasks are always better done within a dedicated application, the ability to carry out this work partially within the main session and even while the basic edit is still in flux -- may open new artistic -possiblilities. +possibilities. Scenario (6) : Large Projects @@ -158,7 +158,7 @@ Scenario (6) : Large Projects At first sight, the operations and the work to be done in large projects is the same as in small ones. But large projects tend to create sort of an additional -"layer" on top of the usage scenarios described thus far, which will "kick in" +``layer'' on top of the usage scenarios described thus far, which will ``kick in'' at various places. - work may be divided upon several editors, working on separate parts @@ -169,8 +169,9 @@ at various places. a certain transition (template), the way fade-outs are done, a certain colour profile. Possibly, this stuff needs to be adjusted all over the project. - - there will be a general (large scale) timing grid and probably there is the - need to navigate to the different parts of the whole project. + - there will be a general (large scale) timing grid with distinct ``check points'' + and probably there is the need to navigate to the different parts of the + whole project. - there may be the necessity to build several versions of the same project in parallel (e.g. a short version and a extended director's cut) - you may have to care for such nasty and tedious things as keeping sub-titles @@ -187,7 +188,7 @@ Several people work on a project. - A longer sequence might be split up into parts, each one edited by another person. The parts will be collected and assembled by the chief editor. Edits to the parts will still be possible, but a system of permissions allows to - lock down access to the material. + lock down access to parts of the edit, so to prevent unexpected interferences. - Arrangements based on the same resources can be branched, tagged and merged. - Edits are logged with usernames - Markers can be shown/hidden on a per creator base. @@ -201,12 +202,12 @@ Several people work on a project. Scenario (8) : Script driven ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The application is started ''headless'' (without GUI) and controlled via an +The application is started ``headless'' (without GUI) and controlled via an API. Either an existing session is loaded, or a new session is created and populated. Then, some operations have to be done in a systematic manner, requiring a way to address parts of the session both unambiguously and in a way easy to access and control from a programming environment (you can't just -''see'' the right clip, it needs to be tagged). Finally, there might be an +``see'' the right clip, it needs to be tagged). Finally, there might be an export or render step. A variation of this scenario is the automatic extraction of some informations from an existing project. @@ -218,26 +219,26 @@ Discussion * describing such scenarios, even if hypothetical, create an anchor or point of referral for feature/GUI design work to be done in detail * relating features to working situations helps to see what is really important - and what is rather of technical merrit + and what is rather of technical merit * compiling and discussing this list helps shaping the character of the application as a whole * the above compilation relates individual features to a general production process. -* the goal of this compilation is to be ''fairly complete'' +* the goal of this compilation is to be _fairly complete_ .Cons * any of those descriptions is artificial * sometimes it is better to develop an application technology driven, especially when it is technologically challenging to get it to work properly. -* having such a large-scale vision may frighten people away which otherwise +* having such a large-scale vision may freak away people which otherwise might jump in and implement some crazy but valuable new feature -* the listed usage scenarios intend to be ''fairly complete,'' which can be a +* the listed usage scenarios intend to be _fairly complete_, which can be a limitation or even self-deception. Better have an open ended list. * the above compilation seems quite conventional and explicitly leaves out some scenarios - networked, distributed scenarios, compound applications - - television, life video, !VeeJay-ing + - television, life video, VeeJay-ing - cartoons, animations, game design @@ -247,11 +248,11 @@ Discussion * just start out with one scenario directly at hand (e.g. the simple assembly) and not worrying about the rest * rather then defining those scenarios (which are necessarily hypothetical), - rather stick to the operation level. E.g. a use case would be rather "trim a - clip" + rather stick to the operation level. E.g. a use case would be rather + on the level of ``triming a clip'' * doing a complete state-of-the art UML use case analysis. * after having created the foundation, rather stick to an XP approach, i.e. - implement, integrate and release small "usage stories" + implement, integrate and release small ``usage stories'' @@ -281,15 +282,19 @@ circumstances of production change quite dramatically. Comments -------- +//comments: append below -Template e.g. for regular TV series -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - +.Template e.g. for regular TV series Constraints to fit all contents within fixed timeline, cover topic, select collage of iconic scenes from archived and collected footage. Update intro and credit roll for each episode. Add in stopmotion, and 3D model animations with -vocal commentaries. Gather together separate items from "outworkers". Tree -(@)SIG(@) +vocal commentaries. Gather together separate items from "outworkers". + +Tree:: '2008-12-27 08:36:36' + + +//endof_comments: + Parked diff --git a/doc/devel/rfc_parked/index.txt b/doc/devel/rfc_parked/index.txt index 0775f1f47..e975a1af2 100644 --- a/doc/devel/rfc_parked/index.txt +++ b/doc/devel/rfc_parked/index.txt @@ -6,8 +6,14 @@ Parked Design Proposals -> read link:../rfc.html[more about Lumiera RfC] and the Design Process -The RfC entries listed here where proposed but not finally decided or agreed on, +The RfC entries listed here where proposed but not finally decided or agreed on; moreover parked RfC's are put here until someone has time to review and improve them further towards an final decision. +Labelling a RfC as _parked_ doesn't entail any pre-judgment -- it just might be +beyond our current working focus, or we're lacking the resources to care for this +topic right now. + + + From a3dd83c0f0425a641b71be271145a38d17dd5924 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 16 Apr 2011 02:12:03 +0200 Subject: [PATCH 023/496] move all existing IRC meeting summaries from the website into the core tree Note: the documentation subtree is included in the website, so the meeting summaries are still visible online --- doc/devel/meeting_summary/2008-02-01.txt | 82 +++++++ doc/devel/meeting_summary/2008-03-06.txt | 189 +++++++++++++++ doc/devel/meeting_summary/2008-04-03.txt | 290 +++++++++++++++++++++++ doc/devel/meeting_summary/2008-05-08.txt | 244 +++++++++++++++++++ doc/devel/meeting_summary/2008-06-06.txt | 81 +++++++ doc/devel/meeting_summary/2008-07-06.txt | 94 ++++++++ doc/devel/meeting_summary/2008-09-04.txt | 74 ++++++ doc/devel/meeting_summary/2008-10-10.txt | 232 ++++++++++++++++++ doc/devel/meeting_summary/2008-12-10.txt | 72 ++++++ doc/devel/meeting_summary/2009-01-14.txt | 122 ++++++++++ doc/devel/meeting_summary/2009-09-09.txt | 118 +++++++++ doc/devel/meeting_summary/2011-03-09.txt | 208 ++++++++++++++++ doc/devel/meeting_summary/index.txt | 285 ++++++++++++++++++++++ 13 files changed, 2091 insertions(+) create mode 100644 doc/devel/meeting_summary/2008-02-01.txt create mode 100644 doc/devel/meeting_summary/2008-03-06.txt create mode 100644 doc/devel/meeting_summary/2008-04-03.txt create mode 100644 doc/devel/meeting_summary/2008-05-08.txt create mode 100644 doc/devel/meeting_summary/2008-06-06.txt create mode 100644 doc/devel/meeting_summary/2008-07-06.txt create mode 100644 doc/devel/meeting_summary/2008-09-04.txt create mode 100644 doc/devel/meeting_summary/2008-10-10.txt create mode 100644 doc/devel/meeting_summary/2008-12-10.txt create mode 100644 doc/devel/meeting_summary/2009-01-14.txt create mode 100644 doc/devel/meeting_summary/2009-09-09.txt create mode 100644 doc/devel/meeting_summary/2011-03-09.txt create mode 100644 doc/devel/meeting_summary/index.txt diff --git a/doc/devel/meeting_summary/2008-02-01.txt b/doc/devel/meeting_summary/2008-02-01.txt new file mode 100644 index 000000000..46e3ef793 --- /dev/null +++ b/doc/devel/meeting_summary/2008-02-01.txt @@ -0,0 +1,82 @@ +Initial Lumiera Developers Meeting +================================== +:Date: 2008-02-01 + +//Menu: label 2008-02-01 the first Lumiera Developers Meeting + +__Participants__ + + * hermanr + * cehteh + * ichthyo + * Dasypus + * gmerlin + * SimAV + * pippin + * Plough + * Plouj + * Velmont + + +Discuss the open points in [Lumiera/DesignProcess] --- do we need this formalism? +--------------------------------------------------------------------------------- +At start of the project last summer, Cehteh made a http://www.pipapo.org/pipawiki/Lumiera/DesignProcess[design process proposal]. We will keeping this up, not for every implementation detail, but for major plans, wishes and design decisions. One point in the agenda of future meetings will be to work through proposals in the queue + + * proposals in the "idea" state are not complete, can be just brainstorming or need further discussion. Comments please to the proposal page. + * proposals in the "draft" state are ready for conclusive discussion and will be treated in one of the next meetings + * "final" proposals are either "accepted" or "dropped". We don't differentiate the latter, but should write a short note why it was dropped. + * if there is need for more or finer grained categories, we'll extend the page and the template as needed or provide different views + + +The development model +--------------------- +We employ a distributed model based on GIT. We want this repository to be as complete as possible, including documentation in embedded TiddlyWikis and Bug reports. Each dev has its own GIT repo, devs are pulling from each other, they are free to cherry pick and try to make the combined version work. Point is that everyone can clone the git, negotiate with the others what s(he) wants to do, and hack on. Every dev signs off his branch with an standardized signature. For small changes we provide a "Mob GIT", i.e. anonymously pushable git (which is untrusted of course). Cehteh is currently working on a git web frontend which makes the codebase in the mob-repo web-editable like a wiki. +Will we need a stable version or an official branch? not yet — as long as the team is small it will work more painless without. At some point, when the project is more mature, we will define an official branch. Later on we will have automated builds and regression test runs. As we do test-driven development anyways, it's just a question of someone setting up all the infrastructure, then we'll do it. +Ichthyo proposes a new requirement: All devs should ensure the "master" branch of their respective repositories always passes the compiler and the test suite. Work-In-Progress should be done on branches. Rationale: it is sufficient to pull from the master branches, and you can be sure the version you pulled worked for the originator. +A note on dependencies: it will be hard to target minimal dependencies for such a project, but we shall try not to bloat it unnecessarily. Sometimes it can be sensible to rather re-invent a feature — esp. when it fits into the core focus of the project — instead of depending on difficult to build and not sufficiently maintained external projects. But we should avoid reinventing things like GUI toolkits. +And, pretty much obvious, we try to stick to modern programming standards. Read: modules have interfaces, interfaces need some (minimal) documentation, and it is not allowed to bypass the interfaces and tangle everything in a wild fashion. +Currently, the project can be separated into three layers, which could evolve into sub-projects: Backend, Proc-Layer, GUI. For each part, the dev most deeply involved and most knowledgeable will take on the sometimes necessary leadership and have the last word in case of quarrels. Currently, Cehteh cares for the Backend and Ichthyo headed for the Proc-Layer. We have postponed any work on the GUI for now and don't participate in GUI toolkit discussions. If there is a dev willing to care for the GUI, collect proposals, care for usability and the users needs and finally code it up, then he will the one to decide what toolkit to use. +We plan to make the discussion about GPL v3 a point on the agenda of the next meeting. + + +Monthly meetings +---------------- + + * make it thursday, not friday + * time for now 21:00 GMT — if some (potential) participants have problems with this time, please speak up (maybe alternating times?) + * write a short status report for each mayor part *prior* to the meeting (saves us time). Maybe add an TODO list there + * go through the open issues for the design process drafts + * publish a protocol of each meeting on the (Cinelerra-CV, LibOpenvideo) Mailinglists, in the link:TiddlyWiki[] and on pipapo.org + * News, Protocols and the agenda of the next meeting can be found at link:MonthlyMeetings[this page here] + * next meeting on first Thursday in March (6.3.2008) + + +Who works on what, what are the short term goals, what tasks are open? +---------------------------------------------------------------------- +*Ichthyo* works on the processing layer. Current goal is to get the core of the builder fleshed out. Next goal is to create a clip object (dummy), add it to the EDL, hand the EDL over to the builder and let the builder make the first (preliminary) render nodes. (note: left many details for later). +Ichthyo started coding his design draft and things seem to work out as intended. Some Keywords: Have a high-level model and a low level model. The former is comprised of the objects in the Session edited by the user, the latter is a network of completely configured render nodes, employing the same pull model as in Cinelerra-2. In between sits the Builder, translating high-level into low-level. This translation is done on demand (not for every frame). +Current state in this part is: basic asset manager is done, asset objects (forming a hierarchy) can be created and will be registered automatically, it is possible to create a clip-"MediaObject" from a media asset (without caring for any details regarding multichannel clips). Some support lib components are written, Session and data holders start up on demand and shutdown cleanly. The test suite is the only interesting executable, and this will remain so for quite some time. Currently writing the first parts of the Builder. +Further plans/ideas: Ichthyo is rather determined to embed a Prolog interpreter (YAP Prolog) to handle all sorts of configuration queries in a rule-driven fashion. Things Ichthyo can't do in the near future: caring for session loading/saving serialisation plus storage backends, caring for a DB based implementation of the asset manager and integration with production support software, target the scheduler which will receive any edit operations initiated from the GUI. + +*Cehteh* is currently working on webgit, which is somewhat related inasmuch it will make small contributions to the mob repository much simpler. Previously he started with some foundation and support facilities. He plans to come back to the Backend implementation in about two weeks. The Backend is intended to handle all media (and even meta-)data as generalized frames. The render nodes network created by the Proc-Layer is completely stateless and all data is served from below. While it will be possible to address and access individual data within a frame (e.g. audio samples), frames are the smallest unit for memory and cache management. No plans to use a tiled memory model or to support frames larger than aprox. 20-40% of the available RAM. +Cehteh's design plan includes a scheduler to organize the access to the raw data, monitor the timings and prefetch data as needed. This scheduler will be configurable via quality preferences ("need realtime", "need perfect quality"). Further, there will be an elaborate caching scheme trying hard to avoid re-rendering any frames already calculated previously. Temporary data will be backed by files and thus swapped out — this swapout and size of temporary data is to be monitored and adjusted according to the current load — and all temporary data is kept as most-recent-used cache discipline. Incoming and outgoing footage shall mostly be handled by using mmaped buffers. The rationale is to avoid unnecessary copy from kernel to user space and wasting memory for an additional in-kernel buffer. Writing via a mmapped buffer is little more tricky; there will be a in-place writing which is used for indices and other precalculated data which needs updates, and the processing layer can query write buffers which are actually a small cache/ring and then comited to the file. Basically, mmapping is a clean solution if you can design for it, and it's portable (posix) +Things to do: object serialisation backend is sometime on Cehteh's schedule, but that's ahead and if someone else helps or takes over it would be OK. Even more true for a DB based backend for the asset manager. + +*Gmerlin* plans to implement grayscale support for GAVL, so the upper layers can store arbitrary data in it. + +About multithreading +~~~~~~~~~~~~~~~~~~~~ +Since the render nodes are stateless they can be driven in multiple threads (but inter frame dependencies need to be resolved/serialized). Mostly the backend manages threads and does that quite conservatively (compared to Cinelerra-2 which massively creates separate threads for small tasks). Any edit operations initiated from GUI go to a scheduler in the middle layer, which enqueues and effectively serializes operations done to the "media objects" in the high-level model. The editing operations themselves are *not threadsafe* by design, they rely on being scheduled correctly. The builder is triggered from this Proc-Layer scheduler and starts in one separate thread, and when done, we swap whole parts of the render nodes network and then the backend can re(start) rendering as needed. + + +The naming discussion +--------------------- +The discussion looks healthy so far... People can add new proposals on the http://www.pipapo.org/pipawiki/Lumiera/Names[pipawiki]. Interesting names are still coming in, so we should just let the name-choosing game go on for a while. And, btw, we _can_ depart from beeing similar to "Cinelerra" ;-) +Let's say, we need a person volonteering to guide/moderate the selection, going over the list and scratching inammprobiate ones. Criteria for good names being: + + * should not be an existing project + * should be "googleable" + * should not be offensive + * should have one of the free top level domains (.net, .org) + * should be compatible with educational institutions (sorry, no pr0nedit :) + * should not obviously collide with trade marks diff --git a/doc/devel/meeting_summary/2008-03-06.txt b/doc/devel/meeting_summary/2008-03-06.txt new file mode 100644 index 000000000..4b28897b8 --- /dev/null +++ b/doc/devel/meeting_summary/2008-03-06.txt @@ -0,0 +1,189 @@ +2008-03-06 Lumiera Developers Meeting +===================================== +:Author: cehteh +:Date: 2008-03-07 + +__Participants__ + + * hermanr + * cehteh + * ichthyo + * Plouj + * SimAV + * akirad + * _MMA_ + * rgareus + * ddennedy + * edrz + * anewcomb + * gmerlin + * oracle2025 + * gisle + + +Boilerplate +----------- + +Organization of this meeting +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * cehteh writes the protocol + * hermanr does chairman + + +Leftovers from last meeting +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +We concluded there are no leftovers + + +Go through Ideas and Drafts in design process +--------------------------------------------- + + +Idea: time_handling +~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/time_handling[] + +Point 1 is superseded by using gavl. + +Points 2 and 3 are still valid. + +Ichthyo asked gmerlin if there are any problems according gavl for the points 2 (position of frames) and 3 (position for keyframes and automation). Gmerlin acknowledges that he doesnt see any problems. + +Oracle2025 brings interlacing on topic "are you aware that automations and keyframes could/should also apply to fields?". We agree that this must be handled "in interlacing, a frame is a pair of 2 subsequent fields". + +Conclusion: + Refactor the proposal according to the discussion and work it out, make it draft then. Discuss about it on the next meeting + + +Idea: Unit tests in Python +~~~~~~~~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/UnitTests_Python[] + +We have a testsuite based on cehtehs 'test.sh' which superseedes this proposal. + +Conclusion: + Drop it. + + +Idea: Todo Lists +~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/TodoLists[] + +This Idea is in a very early state an not yet mature. +Cehteh explains: "I want something which doesn't need much human care and i want one big \'milestones' thing and a small \'mini-task' thing". Ichthyo refines this as "Roadmap" and "Near time task list". We agree that this shall not be too strict. There are some ideas floating, like adding this things to the testsuite or use the wiki. Ichthyo shows how he uses the tiddlywiki's task macro (http://ichthyostega.de/cin3/wiki/renderengine.html#PlanningNodeCreatorTool[]). He likes it but doubts its usefulness when it is used without guessing the hours/days of work. + +Conclusion: + We use the Tiddlywiki task macro thing for now. + + +Draft: CCodingStyleGuide +~~~~~~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/CCodingStyleGuide[] + +Ichthyo tells that the discussion on the wiki page made this clear now. Cehteh explains that he uses this style with success for diffrent projects. We make clear that this is meant for C, in C++ we use namespaces. Overall we agree that code shall be self-documenting. + +Conclusion: + Make it final. + + +Draft: DevelopmentFramework +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/DevelopmentFramework[] + +Cehteh explains that he will transfer this propoal to a tiddlywiki covering compatibility guides and dependencies. We conclude that this wiki must reflect the actual practice (NoBug, Doxygen, test.sh) despite the proposal is not concrete yet. + +A short discussion about build systems came up, we still use autotools and scons in parallel, delaying the decision later. oracle2025 mentioned that scons could be used for development and autotools for release. No decision about that everyone has different opinions. But we agree that it works as is. + +Conclusion: + Cehteh will make the proposed wiki which shall be maintained to reflect the state and this topic will be finalized by that. + + +Draft: Skills Collection +~~~~~~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/SkillsCollection[] + +This might be only useful if there are more developers working on the project. + +Conclusion: + Leave in draft state for now. + + +Draft: Architecture Overview +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/ArchitectureOverview[] + +Ichthyo drawn a diagram showing the planned architecture. Cehteh raises concerns about codecs/plugins/effects in backend. After that there was some discussion about details. Cehteh suggests to place plugins in a extra box, gmerlin suggests that \'plugins' don't fit into the diagram, there should be "filters", "sources",..; Followed by some more discussion, showing that we basically agree and understand the design but the drawing needs some refinements. + +Conclusion: + Good idea, needs some refinements, work in progress. + + + +Call for Design +--------------- + +Ichthyo explains that the wants the overall design a bit more formalized. He put this topic on the agenda to remind that we have to work it out in link:DesignProcess[] and already started to make some design proposals. + +Conclusion: + Things need to be worked out in link:DesignProcess[], take a look and review it. + + +Naming +------ + +Raffa, Velmont, goibhniu and others collected and managed proposed names past weeks which finally ended in a community voting. Results are at http://www.cs.cornell.edu/w8/~andru/cgi-perl/civs/results.pl?id=E_9fad0d1d10c23d38[] +Lumiera won, Lumiera.org is free and no one of in this meeting has objections against this name. Velmont offers to register and pay for the lumiera.org domain which will be hosted on our own server (see topic below). Hermanr raises concerns if it is ok when the name is registered on another site and by another person than the server, cehteh explains that he like this distribution where doable and no one other objects. Ichthyo asks about a short name for Lumiera which will be used for C++ namespaces and such kinds. After a short discussion we agree that we use the full name and no abbreviation. We now have to rename the source and the wiki. Anyone renames the sourcecode where he is responsible for. Cehteh will go over the pipapo.org wiki and rename things. + +Finally we express our great thanks to the people who put the efforts into the name selection, thanks raffa, Velmont, goibhniu & co. + +Conclusion: + Projectname is Lumiera, we have a lot things to rename. + + +Project Server, setup, organization, administration +--------------------------------------------------- +Some people gathered together and rented a server at http://www.hetzner.de[hetzner.de] which will host some free project pages and personal sites. Cehteh is the one who signed for the server and officially respobsible for it. + +The server is split into isolated \'vservers' which are created as needed. Idea is to work cooperatively to get the best out of its resources. + +For our projects (cinelerra.org and lumiera.org) we now need volunteers who help setting it up and administrating things. Velmont offered to help with lumiera.org, hermanr takes care of cinelerra.org, raffa will help with maintaining the webpages. oracle2025 will care for developer chroots, build and test environments. cehteh will make a page on pipapo.org which reflects the current server setup http://www.pipapo.org/pipawiki/RootServerSetup[]. There are still a lot jobs to do! + +Cehteh asked about how to manage emergency root access on the host/root server when he is unreachable. There where several suggestions between one root key for all who pay for the server to shared key schemes. We finally agreed that anyone who payed for the server can send a ssh key to ssh to be registered for emergency access. + +The concrete server setup will be worked out next days. Ideas are one frontend proxy and a shared nameserver. A shared nameserver, A developer vserver shared between all developers. Maybe a shared mail server. + +Hermanr asked about how to handle distribution of large media files. There was some discussion about bittorrent vs http. We concluded to work that out as needed. + +In the forthcoming discussion about cehteh stresses that "it is a public server, if you place confidential information unencrypted on it, its you fault". Ssh keys shall be kept secret by the users but we can't enforce policies on those. + +Ichthyo asks about how to setup lumiera.org (wiki?), oracle2025 suggests webgit, cehteh explains that webgit isn't ready yet but should be useable in a few days (pipapo.org will use that too). Velmont suggest a \'real' website for the time when non-developers want to use it. Git will be set up on lumiera.org the same way as it is currently set up on pipapo.org. + +Hermanr asked Velmont about some help moving the bugs.cinelerra.org over, this might be little challenging since it needs an MTA and other things. + +Ichthyo asks about keeping the Lumiera project pages on pipapo.org until the server is ready, this is ok. + +Conclusions: + We have our own server which now needs volunteers for maintenance. The projects will slowly move there, until ready Lumiera stays on pipapo.org. + + + +GPL3 pros cons, license rationale +--------------------------------- + +Ichthyo and cehteh would like to investigate a license change to GPLv3, neither of us has experience with problems this +might raise. Questions are if we lock us out from potential libraries which are GPLv2 only or lock out possible contributors because of unforeseen patent clauses. The others here thinking that "GPLv2 or later" would be most-compatible. Gmerlin tells that he uses some "GPLv2 only" mplayer code in gavl which is itself "GPLv2 or later". Cehteh explains that "GPLv2 only" code is a problem, where "GPLv2 or later" is not. Ichthyo raises concerns that the Support library may need to be LGPL, cehteh mentions that it would likely be enough if we only apply that to the plugin-loader. After all it turns out that there is a lot of confusion, misunderstanding and interpretation used with licenses and no one of us knows for sure. Finally we conclude that the interpretation and license enforcement is ruled by the copyright owner. + +Conclusion: + Learn more about GPLv3, use GPLv2 or later and propose to switch to GPLv3 when we are ready to do public beta releases when possible. + + + +Next meeting +------------ + +The next meeting will be at thuesday 3rd april 21:00GMT (if not rescheduled see below). + +Ichthyo tells that Martin Ellison wrote he couldn't attend because of the time. So again we made a short discussion about changing/alternating timings to allow people in downunder/japan to attend. They have to speak up an make time suggestions. + +Conclusion: + Next meeting 3rd april 21:00GMT. But Aussies, please speak up if you want the time changed! diff --git a/doc/devel/meeting_summary/2008-04-03.txt b/doc/devel/meeting_summary/2008-04-03.txt new file mode 100644 index 000000000..ebfee3f01 --- /dev/null +++ b/doc/devel/meeting_summary/2008-04-03.txt @@ -0,0 +1,290 @@ +2008-04-03 Lumiera Developers Meeting +===================================== +:Author: Sami Kallinen +:Date: 2008-04-09 + +__Participants__ + + * cehteh + * gmerlin + * hermanr + * ichthyo + * joelholdsworth + * johncoswell + * rgareus + * sakalli + + +Boilerplate +----------- + +Organization of this meeting +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * sakalli writes the protocol, ichthyo will help reviewing it + + +Recurring Topics +~~~~~~~~~~~~~~~~ +We concluded there are no leftovers and currently no Drafts from the design process to discuss + + +Call for volunteers +------------------- +(which tasks are to do, how can we interest people...?) + +There was a sense that there are people willing to help but they might need direction or cannot do actual coding. But there is also a lot of tasks that could be done by beginners and non-programmers. These tasks are important and if someone who is not a programmer is willing to take them on that leaves more time for the programmers. Examples of such tasks: + + * Protocol writing + * maintaining to do lists and isolating tasks + * constant code and documentation review + * unit/functional testing + * header file "task force" - some fundamentals discussed by the developers on libopenvideo should find their way into basic .h files + * installing and maintaining project tracking and automated builds/tests on the server + +See http://www.lumiera.org/wiki/todo.html#Tasks[] + +It was also noted that at current phase some basic development is still needed before we can accommodate a large number of coders doing small tasks and for now we need some subsystem owners. For GUI for example. + +To make it easier for "beginners" to get aquainted with the program it was decided that one *official Lumiera GIT repository* should be established. + + + +Project announcements/registration +---------------------------------- +(freshmeat, gnu, etc...) any takers? + +The discussion was about advertising the project on various sites. There were some concerns raised that it is too early to do too much publicity at the moment. That the right time should be shortly before the first public release. But there is also some value in registering to the project in planning/alpha stage to interest people such as prospective coders. It was noted that the http://www.linux.com/feature/126441[article on linux.com] resulted in a lot of people contacting ichthyo and cehteh. http://plot.bek.no/pipermail/piksel/2008-January/003328.html["I believe in Cinelerra"] by Leo Germani also generated a lot of traffic by developers. Some level of presence on mailing lists would be good although not too much just enough to indicate the project is going on. + +Conclusion: volunteer needed to coordinate publicity (not overly urgent, but helpful) + + + +Informal talk about the GUI +--------------------------- + +Firstly, as SimAV was not able to be present during the meeting a proposal by him was relayed by Ichthyo: GUI and backend should be completely separated and communicate via pipes. It was agreed that the GUI should be detached but some higher level message protocol rather than bare pipes should be used. Some comments: + + * cehteh standalone gui is a nice to have, but implementation details have to be worked out + * gmerlin Video playback can be separated in a separate X connection + * cehteh suggests to start with an conventional, attached gui and just keep it in mind to make it detachable later + * rgareus IMHO you want to share memory for the video-frame buffer beween the player and backend - with pipes you'll go the video-jack way. + +joelholdsworth is a new developer in the group and had showed interest in developing the GUI. he stated that he is interested in seeing a GUI that uses popular toolkits and that plays by the standards to create a consistent UI with the rest of the free desktop. For this reason he was advocating using GTKmm. There was no direct objections to GTKmm. Generally, there seems some preference for GTK. (Note this doesn't mean GNOME). Cehteh advocates the idea to use Lua-gtk, i.e. writing the GUI in a clean and lightweight scripting language. + +Cario was suggested to be used for implementing the canvas in the timeline view. + +The gui should provide a skeleton. Timeline and canvas should be controllable and extensible by plugins. Tracks can be nested. Use tiling windows and dockeable views and tools palettes. For example some configuration within session/renderpipeline may cause some pluggable elements to be added to the gui. A fader or a toggle/icon and so forth. Some plugins might render additional output (e.g. in previews). A video track renders thumbnails, automation curves on top. + +Workflow is another important aspect to take into account in the design. A lot of information needs to come from editors and users in the design process. Different thoughts about the workflow: + + * Should not make a predefined workflow. Just produce a tool that can be adapted to different workflows. It can be predefined, but should not be fixed. + * We dont have the resources to develop a "workflow" in the formal manner, we will go the "propose and comment" route. + * We should take the best ideas from popular competing products such as Final Cut Pro, AVID, Premiere plus After Effects. + +Customisation is deemed important. Some points brought up in the discussion: + + * sakalli: would be great if we could provide a fcp configuration and a avid configuration besides the default configuration as well. That could lure a lot of users quickly. On the other side, too much flexibility can be be a problem as well. + * cehteh: yes .. but such things are prolly costly to setup .. we need volunteers for anything optional + * Customization should be possible without recompile, but not too cheap. + * There should be a working skeleton or some fixed anchor but gui operations should be bound to a scripting language to achieve customization. + * The whole gui in a script with some performance critical widgets coded in C/C++ + * Some parts should just be customizable, other parts should be kept fixed. + * cehteh suggested to start out by making a \'high level' description with no language/toolkit in mind .. what widgets do we need etc and after that to work on the skeleton mockup. + * cehteh / hermanr: Lumiera should allow to choose theme independently from desktop .. because what you have as your desktop-theme doesn't suit for video editing in many cases ... see ardour + +ichthyo brings up the role of customization for the middle layer and gives an overview of his plans for the new participants. +He had good experiences with a rule based aproach in various projects. He wants to embed a Prolog interpreter that can answer to "configuration queries". There is a "builder" component in the middle layer to derive the render nodes graph based on a "high level model", which is visible to the user (in the GUI) and based on these configuration queries. + +Rationale for using prolog is: The rules are very readable because they rather represent facts and relations, not the way "how dings are done" but the way "how things are related". Example: how to configure some effects when the footage is interlaced. Additionally, we want to have some global swithces, which could control these rules, like "I want maximum quality", or "I want as much as possible set up automatically". The prolog rules are stored in the session and can be editd by advanced users. Of course, there is a set of basic rules. This aspect of customisation isn't exactly a GUI issue but it would have consequences for the GUI. + +some discussion: + + * joelholdsworth: so we just need a way of exposing properties. This is reasonably straightforward when all you want is value, colour, string etc. + * rules within the middle layer/session could bind to some properties exposed in the GUI + * gmerlin joelholdsworth: Agree, but point out that this can become difficult + * cehteh joelholdsworth: skeleton and plugins who attach into some gui areas, so you can add custom widgets + * ichthyo: also the ability to attach "tags" to various objects, so the rules can bind on those tags + * cehteh: maybe a standard set of widgets, not really completely custom ones (gmerlin agrees) + * joelholdsworth: probably you need to be able to do both well + * joelholdsworth in many apps the value/colour/string set works perfectly well, but in luminierra the controls will likely need to be much richer + +Some brainstorming points about usability: + + * We need a realy good bezier widget for all sorts of curves. + * Faders that can be grabbed everywhere are nice like like ardours and Blender faders. Or others which have no big knob + * The controls in blender's node view would be a good modal to copy (not advocating GL UI, just using widgets and concepts). + +One extra point: + + * we have agreed to make all of the big interfaces between the layers as plain C interfaces, because this is the most widely supported common denominator. + + +GUI CONCLUSION +~~~~~~~~~~~~~~ + + * joelholdsworth is now the official GUI maintainer for Lumiera! + * sakalli, hermanr, ichthyo announced interest in contributing to discussions about GUI, workflows and usability. gmerlin noted that he knows some gtk secrets and is willing to help out here too. + + + +Froscon application +------------------- +http://www.froscon.org/[Froscon] is a small nice german open source exhibition that is very very technical oriented. Cehteh will be there as well as Ichthyo. Question is wether we should have a booth, perhaps together with some other interested media projects. Could ask Richard and the ffmpeg/mplayer people. End of call for papers is in june. + +We made no decision about official presence but most likely there will be a developer meeting if nothing else. + + + +Timecode metadata discussion +---------------------------- +This Discussion started out on the libopenvideo list: The question was wether we agree on one uniform format solution from decoder up to app level regarding timecode. And what this format would be: arithmetic or struct type. Regarding metadata (including type of timecode) the question is if we should work out a rather fixed data format, or go the route towards rather open description records. + +Conclusion: Gmerlin does it the way it fits best for within the decoder, and Lumiera uses a more uniform time format for representing timecode in the higher app layers. Conversion functions will be concentrated in a library. Regarding metadata we will investigate further. + +_A (shortended) digest of the timecode and metadata discussion is attached below_ + + +Next meeting +------------ + +The next meeting will be at thuesday 8th may 21:00GMT + + +'''' + +.Timecode discussion +---- +12:51 cehteh gmerlin: did you read my last mail about generalization of metadata? +12:51 cehteh key/value pairs +12:51 gmerlin not sure about that +12:51 cehteh i am pretty sure that i want that in lumiera +12:52 cehteh if gavl does that for us it would be even better +12:52 gmerlin first of all 2 different things: +12:52 gmerlin 1. arithmetic <-> struct +12:53 gmerlin 2. Generic <-> format specific +12:53 cehteh arithmetic for sure +12:53 gmerlin On the decoder API side, I want to export ideally only one type +12:54 gmerlin No no SMPTEXXX and ISO999908098 +12:54 cehteh will that be attached to the frames in a intrusive way? +12:54 cehteh i rather leave the attaching up to the application +12:54 gmerlin intrusive? +12:55 cehteh struct frame { uint64_t timecode; } vs... +12:55 cehteh struct frame { struct metadata* something; } +12:55 cehteh or even vs something done at a api level +12:56 gmerlin struct lumiera_video_frame { gavl_video_frame_t * f; uint64_t timecode}; +12:56 gmerlin Or add timecodes to gavl_video_frame_t +12:57 cehteh struct lumiera_video_frame { gavl_video_frame_t * f; struct metadata* timecode_n_stuff; } +12:57 ichthyo so I support a struct metadata* +12:58 cehteh ichthyo: timecodes might not be regular .. they should be optional, but anyways they dont cost when using an arithmetic type +12:58 cehteh i mean thats the purpose of timecodes .. to tag every frame and dont make assumptions that your footage is perfect +12:58 ichthyo but a struct metadata* would rather mean to keep it out of basic GAVL and have a libmetadata -- which may be integrated with GAVL -- right? +12:59 cehteh someome may have stolen a frame :P +12:59 cehteh thats gmerlins decision ... +12:59 gmerlin I'm against gavl integration of metadata +12:59 gmerlin gavl has a clear scope: Number crunching +01:00 cehteh gmerlin: you somehow have to pass it from the decoder to the application +01:01 cehteh i would prefer to introduce some sidechannel +01:01 gmerlin Yes, gmerlin_avdecoder will provide a separate function for getting the next timecode +01:01 gmerlin bgav_get_next_timecode() +01:01 cehteh gavl_get_frame (gavl_frame* place the frame here, metadata* place metadata there) +01:01 gmerlin or: bgav_timecode_from_pts() +01:03 gmerlin Some higher MXF profiles allow changing timecode parameneters in one stream +01:06 gmerlin It's sometimes impossible to separate demuxers and codecs +01:06 gmerlin better have them together in one lib +01:06 cehteh actually i would like to cache gavls internal states into the backend +01:08 gmerlin Ok, so timecodes are YYYYMMDDHHMMSSFF as int64_t +01:08 cehteh the indexing is something i prolly have some words about +01:09 cehteh timecodes are uint64_t as microseconds +01:09 cehteh well for lumiera :-P +01:10 cehteh YYYYMMDDHHMMSSFF as int64_t isnt that much better than a struct :P +01:11 ichthyo now, IMHO its mostly up to gmerlin to decide how it fits into GAVL +01:11 cehteh ack +01:11 gmerlin Not gavl +01:11 ichthyo gmerlin? +01:11 gmerlin gmerlin avdecoder yes. +01:11 cehteh in lumiera we will use usecs +01:11 ichthyo yes +01:12 ichthyo we will have a conversion function somewhere +01:12 gmerlin And will it include a calendar date? +01:12 cehteh doing it only once (avdecoder->usec) makes some sense +01:12 gmerlin usecs since when? +01:12 ichthyo either, we get directly usecs from avdecoder, if this fits in for gmerlin, +01:12 ichthyo or we'll get the format which works best for him and we do the conversion ourselves +01:12 cehteh defined elsewhere +01:12 cehteh reel-start or absolute time or whatever +01:13 ichthyo also, what sort of timecode this was based on (drop frame or not) +01:13 cehteh gmerlin: thats a important point .. just uint64_t does not suffice +01:13 cehteh it needs a tag describing what kind of timecode was used +01:14 cehteh no matter how you encode it +01:14 cehteh and iirc there are quite some timecodes +01:14 ichthyo because in this respect richard was right: we need to be able to reprocuce any value 1:1 +01:14 cehteh yep +01:15 cehteh further i want to be able to convert between any kind of timecode in a fingersnip +01:15 gmerlin ichthyo: and that's impossible with usecs +01:15 cehteh gmerlin: its not +01:15 ichthyo with usecs alone: agreed +01:16 ichthyo with usecs + metadata it should be possible +01:16 ichthyo modulo some extreme cases (high speed cams, weeks of footage....) +01:16 cehteh yes .. you need framerate and other infos depending on the metadata +01:17 cehteh you need to be careful to do the math right so that inverse functions dont drift +01:17 gmerlin I'll export something lossless, which repduces MXF, DV and Quicktime timecodes losslessly +01:17 gmerlin You can then do what you want with them :) +01:17 cehteh thats why i would like to do it at only *one* place/library .. because its delicate and bugs are easier to spot/fix there +01:18 ichthyo and then there is some api to get at the additional metadata +01:18 cehteh yeah +01:18 ichthyo and beyond that: lets devise a libmetadata +01:18 cehteh just shove the data over .. YYYYMMDDHHMMSSFF as int64_t would be ok +01:18 gmerlin Ok. +01:18 cehteh we make a libmetadata (as part of our support lib) +01:19 cehteh somewhat independent so that it can be spun out +---- + + +.about Metadata +---- +01:19 cehteh gmerlin: well .. how bout other metadata +01:19 cehteh your decoder needs to pass that too +01:20 gmerlin like? +01:20 gmerlin Author, Artist title... +01:20 cehteh some cameras encode shutter speed, focus, gain control, colour balance +01:20 cehteh and other things +01:20 gmerlin Hmmm +01:20 gmerlin I think MXF exports some of these +01:21 ichthyo wav files can have additional chunks; whats with flac? +01:21 gmerlin flac has vorbis metadata +01:21 ichthyo important for the more modern sound formats +01:21 cehteh dunno .. just thinkin about pro codecs and raw film metadata +01:21 ichthyo just wanting to point out: things to come here, +01:22 ichthyo e.g for sound: what stream is what channel +01:22 gmerlin Channel locations are handles by gavl +01:22 gmerlin If you have a multichannel stream +01:22 cehteh well .. note that there is per-asset metadata you want to pass .. and per frame metadata +01:22 cehteh maybe even some more undiscovered cases :) +01:23 cehteh did someone say this is easy?? +01:23 sakalli did any of you watch the video regarding red workflow that i sent to the mailing list? redcode raw holds everyting, asa, wb, etc... hope lumiera will be able to facilitate that as well. +01:23 ichthyo at some point I want to be able to support ambisonics +01:24 ichthyo and maybe some day there will be wave field sysnthesis in more broad use +01:24 cehteh the codec in the back needs to pass us these things else we cantb handle it +01:24 gmerlin well, the easiest would be to extend bgav_metadata_t +01:24 ichthyo rationale is: just be open +01:24 cehteh gmerlin: maybe we need to investigate this things more, nothing needs to be decided now +01:24 ichthyo ack +01:25 cehteh see my key/value metadata proposal .. at some point maybe you make a complete sidechannel just for metadata +01:25 cehteh decoder stuffs that in on demand and it is passed along with its own api like the bgav_timecode_from_pts() +01:26 cehteh that way you dont need to touch it more than necessary +01:26 gmerlin shouldn't that key/value stuff be unified with plugin parameters? +01:27 cehteh for lumiera, maybe, maybe not +01:27 ichthyo you mean: you have a common api for describing "values" ? +01:27 cehteh some plugin parameters are determined by math functions +01:27 gmerlin You have a key, a type and a value +01:27 cehteh maybe for caching them +01:27 cehteh the api might be unified .. but the storage not +01:28 gmerlin why? +01:28 ichthyo because any block of data could be in the metadata +01:28 cehteh metadata which belongs to the frame coming from the decoder is const +01:28 cehteh plugin parameters are volatile + +01:33 gmerlin One other point (but too late for it now) are audio timecodes. I'll adress that on libopenvideo +01:33 cehteh we will write a little more sophisticated timecode library +01:34 cehteh gmerlin: that should be unified :) +---- diff --git a/doc/devel/meeting_summary/2008-05-08.txt b/doc/devel/meeting_summary/2008-05-08.txt new file mode 100644 index 000000000..6ab08ebde --- /dev/null +++ b/doc/devel/meeting_summary/2008-05-08.txt @@ -0,0 +1,244 @@ +2008-05-08 Lumiera Developers Meeting +====================================== +:Author: cehteh +:Date: 2008-05-15 + +__Participants__ + + * cehteh + * joelholdsworth + * ichthyo + * raffa + * Wes``Lappy + * gmerlin + + +Boilerplate +----------- + +Organization of this meeting +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * cehteh writes the protocol + + +Webpage-Infrastructure, Maintenance +----------------------------------- + +Cehteh put this on topic because it's really urgent to bring up some +infrastructure to manage the information we produce. + +The Lumiera pages on pipapo.org were always meant as intermediary +solution. Pipapo.org gets much spam on the moinmoin wiki and cehteh +expresses that he wants to move pipapo.org to a new infrastructure +based on asciidoc and git he created +(see http://git.pipapo.org/uWiki.html[]). This system is barely useable +and needs lots of work to be completed. It would be nice to use it for +Lumiera too, if the others agree. Maintaining and extending +(scripting) needs someone who knows shell scripting and doesn't need +to be done by the core devs freeing their resources to work on +Lumiera. Cehteh expresses that none of the people who proposed to +help before showed up yet. WesLappy tells he might help (addendum not +this week, because he is busy). + +Next there was a suggestion by cehteh to convert the tiddlywikis to +asciidoc. Ichthyo expresses that he likes the tiddlywikis, Joel +mentions that they feel a little odd. Generally we all agree that +they have some problems in sense of workflow and merging. + +Ichthyo makes the suggestion to keep the tiddlywikis as scrapbook and +build up \'official' documentation based on their content in +whatever-we-use-then (asciidoc) documentation system. All agree on +this approach. + +Back to the new wiki, cehteh suggests to make stricter access rules to +prevent spam: "There will be a group of \'Validated' people which +following rules: only Validated people can edit general content and +\'Validated' people can add new people to \'Validated' themselves". That +means that we can have a lightweight self-administrating +authentication where new people have to be introduced by someone who +is already there. + +Ichthyo suggests to send a reply to Serge G. who send an introduction +to the cinelerra mailing list expressing that he would like to help. + +Raffa will take care of content/design as much her time/knowledge permits. + + +Conclusion +~~~~~~~~~~ + + * We really need someone to help with the webpage scripting. + * Documentation needs to be well organized and moved over. + * Content of the pipapo.org moinmoin wiki should be moved over. + * The new website should be well organized with a nice looking frontpage + * All other documentation should be below that + + + +Whats pending in link:DesignProcess[] +------------------------------------- + + +MistakestoAvoid +~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/MistakestoAvoid[] + +rick_777 wrote a "MistakestoAvoid" page which explains some possible +gotchas. We basically agree with most points there while we already +decided to address some things differently than he suggested. One +noteable difference is that we do not intent to provide a windows +version of Lumiera, which was explained in serveral places. Cehteh +added some comments to the page explaining some things. + + +nothing more to discuss in \'Ideas', we go over to \'Drafts' + + +ArchitectureOverview +~~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/ArchitectureOverview[] + +Cehteh suggests to put that drawing under version control, as .fig. +Ichthyo explains that it is already a .SVG and that he does not like +.fig. + +Conclusion: We agree to keep it as .SVG, add it to the repository and +improve/complete it. + + +GitSubmoduleTransistion +~~~~~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/GitSubmoduleTransistion[] + +Another suggestion made by cehteh is to make managing of subprojects +within the sourcetree easier. Joel and ichthyo oppose that it is not +really needed now and needs more understanding of git. Ichthyo +suggests that the documentation might be separated into its own git, +he further explains that the things are not settled yet and we don't +know if we will some rearrangements and movements of files between +modules. + +Conclusion: We transform the documentation to a submodule and see how +it works. Other things will be decided much later. + + + + +GlobalInitialization +~~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/GlobalInitialization[] + +This topic is discussed and agreed on the link:DesignProcess[] page already. + +Conclusion: finalize it + + +MasterRepositorySetup +~~~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/MasterRepositorySetup[] + +Setting up an master repository was decided on the last meeting. +cehteh now set one up which also does some automatic, but intended +fragile merging from subsystem maintainer branches. + 1. it updates automatically for the maintainers repo for conflict + free fast-forwards + 2. it can always be overridden with explicit pushes + +The others agree on the setup. + +Ichthyo stresses that maintainers shall watch that their \'master' +branch should compile cleanly and pass the testsuite always, test +which are not operational should be tagged as PLANNED. We all +agree, while cehteh's master is currently in a broken state (note: +fixed now). + +Then a short discussion about building and synchronizing the docs +follows. Problem is that docs build in maintainer branches are +different for each branch, rsyncing them up to the server will always +change a lot of things. We agree that the \'official' docs shall be +build from the LUMIERA/master repository, preferably on the \'devel' +vserver which has to be set up. + +Conclusions: Make this final, setup build environment in the devel +server and build docs there. + + + +NoBugFlags +~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/NoBugFlags[] + +Defining a debugging control hierarchy. This is work in progress and +cehteh explains that he works it out and deploys it, this depends on +the link:GlobalInitialization[] decided earlier. + +Conclusion: accepted, finish and finalize it. + + + +Further Technical Discussion +---------------------------- + +Ichthyo asks how the GUI will be pulled up. Since he didn't attend IRC +discussions recently he has no information yet whats going on. We +explain him that we already made some discussions. Integrate the GUI +into the build system probably linked as library, nothing finally +decided yet. Communication will go over the plugin/interface facility +(Plain C API). This things should be worked out and documented in +link:DesignProcess[]. + +Cehteh made a small tool `./admin/headercheck` which will gradually +extended to proof that headers are sufficiently selfstanding. + + + +Progress +-------- + +Cehteh finished low level file handling and working in mmaping and +frameprovider next. When thats finished, the finalization of the +Plugin loader and interface definition things is the most urgent thing. + +Ichthyo works on the builder internals and next on some sort of +"connection manager". + +Joel goes on with GUI programming and integrating it into the source +tree next. + +Gmerlin and cehteh discuss about how to handle the index files which +avdecoder uses internally. cehteh would like to manage them in the +Lumiera backend to, because filehandles are a precious resource. +Gmerlin explains that this index files are just loaded and kept in +memory. So this poses no problem for filehandle exhaustion. We will +discuss this further via email. + +Cehteh suggests that we should think about some kind of +preferences/registry sometime next to store default configs. + +Following a discussion about how messages are passed between GUI and +core. Generally we will use the interfaces defined by the plugin +system. Gmerlin says that he uses message queues in \'gmerlin', Joel +requests that a lot of things should be done synchronous and he wants +to avoid message queues. Cehteh suggests to use use the scheduler for +GUI things too. For the parts where the GUI interacts with the high +level proc model ichthyo and joel agree on working something +(synchronous) out. Ichthyo stresses that the edit core is not +threadsafe by design and relies on the backends scheduler. +The underlying builder might sometimes to expensive for synchronous +calls (ichthyo plans a rule system there) this needs to be worked out. + +Ichthyo explains that the builder needs to detect cycles and check if +the high level model is translateable into the low level model (in +case plugins git removed and so on). Parts in the render graph which +are not \'doable' should be flagged as erroneous but not dropped since +one doesn't want to loose project information when loading a project +on a machine with different installed plugins. In any case it should be +possible that the GUI gets immediate synchronous feedback for such +things. + + + +Next Meeting +------------ + +Next meeting is on 5th June 21:00 UTC diff --git a/doc/devel/meeting_summary/2008-06-06.txt b/doc/devel/meeting_summary/2008-06-06.txt new file mode 100644 index 000000000..473edd6f5 --- /dev/null +++ b/doc/devel/meeting_summary/2008-06-06.txt @@ -0,0 +1,81 @@ +2008-06-06 Lumiera Developers Meeting +===================================== +:Author: ichthyo +:Date: 2008-06-07 + +21:00 -23:15 UTC on #lumiera + +Participants: +------------- + * cehteh + * ichthyo + * joelholdsworth + * rcbarnes + * raffa + + +Left over from the last meeting +------------------------------- +Nothing left over and no urgent topics. Seemingly, work is proceeding in all parts of the application. + + +Discuss Ideas and Drafts in design process +------------------------------------------ +There are no new design proposals and no proposals that can be finalized. + +Ichthyo points out that he's about to work out the details of some of his proposals, which are currently in "idea" state. Following that, most of the meeting is spent on discussing the details of two of these proposals. + + +Idea: Design the Render Nodes interface +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/DesignRenderNodesInterface[] + +*Cehteh* points out that, as we are in the pre-alpha phase, interfaces may be growing on-demand. Later on, interface versions will be numbered. If needed, we could add a special "draft" or "experimental" tag, or, alternatively, use the common numbering scheme, where odd major version numbers denote the development line of an interface. + +*Ichthyo* agrees, but adds he also meant "interface" in this proposal in a wider sense, like in "what do we need and require from a processing node". Knowing how generally Lumiera will handle the processing nodes while rendering helps him with defining and implementing the builder + +__Conclusion__: "currently in work". For now, grow interfaces on demand. + + + +Idea: Placement Metaphor used within the high-level view of Proc-Layer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/ProcPlacementMetaphor[] + +In the course of the discussion, *Ichthyo* explains the rationale + + * one common mechanism for sticking objects together and putting them into the session + * either specify the "placement"-parameters (time, output destination, track) directly, link to another object's parameters, or derive some or all of those values from the context (up the tree of tracks) + * ability to build a system of high-level media objects (clips, effects...) which re-adjust automatically on change + * extensible to handle or derive some parameters based on conditions and rules, e.g. for semi-automatic wiring of the output destination based on tags + +*Joelholdsworth* is concerned that this proposal may go too far and tries to tie things together which aren't really connected. While basically it's no problem having the time position of a clip either absolute, or derived by a link to another object, he can't see a clear benefit of controlling sound pan or video layer order from the placement. Pan, for example, is just an parameter value or interpolated curve, similar to colour correction or gamma adjustment. For the gui, he points out, it's probably better to stick to the object metaphor, so start time, output, layer number or sound pan would be properties of the object. + +But that's exactly what Ichthyo wants to avoid. Obviously, this would be the standard solution employed by most current editing apps, and works reasonably well in average cases. But he is looking for a solution which covers this standard case, but also doesn't get into the way when dealing with advanced compositing, working with spatial sound systems (Ambisonics, Wave Field Synthesis) or stereoscopic (3D) video. + +On the whole, there is no conclusion yet. Certainly, this proposal needs more discussion, parts need to be defined much more clear (esp. the "Pro" arguments), maybe parts of the functionality should be separated out. + +While in this discussion, several aspects of the cooperation of GUI and Proc layer are considered. + + * it is not necessary to make all of the Placement proposal visible to the GUI (and the user). Proc layer may as well provide a simplyfied and hard wired API for the most common properties (layer index, pan) and only use this part of the Placement concept for building and wiring the nodes. + * the adjustment of objects linked together by a placement can be handled as follows: + . GUI notifies Proc of a position/parameter change of one object and gets immediate, synchronous feedback (OK or fail) + . Proc detects the other linked objects affected by the change and notifies GUI (both synchronous and asynchronous is possible) to update information regarding those objects + . GUI pulls the necessary properties by calling Proc on a per object base. + * as a rule of thumb, GUI <--> Proc is mostly synchronous, while Backend <--> GUI is often asynchronous, but there are exceptions from the rule + * we have general _parameters_, which are automatible. These are represented as _control data connections between the nodes_. We certainly don't want to represent some things in this way, though. For example, the in/out points of clips are fixed values. + * in Ichthyo's concept, the Placement doesn't itself provide such parameter values/sources, rather it can be used to _find_ or _derive_ parameter sources. + * the node graph is built bottom up, starting at the source, then via the effects attached locally to a clip, further on upwards (directed by the tree of tracks) to be finally connected via global busses to the output ports. Rendering pulls from these output ports. + * Joelholdsworth, Cehteh, Ichthyo and Rcbarnes agree that the plain _node-editor_ approach is problematic in the context of a NLE. It shows too much details and fails to capture the temporal aspect. We strive at having node-like features and flexibility, but rather within the timeline. + * especially, the topology of the node graph isn't constant over the whole timeline. But it's the job of the builder in the Proc layer to deal with these complexities, the user shouldn't be overwhelmed with all those details. + + + +Next meeting +------------ + * some people in europe complained that 21:00 UTC is too late, esp. with daylight saving + * there was the proposal to alternate between the current schedule, and sunday 16:00 UTC + * but Joel can't attend on sunday afternoon for now + * so we settle down on thursday, 16:30 + +Next meeting: *Thursday 3. July 2008 16:30 UTC* diff --git a/doc/devel/meeting_summary/2008-07-06.txt b/doc/devel/meeting_summary/2008-07-06.txt new file mode 100644 index 000000000..a7255a802 --- /dev/null +++ b/doc/devel/meeting_summary/2008-07-06.txt @@ -0,0 +1,94 @@ +2008-07-06 Lumiera Developers Meeting +===================================== +:Author: Teld +:Date: 2008-08-10 + +Participants +------------ + * ichthyo + * cehteh + * jilt + * dmj726 + * Teld + * \_nasa_ + * alcarinque_ + * MNO + * Wescotte + * weatherhead + + +Boilerplate +----------- + +Organization of this meeting +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * dmj726 intends to write the protocol + * ichtyo is chairman + * there is no agenda, so this is a freeform meeting + +Leftovers from last meeting +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +There are no leftovers + + +Introduction of Lumiera +----------------------- +Because there are quite some new participants, an introduction of the project Lumiera is given + +There are 3 core devs: + + * ichthyo: proc layer, render graph, in the middle, C++, he maintains scons + * cehteh: backend serving data (from filesystem and so on), manages threads, using C, Posix System programming, maintains autotools and git + * joelholdsworth: GUI in C++/Gtkm +Other people involved: + + * rcbarnes: ui designer and coder + * raffa: web content + * Simav: gives a hand when and where he can + * Teld: web infrastructure +The foundations of the design are already done but a lot of detail needs to be worked out. cehteh and ichtyo provide a non exhaustive list. + +cehteh: + + * improvement of the testsuite (simple Bash) + * start of a worker thread manager (Posix knowledge required) + * start of the scheduler (some Posix, good C coding) + * runtime profiler (little but advanced Posix realtime programming job) + * review of code and documentation + * system administration + * setup of a build/test environment on the server + * setup and maintain postfix/dovecot by a mail administrator +ichtyo: + + * asset management (keeping track of all media files, captures, clips, scenes) + * session loading saving and the issues of compound documents + * session structure related issues to be able to Import/Export/Connect via e.g. OSC (Open Sound Control) or JACK + * flesh out the more high level "edit operations" and the interface to UNDO + * Prolog integration after the first integration round has been reached. The Prolog interpreter will do some of the more advanced configuration (example: if effect XYZ is used, then deinterlace beforehand). + * integration with some sort of production support software (like Celtx) +cehteh emphasizes that Lumiera is an open project, so anybody can jump in where he sees fit as long as he communicates and acknowledges with the persons involved. ichtyo points out that the plugin structure is very important: anything that is not legally completely clean (proprietary), should be factored out into a sister project and be loaded as plugin. + + +Issues and questions that come up +--------------------------------- + * handling of config errors. +When the configuration has a syntax error, in 90% of the cases there will be no possibility to do anything sane. Therefore, the config system will be built to log a warning and the user code does not need to care. The user just gets an alert and the application continues to work. + + * scripting language. +There will be a scripting interface. ichtyo does not want scripts everywhere, only at well defined interfaces. That implies also that scripts cannot just do anything, only that what is permitted in a controlled way. The meeting agrees on that. cehteh wants one default language and proposes Lua: light, simple. + +Other members suggestions: Python, Ruby, Scheme. However, Python and Ruby are very slow. Scheme has many variants. + + * editing on small devices (eeePC) +Problem: video editors GUIs are some of the most elaborate and complicated GUIs. However, basic functions consist of only a few buttons. Proxy editing could be a solution. It is decided that it is not a primary goal now. First the basics have to be implemented. + + * uWiki. +uWiki is the glue between a markup driver (Asciidoc) and revision control (git). Haserl with Bash is used now. Haserl appears to have problems with conditional includes. Its limits have been reached while prototyping. Lua could very well be a valid replacement. It will be investigated. PHP is rejected because it is not stable and suffers from serious security problems. + + * \'musical\' timeline in bars and beats +The questions of syncing and linking things together are already on the core agenda: the so-called placement concept. Discussion is still going on, especially with the GUI developer joelholdsworth. See for detailed information: http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/ProcPlacementMetaphor[] + + +Next meeting +------------ +The next meeting will be held Thursday, 7 August 19:00 UCT. diff --git a/doc/devel/meeting_summary/2008-09-04.txt b/doc/devel/meeting_summary/2008-09-04.txt new file mode 100644 index 000000000..dbb7b0e5a --- /dev/null +++ b/doc/devel/meeting_summary/2008-09-04.txt @@ -0,0 +1,74 @@ +2008-09-03 Lumiera Developers Meeting +===================================== +:Author: Joel Holdsworth +:Date: 2008-09-05 + +Participants +------------ + * ichthyo + * cehteh + * joelholdsworth + * Plouj + * thorwil + * wildhostile + + +Boilerplate +----------- + +Organization of this meeting +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * joel + * ichtyo is chairman + * there is no agenda, so this is a freeform meeting + +Leftovers from last meeting +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +There are no leftovers + + +Mailing Lists +------------- + * any improvements should be reported to cehteh + * need volunteers to be moderators. The only volunteers are cehteh and joelholdsworh_ + * moderating the ML might not be that big a job - we might not get that much spam + * it's preferable to allow people to post to the mailing list even if they're not subscribed + * cehteh eventually wants the mailing list to interact with uwiki. + +Plugin Interfaces +----------------- + * all plugin interaction, including manifest queries will happen through the system of interfaces + * this includes enumerating structured data within the plugins. It might sometimes be desirable to store this in a text file, but in this case still the list will be exported through the interfaces. If this were not the case, it would not be possible to make plugins which are wrappers of LADSPA plugins for example. + * For a lumiera plugin there is only one entry point, thats an interface itself which bootstraps all the other interfaces in that plugin. + +Lib Dependancy Problems +----------------------- + * we've agreed to stick to one compatibility level: Debian/Lenny for perhaps up to 3 years from now + * others will be supported - RedHat etc. + +Builds and Buildbot +------------------- + * ichthyo asks for help setting up a test/build server + * We now have build scripts based on scons and autotools. + * All current build systems have different problems. + * ichthyo will take care of the Debian packages for Lumiera and NoBug + +Website Transition Plan +----------------------- + * There's lots of work to do on the website - transitioning etc. that the core developers don't want to get bogged down with. We need a web developer to volunteer. + * All the website stuff is stored in git, and works through Asciidoc, which is not difficult to learn. + +Logo Design +----------- + * Postponed for the next meeting when joelholdsworth is around. + +Next meeting +------------ +The next meeting will be held Thursday, 2 oct 21:00 utc. + + +Comment +~~~~~~~ +re "ichthyo asks for help setting up a test/build server" for "Debian" + +see "Build Server" at http://www.pipapo.org/pipawiki/Lumiera/GuiBrainstorming?action=edit&editor=gui[] diff --git a/doc/devel/meeting_summary/2008-10-10.txt b/doc/devel/meeting_summary/2008-10-10.txt new file mode 100644 index 000000000..93de6ca38 --- /dev/null +++ b/doc/devel/meeting_summary/2008-10-10.txt @@ -0,0 +1,232 @@ +2008-10-10 Lumiera Developers Meeting +===================================== +:Author: Ichthyo +:Date: 2008-10-15 + +Oct 10, 2008 on #lumiera 19:00 - 23:15 UTC + + +__Participants__ + + * cehteh + * ichthyo + * joelholdsworth + * alcarinque + * !KenSentMe + * Plouj + * raffa + * thorwil + * Victor_Sigma + * wildhostile + + +organisational +-------------- +Not much of a fixed agenda this time. + + +Agreement to start with the Logo discussion, as this is of general interest, followed by the design drafts and similar dev topics. + + +The Lumiera Logo +---------------- +Summary of the situation: discussion regarding a logo for Lumiera is going on sporadically since quite some time. Several people from the community have made proposals. Also, there was discussion about the criteria a logo would have to fulfil. Especially the core devs raised the bar and required quite some professional level of design. On the contrary, several members of the community were concerned that such a demanding attitude will hinder creativity in a domain which is far off from coding. Moreover, many people complained they are really excited about Lumiera and strongly want to participate in some manner, but find it very difficult in the current phase of the project to give any valuable contribution. + +This summer, one of the proposals by http://farm4.static.flickr.com/3060/2927333034_ac94be80ea_b.jpg[Leandro Ribeiro] gained some popularity and especially was embraced by two of the core devs, while the GUI core dev wasn't convinced and http://www.pipapo.org/pipawiki/JoelHoldsworth/LogoThoughts[explained] his reservation. Prior to this meeting some people joined a brainstorming session and came up with http://www.pipapo.org/pipawiki/Lumiera/Logos?action=AttachFile&do=get&target=combo.png[another design] compiled of several proposals, which could meet the acceptance of the core devs. At the same time, Raffa made an argument for conducting a public contest, similar to the one which gave us the name of Lumiera. The situation for Lumiera is somewhat special. Usually, the community builds when the product is already minimally usable; we can't have users for now, but we have a lot of prospective users. + +Thus, while basically it would be possible for the core devs to shorten the process by just picking a design which is acceptable for all, maybe after augmenting it a little, several of the arguments articulated this far are in favour of a more formal decision by a contest: + + * it would allow for a lot of people to really participate + * it could help to shape a general (graphic) style for Lumiera + * it could underpin the fact Lumiera indeed is a collaborative effort + * it doesn't mean additional work for the core devs on the whole + * it helps spreading the word + +Then, there is some discussion about the requirements. The core devs are concerned to keep up some quality level, because there is the possibility for the community to embrace a design, but when Lumiera enters the area it is intended to do, the standards of comparison will be different. The GIMP logo can be quoted as a negative example here. + + +Conclusion +~~~~~~~~~~ +There will be a Lumiera Logo contest. + + * we should further discuss requirements on the Mailinglist until the end of the next week + * the '''deadline for submissions''' will be the next meeting (Nov 12 2008) + * then, after a pre-selection phase, the vote shall be conducted prior to the December meeting. + +Some minimal technical requirements will be enforced: + + * the basic sign fits a compact rather quadratic bounding box (like 4:3) + * easy to print on various media (on posters, t-shirts, ..) + * basically a shape, using only a small number of solid colours (web safe) + * should work on different backgrounds, esp. not requiring white background + * the design should scale from microscopic size (favicon) up to big posters + * vector graphic source is mandatory + +Besides, we give some artistic guidelines + + * it should be recognisable + * it should contain something like a sign, not just "Lumiera" in a fancy font + * it should not rely on transparencies, gradients and subtle shades, + * it should be viable, possibly work well in different presentation styles, able to be simplified as well as graphically augmented + +Raffa volunteers to help organizing the contest and the voting. + + + + + +Recurring Topics: design proposals +---------------------------------- +Discussion of open http://www.pipapo.org/pipawiki/Lumiera/DesignProcess[design process] drafts. + + +Mistakes to avoid +~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/MistakestoAvoid[Mistakes to avoid in the Lumiera Design] +We are carrying this one along since quite some time and we'd like to get rid of it, either by reworking it or by dropping it as-is. Because it contains a mixture of things + + * we fully agree to 80% of the statements made there, but we think those statements are so very basic and self-evident as to be considered off-topic here. We are aware of the recurring problems with open source video editing. That's why we are here. + * the proposal draws conclusions on two technically substantial points, at which we don't agree. And it fails to provide sufficient (technically sound) arguments to prove these statements. + +While it is certainly ''desirable'' to be cross-platform as much as possible and especially '''target Microsoft Windows''', we don't see much possibilities with today's mainstream technology to build an application which is as technologically demanding as a video editor is. We would end up developing two or even three sister applications, or we are forced to sacrifice performance for portability. When put up to face such options, we have a clear preference to concentrate on a really free and open platform. + +While it is certainly ''desirable'' to make the application as robust as possible, we don't see how '''using multiple separate processes''' could help us with this goal ''without creating major scalability or performance problems'' due to the use of shared memory. And, yet more important: we don't share the basic assumption made in the proposal, namely that video processing is inherently dangerous. We think the basic algorithms involved are sufficiently well-known and understandable to implement them in a sound manner. + +__Conclusion__: drop it + + +on the question of separate processes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The only practical solution would be to separate the GUI. Separating backend and proc-layer doesn't make much sense, technically speaking. We re-considered this question. Joelholdsworth (GUI) and Ichthyo (Proc-layer)prefer running the GUI in-process and to avoid the additional hassle with shared memory. Cehteh (backend) doesn't care for know and would like to re-discuss it as an option later on. This is somewhat similar to the possibility of running Lumiera distributed over the network, which is a goal, but not an immediate one. + + + +Tag Clouds +~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/TagCloudsOnResources[Tag clouds on resources] +Tags are considered very important. Meanwhile, we have a much more advanced proposal, which superseeds this one: http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/DelectusShotEvaluator["Delectus"] + +__Conclusion__: drop it + + + + +Overview of Lumiera Architecture +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/ArchitectureOverview[Architecture Overview] +The purpose of this drawing to give an first impression what subsystem is where and what the names mean. Since discussed last time, Ichthyo re-arranged the plugins as requested and added some details for the backend. Joelholdsworth points out that it's OK for him to show the GUI just as a single block here (and of course the GUI has much more internal structure). Cehteh adds that maintaining this drawing surely is a moving target, so we better remove the rendered image and just retain the textual description and link to the source (SVG), which is in GIT. + +__Conclusion__: accept it, change the image to a link + + + + +EDLs as Meta-Clips +~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/EDLsAreMetaClips[EDLs are meta-clips] +This is just a high-level proposal, which doesn't go into technical details of implementing nested EDLs. It just poses the question "do we want to treat nested EDLs as being meta-clip" -- which we do. + +__Conclusion__: accepted + + + + +The Builder +~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/ProcBuilder[Builder in the Proc-Layer] +Similar to the previous one, this is a high-level proposal. It covers the fundamental approach Ichthyo takes within the Proc-Layer. Cehteh adds that he agrees to 98% and the remaining 2% are just matter of favour. Personally, he would have preferred one large graph which gets garbage collected (instead of the segmented graph) + +__Conclusion__: accepted + + + + +High-level Model +~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/ProcHighLevelModel[Overview of the High-level model within the Proc-Layer] +Cehteh queries if this shouldn't be better moved over to the documentation? He is fine with the contents, but it seems to be a bit voluminous for a design proposal. Ichthyo asks to leave it there just for now, as he feels it still needs review. + +__Conclusion__: leave it for now, maybe retract it from the design proposals and file it as documentation? + + + + +Lua scripting language +~~~~~~~~~~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/ScriptingLanguage[use Lua as required scripting language] +All core devs agree with this decision. Joelholdsworth points out that he is fine with Lua, just he didn't want to write the GUI in it. Ichthyo adds that Lua is probably the best choice from today's mainstream scripting languages, because it is very lightweight. He further points out, that having Lua as ''required'' scripting language doesn't rule out using other popular languages (Python, Ruby) for scripting. Just they aren't required for running Lumiera. Cehteh will have a look at the details as soon as possible, but has currently other more urgent things in the queue. (Plouj shows interest to help here) + +__Conclusion__: accepted + + + + +Time Handling +~~~~~~~~~~~~~ +http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/time_handling[time handling] +A long standing proposal; meanwhile we've decided to build on GAVL, which is now reflected in the text of this proposal too. Ichthyo points out he changed the rounding rule in this proposal from "mathematical" to "floor (x+0.5)". Cehteh asks if we should use a thin library layer on top of gavl, to centralize all the time calculations. There seems to be agreement that this should actually be done ''on demand.'' Joelholdsworth sais sometimes he'd prefer to have a simple raw type to represent time, because it makes calculations much easier. Ichthyo adds that internally (within the layers, but not on interfaces) one could use a thin C++ wrapper with overloaded operators, which is default-convertible to gavl_time. + +__Conclusion__: accepted + +Note: the proposed rigid testsuite for time handling is necessary only when we introduce a wrapper... + + +Interface naming convention +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +See the design proposal http://www.pipapo.org/pipawiki/Lumiera/DesignProcess/InterfaceNamespaces[Interface Namespaces]. While working on the plugin loader, '''Cehteh''' and '''nasa''' did some brainstorming about a plugin naming scheme and how to get unique interface identifiers. The idea is to use a distinctive prefix, like a condensed organisation domain or email name, or a GPG fingerprint or even a UID, followed by the interface namespace hierarchy. These would be combined into a single identifier (valid C name). The intention is to get non-ambiguous names, yet avoiding the need of a central registry. + +__Conclusion__: accepted + +general Topics + + +Config System +------------- +'''cehteh''' landed the first version of this subsystem and asked for review and testing. Currently it's "work with no progress", but it is basically usable (you can get values, you can set values by environment variables, but you can't persist). It should be augmented on demand, e.g. by adding the support for more value types (value types are a hint how to parse, and link the parsed value to a given C type). + + + +Use of Namespaces +----------------- +Currently there is no clear uniform rule regarding namespaces use. *Joelholdsworth* places his code within lumiera::gui and below. He points out that external libs like Cairo, GTK, GLib will bring in their hierarchies and all of Lumiera should comply and put anything below a lumiera:: root. On the contrary, *Ichthyo* deliberately moved his implementation code away from the central lumiera:: interface hierarchy into shallow namespaces and avoids nesting. He argues that having all code below lumiera:: effectively makes this namespace global or forces it to be void of any function; rather he'd prefer to import every interface explicitly into the implementation namespace. *Cehteh* argues that having a global lumiera::, even if empty, would mark a general claim and stand for the uniformity of the project. Generally, there should be some correspondence between folders and namespaces. + +No conclusion yet, to be discussed further. + + + +Interface Definition Language +----------------------------- +In his work on the plugin loader, '''Cehteh''' created a first draft how to export an interface, and calls for review. An example can be found in http://www.lumiera.org/gitweb?p=lumiera/ct;a=blob;f=tests/backend/test-interfaces.c;h=fb1c4d30a34414767a313d24df60e679c96ad2a7;hb=7323114e77348995ccaf03417136aef7ee332776[tests/backend/test-interfaces.c] + +An interface is a list of "slots" mapping functions. The interface descriptor is itself realised as interface, an thus can be versioned, extended and specialised. By use of some glue code and macros we create a simple Interface Definition Language + + * after exporting a header with the C interface, including the types to be used... + * LUMIERA_INTERFACE_DECLARE creates an interface description (i.e. the official interface) + * implementing such an interface can be done in 3 major ways + * LUMIERA_INTERFACE_INSTANCE creates an instance on-the-fly (e.g. for descriptors) + * LUMIERA_EXPORT for defining a table of functions/interfaces to export from the core + * LUMIERA_PLUGIN for defining an interface table for functions located within a dynlib module (plugin) + * besides, with LUMIERA_INTERFACE_INLINE you can create a function on-the-fly while LUMIERA_INTERFACE_MAP maps onto an existing function directly + +The plugin loading system cares for mapping the given implementation function to the interface slots. Interfaces from the core need to be registered before they can be used, while for plugins this is done automatically on loading the module. The client code later just calls {{{interface_open()}}} and {{{interface_close()}}} where it doesn't matter anymore if the invoked function is in the core or loaded from an dynlib (plugin); loader, registry and/or plugin-DB shall manage it transparently. + +Version numbering starts with 1 and uses minor version numbers for compatible changes and major version numbers for everything that breaks existing asumptions. Version number zero is reserved for experimental work and per definition always the most recent version number. + +The system is designed to be very flexible and extensible, but this foundation really needs thorough review. + +Joelholdworth expresses some concern regarding the UIDs in octal notation used within the interface description. Cehteh explains they never need to be specified by client code. They are just distinctive IDs and provided for some special case within the plugin loader / serializer. He plans to provide a simple tool which automatically replaces a $LUIDGEN with such a bitstring. The octal notation was chosen, because it is the most compact albeit portable notation possible in C source code. + + +Conclusion +~~~~~~~~~~ + +looks good, agreement by all core devs. + + +Should be reviewed and investigated in detail to find any hidden problems. + + + +Next meeting +------------ + +There were some problems with the meeting schedule. Using the first week of month seems to be problematic. We'll try with second wednesday... + +The next meeting is scheduled for '''Wednesday Nov 12 2008 19:30 UTC''' at #lumiera diff --git a/doc/devel/meeting_summary/2008-12-10.txt b/doc/devel/meeting_summary/2008-12-10.txt new file mode 100644 index 000000000..29f8090d8 --- /dev/null +++ b/doc/devel/meeting_summary/2008-12-10.txt @@ -0,0 +1,72 @@ +2008-12-10 Lumiera Developers Meeting +===================================== +:Author: Teld +:Date: 2009-01-12 + +Dec 10, 2008 on #lumiera 20:15 - 00:45 UTC + + +__Participants__ + + * cehteh + * ichthyo + * joelholdsworth + * Teld + * tchy + * Divinespectrum + * raffa + * kirk + * Victorsigma + * pedro + * thorwil + + +The Lumiera Logo +---------------- + +The logo contest is a great success: http://www.pipapo.org/pipawiki/Lumiera/Logos[105 entries]. Everyone agrees that a preselection has to be made. Every attendee will have a look at the entries and selects 5 for the preselection list. Finally, 21 logos are selected. Together with a nice collage they are http://www.pipapo.org/pipawiki/Lumiera/Logos/Selection[presented] so that anyone online can vote for his/her favorite one. Voting will be possible from 20 december until 31 december at midnight. + + +Recurring Topics +---------------- +Discussion of open http://www.pipapo.org/pipawiki/Lumiera/DesignProcess[design process] drafts. + + +EDL vs Sequence +~~~~~~~~~~~~~~~ +There has been a long discussion on the ML about the term EDL. In the end almost agreed or was at least ok with the Sequence instead of EDL. The Sequence will be the building block and is a collection of media objects and a tree of tracks. A discussion about the UML showed in [wiki:self:../DesignProcess/TimelineSequenceOutput Project, Timeline(s), Sequence(s) and Output] and the relation of Timeline and Sequence follows. + +__Conclusion__: the name Sequence will be used instead of EDL + + +Interfaces between layers +~~~~~~~~~~~~~~~~~~~~~~~~~ +Ichtyo explains that the idea is that between layers only the C Language interfaces of the interface system are used. Wrappers have been made so that a convenient C++ interface is available. + + +Threads +~~~~~~~ +The UI will mostly run on a single thread, i.e. gtk_main will run in this GUI thread and when it terminates, the GUI is "done" and signals back that it is done. Cehteh points out that really hard working threads should be managed by the scheduler in the backend. All agree that we should avoid thread cancellation + + + +General Topics +-------------- + + +Video format +~~~~~~~~~~~~ +Kirk asks to what video format footage has to be converted so that it can be used in Lumiera. Ichtyo and cehteh point out that Lumiera will not be nailed down to one single format. A small number of formats that permit precise editing will be supported. Other formats may be available through plugins. The working horse is gmerlin, especially libgavl. Essentially Lumiera will try to figure out a conversion path in a lossless way. + + +Knowledge base +~~~~~~~~~~~~~~ +Kirk has already collected lots of information on video formats, conversion from one into the other, formats supported out of the box by Quicktime, Windows Media Player and so on. The developers agree that this is much needed. Kirk proposes to help in this area. + + + +Next meeting +------------ + +The next meeting will be at Wednesday 14 jan 21:00 UTC + + diff --git a/doc/devel/meeting_summary/2009-01-14.txt b/doc/devel/meeting_summary/2009-01-14.txt new file mode 100644 index 000000000..d7019d6a7 --- /dev/null +++ b/doc/devel/meeting_summary/2009-01-14.txt @@ -0,0 +1,122 @@ +2009-01-14 Lumiera Developers Meeting +===================================== +:Author: Raffaella_Traniello_and_Goibhniu +:Date: 2009-02-25 + +Jan 14, 2009 on #lumiera starting 20:00 UTC + + +__Participants__ + + * cehteh + * ichthyo + * joelholdsworth + * raffa + * goibhniu + * Teld + * nikola_ + * thorwil + * SimAV + * raevol + + +The Lumiera Logo - Workflow and GUI +----------------------------------- +Everyone is pretty happy with the results of the logo contest. The logo still needs some refinements, especially: + + * the font + * angle of view + * single logo or multiple logos based on a theme (dynamic logo) +Logo work discussion has started on Mailing list but it is dying out. + +Workflow and GUI discussion has already started on Mailing list. Keeping it effective and useful demands core devs time and energy. + + +Conclusion +~~~~~~~~~~ +Two groups are created, each of these have someone responsible to bring them forward. + +The group nudger is some director/moderator who keeps it going and nudges the people constantly, leading the discussion, not making the decisions alone. This 'supervisor' doesn't need to be someone to do the work of that group, but someone who can spend some efforts and time to coordinate it. + +Work discussion will proceed on the Mailing List but following the directions stated during the IRC meetings. + +Messages will have [ARTWORK] or [LOGO] or [WORKFLOW] in the subject. [[BR]] Groups can schedule IRC meetings separated from the monthly meetings that are meant for general organization. + + +Group 1 [WORKFLOW] +^^^^^^^^^^^^^^^^^^ + . director/supervisor: nikola_ (Nikola Duper) +Unlike other software where the GUI dictates the capabilities of the backend, Lumiera is being built from the bottom up to the GUI. So the foundations are built first to make the things accessible independently to the layout. + +The GUI layout is determined by the people who code it. This workflow group will be counsellor for the coding devs.[[BR]] The core dev responsible for the GUI is joelholdsworth (Joel Holdsworth).[[BR]] One of the group goals is to make the dev point of view dialogate with the editor point of view. + +The group studies how a professional editor uses a NLE, defines the key-features and the main qualities (rock solid, simple, support for the main video formats, usable,...), define the static mainstream functionality as opposed to optional experimental ideas and funny widgets, discuss implementability in Lumiera with the devs. + +It discuss about UI design, to ease the pro workflow, to improve usability for amateurs and discoverability for newcomers. [[BR]] It thinks about ergonomics. + + +Group 2 [ARTWORK] +^^^^^^^^^^^^^^^^^ + . director/supervisor: raffa (Raffaella Traniello) +The group discuss about logo, themes, the look and feel... + + +Getting the 'non-coders' community to help +------------------------------------------ +goibhniu proposes an issue tracker. + +The documentation needs love: it's currently horribly scattered all around. + +All the content needs to be converted to asciidoc and to the git repo at lumiera.org.[[BR]] Waiting for uWiki, the site will be in asciidoc+git [[BR]] The site needs a structure or other means to increase discoverability of all our information[[BR]] goibhniu proposes organizing a "sprint" to help get people involved and set a date for it (documentation migration/organisation) + + +Conclusion +~~~~~~~~~~ + * goibhniu volunteers to set up Trac. + * raevol volunteers to maintain it. + * The link:AsciiDoc[] Day idea will be discussed at the next meeting. + +Possible GUI-Integration: Display a frame? +------------------------------------------ +There was some discussion about the need for a component which does the timing and communicates with the scheduler and where it should sit in the stack. +For example: a render controler would be `for (frame i=0; i < end; ++i){pull_frame_without_timing_constraints(i);}` and a player would be the same with timing constraints and possibly lower quality. The GUI just has to remember that a play button is pushed, and that a certain instance of the play control is associated with a certain playback task in the backend. It was decided that this doesn't need to be resolved at the moment and requires further discussion and planning. It was felt that device dependnet things shouldn't be in the backend since it may one day run remotely as a frame server. It may need to sit between the gui and the backend. It was noted that the backend will run on constraints rather than ticks. For now the focus is on getting a single frame into the gui and there will be time to flesh out final interfaces later. + +The devs agreed to meet up on Saturday to get the latest version up and running. + +http://www.lumiera.org/gitweb?p=lumiera/ct;a=blob;f=src/common/guifacade.cpp;h=7f781845ad79b4a3e2cd820b96b87f5e702a80d3;hb=8e89506d289f3986af133493a40e3705b7919c87#l64[] +(The thread shall be forked from within the GUI code not from the facade) +This was discussed and ichthyo has already pushed a new cleaned up state and resolved most of the dependency problems with the liblumiera. It raised the question: do we prefer a UI facade to a GUI facade or do we want an extra script facade someday? + +It was discussed whether joelholdsworth should wait for cehteh to make a playback interface or use a dummy one for now. Cehteh wasn't ready to commit to a final interface yet but felt it was important to acknowledge the type of bidirectional interface that would be required. Icythyo has been using the link:GuiNotification[] as a dummy to implement an interface and get things working with loading the GUI plugin. Joelholdsworth already has displayer code which uses XVideo and falls back to GDK. It tells the buffer the address instead of being integrated with gavl, so cehteh will be able to hand it the address when ready. At the moment the buffers contain RGB (not gavl frames or anything). For the simple test joelholdsworth will need a buffer with RGB data and some metadata which cehteh and joelholdsworth planned to flesh out in the near future. + + +# Errors, Assertions, Checks, Dev- and Release-Builds +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A new Nobug is nearly ready with new documentation (cehteh). The error handler has been improved, as announced on the ML. Cehteh emphasised the importance of using nobug everywhere and not `#if DEBUG`: + +DEBUG is not a standard C/C++/buildsystem defined macro when it works its just coincidence but NDEBUG is standard. Neither does such code use nobug facilities nor respect nobug build levels. + +in new nobug you can do: + NOBUG_IF_ALPHA( + alpha code here + ) +it's the same for BETA RELEASE and there's a NOT variant for each but even then that should be avoided and higher level nobug facilities should be used instead. +(http://www.lumiera.org/nobug.html)[] + +beta and release builds shall be possible with nobug soon .. waiting for metta to integrate the new test.sh +test.sh with regex is awesome because you can test output which contains no exact things like addresses, version numbers, times +and running the testsuite under valgrind works properly now + + +Workflow/Interface +~~~~~~~~~~~~~~~~~~ +Ichthyo expressed the importance of not having dedicated kinds of tracks, but to be able to use any track for anything (audio, video etc.). This will require more work in the proc layer. Tracks are also not just layered but are trees which can fold. Any clip can contain N streams of various kinds. When a clip contains video and audio streams it still occupies just one track and the system automatically connects the audio to an audio bus etc. but the user is free to split off the audio as an independent clip and treat it separately from the video. The plan is to have various kinds of links between clips. This basic idea will need further refinement and consideration by the workflow team (Nikola et. al.) e.g. the user will need some way to direct the sound to a different bus even though it may be in a compound clip. By default all the video should be layered in the order of the tracks and a user should be able to say "don't mix the audio from track 4 and its child tracks directly to master but route it to the music subgroup". This is where the tree of tracks plays an important role because each track with its children forms a group. +There was further discussion on the general aims, objectives and focus of Lumiera and the reasoning behind them. Integration with control surfaces, jog wheels, sliders etc. should also be considered for the workflow. Nikola expressed the importance of stability and a simple and clean UI. + + + +Next meeting +------------ +Usually meetings are the second Wednesday each month on #lumiera at Freenode. The next meeting will be on Feb 11 - 20:00 UTC + + +Core dev's IRC meetings +~~~~~~~~~~~~~~~~~~~~~~~ +cehteh proposes a recurrent core dev meeting, more frequent than the monthly meeting since there is the a lot of stuff ahead that needs communication. + diff --git a/doc/devel/meeting_summary/2009-09-09.txt b/doc/devel/meeting_summary/2009-09-09.txt new file mode 100644 index 000000000..d48a75c14 --- /dev/null +++ b/doc/devel/meeting_summary/2009-09-09.txt @@ -0,0 +1,118 @@ +2009-09-09 Lumiera Developers Meeting +===================================== +Raffaella_Traniello_and_Hermann_Voßeler <prg@ichthyostega.de> + +Sep 9, 2009 on +#lumiera+ + +^20:00 - 22:30 UTC^ + +.Participants +- cehteh +- ichthyo +- joelholdsworth +- andrewjames +- daniieel +- hermanr +- liveD +- raffa +- SimAV + + + +Point 1: FrOSCon +---------------- + +Our participation to the conference was a success. We met other video +developers and people interested in Lumiera. +It has been a meeting opportunity for the community, it produced good and +reusable propaganda material (about 100 flyers distributed). +This success wouldn't be possible without a lot of people helping. + +So lets say: *Thank you all*! + +We plan to be there again next year with another Lumiera meeting (possibly +all the 3 core devs present). _Ichthyo_ consideres to do a dedicate talk +(in German). + +Participation as a group to other possible events has been considered for + +(in order of interest): +LAC, Piksel, OVC, Piksel Summer camp, Linuxtag, FSCONS. + +To improve visibility _liveD_ proposes to extend the project also on the +new social network like facebook. + +The proposal is discussed but doesn't find supporters. + + +Point 2: Funding +---------------- + +Preparing for FrOSCon had some costs, paid by the people working in +Lumiera. Ideally, _cehteh_ says, people working on Lumiera should not have +to pay for working on it, no matter who pays. +He proposes to make a project partnership with FFIS (a German non-profit +organisation for founding free software). +That means we could officially collect donations through FFIS (people +donate to FFIS, to free software in general or specifically to Lumiera). +That could work out well for specific scopes, like covering travel, +lodging and diet for developer gatherings or for exhibition material and +server. + +The proposal is accepted. _Cehteh_ will contact FFIS to propose them a partnership. +If later on there will be funding for development (not just covering expenses) +that would require a different approach and special communication with the +community. + +_daniieel_ proposes to put a price to each request in "buglist". + +The proposal is discussed and dismissed. + + +Point 3: Website +---------------- + +The news in the homepage makes the project look alive. But this is not +enough to communicate the state of the Lumiera development. Two +solutions are proposed: + +* The website needs to be reorganized and made accessible. andrewjames + volunteers to work on it. +* Quarterly development news will be published in the devel section of the + website. The devs will post drafts to the mailing list and encourage the + other devs to add their informations. Then someone shall aggregate this + and make a news webpage. That creates a new task: help collecting the + news and write them together. + +The devs will communicate their achievements more often in small steps +to the mailing list + +_raffa_ proposes monthly news on the homepage with more broad topics +(development, community, openvideo). + +The proposal gets no interest. + + +Point 4: Ticket Query clean-up +------------------------------ + +Trac allows for creation of custom ticket queries. We've accumulated quite some +custom reports, and _cehteh_ asks if some of them can be dropped (Ticket #225). +_Ichthyo_ points out that he uses some reports frequently, and considers some +others nice-to-have. Finally it turns out that only Report #5 isn't used by +anyone and can be dropped. + +_Cehteh_ points out that the http://issues.lumiera.org/tags[TracTags] macro +is a convenient feature -- for example it allows to tag tickets by required skill. + + +Point 5: Coding +--------------- + +_Cehteh_ finished a first stable NoBug release and intends to give uWiki a boost +in the next weeks. _Ichthyo_ worked on a command frontend usable at the GUI/Proc +interface; in the next time, there will be some integration work to be done +together with _Joelholdsworth_ to get the GUI and high-level model connected. + +A short discussion about the relevance of uWiki for Lumiera follows. Indeed, +it takes away some developer power, and _Joelholdsworth_ is concerned about that, +as the project is a bit underpowered generally. _Cehteh_ on the contrary considers +building a suitable (distributed) infrastructure as an important part of the +project. + + + diff --git a/doc/devel/meeting_summary/2011-03-09.txt b/doc/devel/meeting_summary/2011-03-09.txt new file mode 100644 index 000000000..0daf2303a --- /dev/null +++ b/doc/devel/meeting_summary/2011-03-09.txt @@ -0,0 +1,208 @@ +Lumiera Developers Meeting +========================== +:Author: Stefan Kangas +:Date: 2011-03-09 + +March 9, 2011 on +#lumiera+ + +^20:00 GMT - 23:30 GMT^ + +//Menu: label 2011-03-09 + +__Participants__ + +* Christian Thaeter (cehteh) +* Francesco Siddi (fsiddi) +* Hermann Voßeler (ichthyo) +* Odin Hørthe Omdal (Velmont) +* Raffaella Traniello (raffa) +* Stefan Kangas (skangas) + +The New Website +--------------- + +The new website is finally online. + +How do we proceed with the graphical layout? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + 21:37 <fsiddi> the template code is not ready yet + 21:37 <fsiddi> and there are some incorrect uses of tags in the current one + 21:37 <fsiddi> so i will go on coding a static html layout + 21:38 <fsiddi> then will submit it to your critique and then we can make a template out of it + ... + 21:50 <ichthyo> fsiddi: I liked the way you provided a slightly larger content area + for the tutorial part + +image:{img}/meetings/2011-03-09-mockup_dev_1.jpg["Website Proposal Mockup number 3", width=128, link="{img}/meetings/2011-03-09-mockup_dev_1.jpg"] + +image:{img}/meetings/2011-03-09-mockup_dev_2.jpg["Website Proposal Mockup number 2", width=128, link="{img}/meetings/2011-03-09-mockup_dev_2.jpg"] + +Discussion about the IFrame +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The only serious alternative to IFrames seems to be SSI, since we want to keep +generating pages with asciidoc, and not have to regenerate the entire website +just because one page changed. + +Conclusion was that we will stick with IFrames for now and change later when/if +that becomes a problem. + +Old stuff in the repository +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There is a folder /attic on the website. + +This information is supposed to be merged with the rest of the website, and then +deleted from its current location. This work is on-going and considered a +"background task". + +IRC Logs +-------- + +A discussion was brought up about how to handle IRC logs. + +For privacy reasons we do not want to save them routinely in public +locations. If we put them on the web they will most probably be kept by Google +until the sun burns out. + +*robots.txt* might be respected by Google or not. Surely some other search +engines will not respect it. Private logs or other logs not publically available +are fine though. + + 22:40 <cehteh> well ... IRC should *not* be for persistent documentation .. we shall + force/educate ourself to document things (and decisions) properly, irc + should be volatile + ... + 22:41 <ichthyo> in the past, sometimes we had very "contentful" dicussions + 22:41 <ichthyo> in that cases we just saved a transcript + ... + 22:44 <ichthyo> so personally I prefer to make transcripts of the *really* contentful + discussions and put them in the public documentation + 22:44 <ichthyo> "transcript" means cleaned up irc log + 22:44 <cehteh> yes + 22:45 <cehteh> so for meetings we should prolly do that + 22:47 <cehteh> not further edited but just leave all join/parts/noise and silly + comments out + 22:47 <cehteh> (and also rants and stuff) + 22:48 <cehteh> only conclusions and most important arguments shall stay + 22:48 <cehteh> maybe reorder it a bit as it happens that we talk about 2 things at the + same time + 22:48 <cehteh> but also not too much work on it + 22:49 <cehteh> and more importantly: document decisions and proposals in a more + offical way: that is 'rfc' + 22:49 <skangas_> Agreed. It is more important to preserve information in an accessible + way for the future than focusing on mundane details like + join/part/order/ranting etc. + 22:50 <skangas_> This means I personally always prefer shorter summaries of a + discussion to IRC logs, given that they do not leave anything + important out. + +VoIP discussions over Mumble? +----------------------------- + +There seems to be link:http://kitenet.net/~joey/blog/entry/12_hours_of_talking/[ongoing discussions]. on using VoIP more in Debian GNU/Linux. + +It was decided that while the idea is nice, it will not be used for developer +meetings. We might use it for developer discussions outside meetings if we feel +the need though. This will probably be more common as the number of developers +increase. + +Also, the point was brought up that GNU Emacs has really nice support for +several people sharing the same session. Everyone gets their own point, and with +some hacks it is even possible to color them differently. This needs to be done +on an untrusted remote server though -- Emacs can do anything and it is thus +highly insecure. + +Version numbering +----------------- + +Hermann has written an rfc for version numbers. + + 23:19 <cehteh> so in my words: we will have a usuall major.minor.patch for releases + 23:20 <cehteh> and major+1~develtag for devel snapshots + 23:20 <cehteh> where develtag is a timestamp + 23:20 <ichthyo> and we *could* make a stream of development versions + 23:21 <cehteh> YYYYMMDD should suffice + 23:21 <cehteh> or maybe just monotonic increading from 1 + 23:21 <ichthyo> well... optional suffix+ number + 23:21 <ichthyo> for the rare cases that you roll two releases a day + + ... + + 23:20 <ichthyo> and the third idea of my proposal: *not* use major.minor.patch before 1.0 + 23:24 <ichthyo> i.e. in the whole alpha... beta range + 23:24 <ichthyo> we just do 0.01 0.02 ... 0.99 + 23:25 <skangas_> Does it go without saying that minor versions can go on above 1.9.0 + to 1.10.0 etc? + 23:25 <ichthyo> yes, I didn't mention that, but thats important + 23:26 <ichthyo> its not nice, and usually you get a major first, but it can happen + 23:27 <ichthyo> so i'd say, we have discussed it now, we could comment on it and then + revisit it next time (and maybe decide on the proposal then) + + 23:27 <skangas_> Do we want a stable version and a development version? + 23:28 <skangas_> e.g. 1.0 is stable and 1.1 is unstable + 23:28 <skangas_> I am thinking about the scheme that wine and linux is using. + 23:28 <cehteh> i think linux won a lot with abadon this odd/even practice + 23:28 <ichthyo> regarding stable versions + 23:28 <ichthyo> personally, I don't like that idea + 23:29 <skangas_> cehteh: I did not know they had abandoned it. + 23:29 <cehteh> skangas: linux dont use that anymore + 23:29 <cehteh> since 2.6 years ago + 23:29 <ichthyo> because they tend to rot + 23:29 <skangas_> ichthyo: The stable versions? + 23:29 <cehteh> you just make a release and done .. everything after that is for the + next version + 23:29 <ichthyo> yes + 23:29 <cehteh> the only thing to discuss is how you count there + 23:30 <ichthyo> also, it is more in line with the "extreme programming" style + 23:30 <ichthyo> i.e. don't do too disruptive changes + 23:30 <cehteh> if you want 'pre' or 'rc' + 23:30 <ichthyo> but rather refactor often + 23:30 <skangas_> OK. + 23:31 <ichthyo> there is onre concern though + 23:31 <ichthyo> there *needs* to be some stability then + 23:31 <cehteh> releases get only bugfixes and *maybe* you can declare some release as + LTS ... but for lumiera i would think really hard about that and likely + deny that + 23:31 <ichthyo> because people are working with the software + 23:31 <ichthyo> but we want to have that with our compatibility scheme + 23:31 <cehteh> (our interface system should provide backward compatibility) + 23:31 <ichthyo> yes + 23:31 <ichthyo> but anyway, it *might* turn out that we fail with that promise + 23:32 <ichthyo> and then we'll have to reconsider a "stable" line + 23:32 <cehteh> so maintaining some LTS while having another development branch will + suck developer resources + 23:32 <ichthyo> yes + 23:32 <ichthyo> so I'd really try hard to avoid that + 23:33 <cehteh> well a stable line yes ... that is 'stable' until the next stable + release is there .. and then 'maintained' as in adding important + bugfixes for some (but not excessive) time + 23:33 <cehteh> we need to care for a stable for sure + 23:33 <ichthyo> indeed, but it shouldnt digress too much from the active development + 23:33 <cehteh> but we dont want some really old release to get permanent updates and + care because we declared it as LTS + 23:33 <skangas_> ichthyo: Is your RFC open to editing/additions? + 23:34 <ichthyo> yes + 23:34 <ichthyo> please go ahead, just add comments etc. + 23:34 <cehteh> use the rfc.sh comment function :) + 23:34 <skangas_> I was just thiking about writing this down in some way, that we + specifically do not want this. + + 23:23 <cehteh> also do we want 'rc' releases? + 23:23 <cehteh> or do we just declare certain devel snapshots as rc (i like the later) + 23:23 <ichthyo> yes, thats what I'd prefer too + 23:24 <cehteh> devel snapshots should be automatic buildable by builddrone + 23:25 <cehteh> I would like to make builddrone an automatic staging system + 23:25 <cehteh> add some 'rules' there .. + 23:26 <cehteh> if build and testsuite succeeds and test-install works and latests + commit doesnt contain WIP: then stage a devel snapshot + 23:27 <cehteh> (eventually or maybe we might even put tests/bugs there which prevent + certain staging actions, finally all integration and complete releases + could be automatized) + 23:26 <cehteh> remember in test.sh i reserved the 90-99* area for 'bugs' + 23:26 <cehteh> we may put some blocking tests there which prevent staging + +Next meeting +------------ + +The next meeting will be at Wednesday April 13 20:00 UTC. diff --git a/doc/devel/meeting_summary/index.txt b/doc/devel/meeting_summary/index.txt new file mode 100644 index 000000000..181ec605e --- /dev/null +++ b/doc/devel/meeting_summary/index.txt @@ -0,0 +1,285 @@ +Past Meetings +============= + +//Menu: sort children descending + +Not all meetings gets summarized on this page. For example, there are no meeting +summaries at all available for 2010. This only means that there were not enough +things that needed deciding/documenting during these meetings to warrant someone +spending time on the task of writing a summary. + +Anyone interested in Lumiera development is also encouraged to read mailing list +archives and other documentation. + + +9 Mar 2011 +---------- + +Topics +~~~~~~ + * The New Website + * IRC Logs + * VoIP discussions over Mumble? + * Version numbering + +Summary +^^^^^^^ + * link:2011-03-09.html[Summary (written by Stefan Kangas)] + + +.informal meetings, no summaries +******** +In 2010, the monthly meetings where rather informal gatherings, +without any relevant decisions beyond the organisation of conference activities. +During that time, usually the two remaining Lumiera core developers where alternatively +either prevented by real-life obligations, or otherwise just in ``deep coding mode'' +******** + + +9 Sep 2009 +---------- + +Topics +~~~~~~ + * FrOSCon + * Funding + * Website + * Ticket Query Clean-up + * Coding + +Summary +^^^^^^^ + * link:2009-09-09.html[Summary (written by raffa and Ichthyo)] + +14 Jan 2009 +----------- + +Topics +~~~~~~ + * organisational (protocol, agenda, leftovers?) + * Logo Contest results, elaborating the winner concept + * Forming a Workflow group + * Set up a Trac for issue tracking + * Getting the \'non-coders' community to help + * Possible GUI-Integration: Display a frame? + +Summary +^^^^^^^ + * link:2009-01-14.html[Summary (written by raffa)] + + + + +10 Dec 2008 +----------- + +Topics +~~~~~~ + * organisational (protocol, agenda, leftovers?) + * Logo Contest progress, discussion about how to do the pre selection + * rename "EDL" into "Sequence" + * link:DesignProcess/TimelineSequenceOutput[Project, Timeline(s), Sequence(s) and Output] + * GUI/Proc Interface and collaboration + * Threading questions regarding gtk-main and main() + +Summary +^^^^^^^ + * link:2008-12-10.html[Summary (written by Teld)] + + + + +12 Nov 2008 +----------- + +Topics +~~~~~~ + * organisational (protocol, agenda, leftovers?) + * Logo Contest progress + * uWiki Progress + * Builddrone, any helpers? + * Platform and lib dependency status + * Discussion about foreseeable problems with Gui Plugins + * Include dir and namespaces + * build system, plugin tree + +Summary +^^^^^^^ +_(missing)_ + + + +10 Oct 2008 +----------- + +Topics +~~~~~~ + * organisational (protocol, agenda...) + * Lumiera Logo + * Design Process: Go through Ideas and Drafts + - Mistakes to avoid + - Tag clouds + - Lumiera Architecture + - EDLs as meta-clips + - Builder + - Lua scripting language + - Time handling + - Interface naming + * Config System (1st version landed) + * Use of Namespaces + * Interface Definition Language + +Summary +^^^^^^^ + * link:2008-10-10.html[Summary (written by Ichthyo)] + + + + +4 Sep 2008 +---------- + +Topics +~~~~~~ + * boilerplate (protocol, agenda, ...) + * Plugin Descriptors + * Mailinglist, setup, administration + * Lumiera-work Mailinglist (for TODOs) + * About Website transition plan + +Summary +^^^^^^^ +_(missing)_ + + + + +7 Aug 2008 +---------- + +Topics +~~~~~~ + * Go through Ideas and Drafts in design process + * Config System (in the works) + * Scripting language plans + * GUI brainstorming page + +Summary +^^^^^^^ +_(missing)_ + + + + +6 Jul 2008 +---------- + +Topics +~~~~~~ + * boilerplate (protocol, what's left from last meeting) + * Introduction of Lumiera + * handling of config errors. + * scripting language. + * editing on small devices (eeePC) + * uWiki. + * 'musical' timeline in bars and beats + +Summary +^^^^^^^ + * link:2008-07-06.html[Summary (written by Teld)] + + + + +6 Jun 2008 +---------- + +Topics +~~~~~~ + * boilerplate (protocol, what's left from last meeting) + * Go through Ideas and Drafts in design process + - processing node interface + - Placement concept + +Summary +^^^^^^^ + * link:2008-06-06.html[Summary (written by ichthyo)] + + + + +8 May 2008 +---------- + +Topics +~~~~~~ + * boilerplate (protocol, what's left from last meeting) + * Go through Ideas and Drafts in design process + * Website infrastructure + +Summary +^^^^^^^ + * link:2008-05-08.html[Summary (written by cehteh)] + + + + +3 Apr 2008 +---------- + +Topics +~~~~~~ + * boilerplate (protocol, what's left from last meeting) + * Go through Ideas and Drafts in design process + * Call for volunteers (which tasks are to do, how can we interest people) for wiki/todo.html + * Project announcements/registration (freshmeat, gnu, etc...) any takers? + * Informal talk about the GUI (We have some people who want to contribute now, what's going on, how can they start) + * froscon application (linux multimedia booth?) + * timecode-metadata discussion: convert to arithmetic time type early or late? + +Summary +^^^^^^^ + * link:2008-04-03.html[Summary (written by sakalli)] + + + + +6 Mar 2008 +---------- + +Topics +~~~~~~ + * boilerplate (protocol, whats left from last meeting, next meeting) + * Go through Ideas and Drafts in design process + * "Call for design" for some features relevant for the whole Application: + - parameters and automation + - render node interface + - handling of compound sessions + - session storage interface in general + * Project name + * Project Server, setup, organization, administration + * GPL3 pros cons, license rationale + +Summary +^^^^^^^ + * link:2008-03-06.html[Summary (written by cehteh)] + + + + +1 Feb 2008 +---------- + +Topics +~~~~~~ + * Who will write the protocol below + * Discuss the open points in link:Lumiera/DesignProcess[] do we need this formalism? + * Who works on what, what are the short term goals, what tasks are open + * State of the new-name search for the was-intended-as-cinelerra-3 project (no name discussion, just how we will progress!) + * Introduce the development model + * How to handle the regular meetings in future (day, time, where to put summaries) + +Summary +^^^^^^^ + * link:2008-02-01.html[Summary (written by Ichthyo)] + From d3d7f0e45d169a3c362061eb1beba2c388721e72 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 16 Apr 2011 04:15:20 +0200 Subject: [PATCH 024/496] checkpoint: meeting summary shortened IRC log --- doc/devel/meeting_summary/2011-04-13.txt | 750 ++++++++++++++++++ doc/devel/meeting_summary/index.txt | 19 + .../YYYY-MM-DD-developerMeetingSummary.txt | 2 +- 3 files changed, 770 insertions(+), 1 deletion(-) create mode 100644 doc/devel/meeting_summary/2011-04-13.txt diff --git a/doc/devel/meeting_summary/2011-04-13.txt b/doc/devel/meeting_summary/2011-04-13.txt new file mode 100644 index 000000000..8ccd5dea7 --- /dev/null +++ b/doc/devel/meeting_summary/2011-04-13.txt @@ -0,0 +1,750 @@ +2011-04-13 Lumiera Developers Meeting +===================================== +:Author: Ichthyostega +:Date: 2011-4-14 + +April 13, 2011 on #lumiera 20:00 - 23:47 UTC + + +__Participants__ + + * cehteh + * ichthyo + * fsiddi + * skangas + +_Protocol written by Ichthyo_ + + +.organisational +- IRC meeting summaries will be moved into the main Git tree, below 'doc/devel' +- mostly shortened and tidied IRC logs, plus summary of conclusions \+ decisions +- ``winter quarterly coding news'': this time just paste _Ichthyo's_ contribution into the website +- the next ``coding news'' probably in May? + + +New Website Page Layout +----------------------- +Summary what is discussed + +_cehteh_ points out that... + +_joelholdsworth_ adds.... + + +Conclusion +~~~~~~~~~~ + + * do this + * do that + + + +Recurring Topics +---------------- +Discussion of open link:/documentation/devel/rfc.html[design process] drafts. + + +Prop1 +~~~~~ +link:/documentation/devel/rfc_pending/SomeProposal[descriptive name] +Summary what issues are discussed +..Details.. + +Conclusion:: drop it + + + +Next meeting +------------ + +The next meeting will be at Wednesday May 11, 20:00 UTC + + +'''' + + +.-- Discussion of details -- +[caption="☉Transcript☉ "] +---------------------------- +[2011-04-13 22:48:05] <fsiddi> there are still some layout issues +[2011-04-13 22:48:13] <fsiddi> i'm working on them +[2011-04-13 22:48:45] <cehteh> that comes with the point about upgrading the webserver ... +[2011-04-13 22:49:00] <cehteh> does newer asciidoc improve this soemhow already? +[2011-04-13 22:49:05] <fsiddi> no +[2011-04-13 22:49:13] <fsiddi> so there is no need on my side to upgrade +[2011-04-13 22:49:24] <cehteh> good to know +[2011-04-13 22:49:37] <cehteh> i had the impression it may make your life easier +[2011-04-13 22:50:02] <cehteh> for the nobug documentation upgrading fixed a lot of bugs +[2011-04-13 22:50:32] <fsiddi> anyway if you'll find the time to upgrade, it will be good maybe for other things +[2011-04-13 22:49:18] <cehteh> ok +... +[2011-04-13 22:51:42] <fsiddi> the vertical navigation template +[2011-04-13 22:51:53] <fsiddi> i read ichthyo notes +[2011-04-13 22:52:32] <fsiddi> and i'm not sure about this horizontal space concept +[2011-04-13 22:52:45] <fsiddi> could you clarify, please? +[2011-04-13 22:53:09] <ichthyo> todays, the screens can get pretty wide +[2011-04-13 22:53:24] <ichthyo> so there is a huge amount of horizontal space +[2011-04-13 22:53:39] <ichthyo> while most documents are rater organised vertically (for good reasons) +[2011-04-13 22:53:45] <cehteh> 23" 16:9 with 2048x1152 in front of me +[2011-04-13 22:56:19] <cehteh> http://www.spiegel.de/ looks already ugly on my 12" laptop by default +[2011-04-13 22:54:01] <ichthyo> e.g. if I enlarge my browser here to full screen +[2011-04-13 22:54:13] <ichthyo> the current layout just covers less then half the space +[2011-04-13 22:54:13] <cehteh> is it possibly to flow text in 2 columns on wide screens? +[2011-04-13 22:54:35] <ichthyo> cehteh: thats rather tricky and involved +[2011-04-13 22:54:41] <fsiddi> exactly +[2011-04-13 22:54:51] <ichthyo> guess that won't work without entering more java script coding +[2011-04-13 22:54:53] <fsiddi> it is possible, but very tough +[2011-04-13 22:55:01] <cehteh> well i dislike pages which dont use most of the screen and leave it empty +[2011-04-13 22:55:03] <fsiddi> CSS3 can do it almost on its own +[2011-04-13 22:55:22] <fsiddi> but it's not cross browser yet +.... +[2011-04-13 22:55:35] <ichthyo> so my idea was just to let us discuss how we could use that additional space, when its available +[2011-04-13 22:55:42] <cehteh> if the screen is wide enough they should make 'some' use of it .. maybe just using biggier fonts +[2011-04-13 22:55:43] <ichthyo> and if that is feasible +[2011-04-13 22:56:08] <ichthyo> I mean, just lets discuss open ended +[2011-04-13 22:56:25] <ichthyo> what possibilities do we see for that? +[2011-04-13 22:56:46] <ichthyo> using 2 columns would be one possibility, but that is tough and demading to get to work properly +[2011-04-13 22:57:02] <fsiddi> there are basically 2 ways +[2011-04-13 22:57:18] <fsiddi> 1 is to use liquid layout +[2011-04-13 22:57:49] <ichthyo> liquit layout means, that the content area just expands, right? +[2011-04-13 22:57:56] <fsiddi> like wikipedia yes +[2011-04-13 22:58:14] <cehteh> http://slashdot.org/ scales reasonable well with different screen sizes +[2011-04-13 22:58:52] <ichthyo> wikipedia is also an interesting example, because they use lots of floating block content +[2011-04-13 22:58:57] <ichthyo> tables, images, additional infos +[2011-04-13 22:59:20] <fsiddi> yes the mediawiki software is very powerful +[2011-04-13 22:59:38] <fsiddi> another *very* well done documentation is http://developer.apple.com/library/mac/navigation/ +[2011-04-13 22:59:52] <fsiddi> (from a design point of view) +[2011-04-13 22:59:54] <fsiddi> :) +[2011-04-13 23:00:01] * cehteh likes (or rather demands) that browser zoom (ctrl-+) works well on the lumiera page +... +[2011-04-13 23:05:14] <fsiddi> ok, so apart from that, the discussion is liquid vs not liquid +[2011-04-13 23:05:31] <ichthyo> and also what possibilities there are +[2011-04-13 23:05:43] <fsiddi> ichthyo: what do you mean? +[2011-04-13 23:05:56] <ichthyo> e.g. just expanding the content area +[2011-04-13 23:06:04] <ichthyo> or also using a script to increase the font +[2011-04-13 23:06:13] <ichthyo> or using floating blocks in the sidebar +[2011-04-13 23:06:38] <ichthyo> (asciidoc has several elements which could float into the sidebar) +[2011-04-13 23:07:09] <ichthyo> and if we know that the site layout supports that, then we'll likely make use of that + when writing documentation +[2011-04-13 23:07:10] <cehteh> when you have a page with a lot (many sideful) of content then a side menu + (also footer/header) must be floating or can be left out +[2011-04-13 23:07:31] <cehteh> there is no point in having a side-menu only visible for 3% of the content +[2011-04-13 23:07:45] <ichthyo> ah yes, thats another point, i.e. how to treat the vertical scrolling +[2011-04-13 23:08:07] <ichthyo> it is possible e.g. to have the scrollbars on the content area +[2011-04-13 23:08:08] <cehteh> imo we need 2 classes of pages .. small ones and huge ones +[2011-04-13 23:08:22] <cehteh> scrollbars inside are inconvinient +[2011-04-13 23:08:18] <fsiddi> http://wiki.blender.org/index.php/Doc:Manual +[2011-04-13 23:08:37] <fsiddi> it has scrolling sidebar +[2011-04-13 23:09:08] <cehteh> but with js again +[2011-04-13 23:09:19] <cehteh> i seen that as CSS only solution i think +[2011-04-13 23:09:37] <cehteh> otherwise yes, thats an option .. +[2011-04-13 23:10:02] <ichthyo> cehteh: yes, you can get that just with CSS +[2011-04-13 23:10:23] <fsiddi> actually you need js to trigger the class to make it floating + +[2011-04-13 23:10:27] <cehteh> back to my statement, do you agree that we need this 2 kinds of classes? +[2011-04-13 23:10:36] <fsiddi> i don't think so +[2011-04-13 23:10:42] * ichthyo neither +[2011-04-13 23:10:56] <fsiddi> cehteh, i think it will overcomplicate the backend +[2011-04-13 23:11:03] <ichthyo> because I guess 90% of the documentation pages will get into the "large, much content" category +[2011-04-13 23:11:05] <cehteh> really? +[2011-04-13 23:11:12] <cehteh> yes +[2011-04-13 23:11:19] <ichthyo> so we *do* have two templates already +[2011-04-13 23:11:22] <fsiddi> i think it's better to keep it flexible +[2011-04-13 23:11:26] <cehteh> and does this documentation need a sidebar? +[2011-04-13 23:11:26] <fsiddi> yes +[2011-04-13 23:11:35] <fsiddi> we use 2 templates +[2011-04-13 23:12:16] <ichthyo> one is the "top level", with the horizontal menu +[2011-04-13 23:12:24] <cehteh> this boils down to one for the general pages and one for documentation +[2011-04-13 23:12:25] <ichthyo> and the second one is the documentation template, right +[2011-04-13 23:12:44] <ichthyo> while those "general" pages are a handful +[2011-04-13 23:12:45] <fsiddi> yes +[2011-04-13 23:12:55] <cehteh> yes +[2011-04-13 23:12:56] <ichthyo> while the documentation pages will go into the hundred and more +[2011-04-13 23:14:08] <ichthyo> so I draw the conclusion, that the documentation pages are optimised for much content +[2011-04-13 23:14:19] <fsiddi> yes +[2011-04-13 23:13:30] <cehteh> ok .. settled +... +[2011-04-13 23:13:54] <fsiddi> i propose to keep the layout not liquid also in that pages +[2011-04-13 23:14:35] <fsiddi> if you prefer i can make them larger +[2011-04-13 23:14:49] <fsiddi> but for the moment i'd like to keep the same width +[2011-04-13 23:14:48] <ichthyo> what are the problems you see with a liquid layout? +[2011-04-13 23:15:08] <fsiddi> mostly a readability issue +[2011-04-13 23:15:29] <fsiddi> if a page is too large, it's unreadable +[2011-04-13 23:15:39] <ichthyo> well, I second that statement +[2011-04-13 23:15:53] <ichthyo> if a page goes over a certain amount of characters per line +[2011-04-13 23:16:10] <fsiddi> ichthyo: that's what i mean +[2011-04-13 23:15:53] <cehteh> can you scale the font on huge screens with css? +[2011-04-13 23:16:10] <ichthyo> cehteh: not in css2 +[2011-04-13 23:16:15] <cehteh> ok +[2011-04-13 23:16:24] <ichthyo> java script would work +[2011-04-13 23:16:42] <cehteh> i opt for a reasonable big max size as already proposed +[2011-04-13 23:16:47] <ichthyo> well, but if we manage to limit those number of characters per line +[2011-04-13 23:16:55] <fsiddi> i can implement that +[2011-04-13 23:16:58] <cehteh> but dont make this too small +[2011-04-13 23:17:05] <ichthyo> then liquid layout might be more reasonable +[2011-04-13 23:17:33] <fsiddi> ok + +[2011-04-13 23:17:36] <ichthyo> I am not 100% sure, but I recall having seen a layout, just based on CSS, + which achieved exactly that, +[2011-04-13 23:18:03] <ichthyo> i.e. limiting the maximum width, but allowing some liquid expansion below that +[2011-04-13 23:17:55] <fsiddi> i'm not sure that just CSS is possible +[2011-04-13 23:18:20] <ichthyo> If I recall right, it used several nested containers +[2011-04-13 23:18:17] <cehteh> wtf is liquid? +[2011-04-13 23:18:23] * cehteh is no web developer +[2011-04-13 23:18:27] <cehteh> free floating text? +[2011-04-13 23:18:32] <ichthyo> cehteh: liquid means, that the sizes adjust +[2011-04-13 23:18:55] <cehteh> boxes where the content is rendered? +[2011-04-13 23:18:36] <fsiddi> sorry guys +[2011-04-13 23:18:42] <fsiddi> let me clarify +[2011-04-13 23:18:51] <fsiddi> I thought you were familiar with the term +[2011-04-13 23:18:55] <ichthyo> I am +[2011-04-13 23:19:03] <cehteh> yes i am the web noob here +[2011-04-13 23:19:20] <ichthyo> ... while I did quite a lot in the past, but mostly web applications, shops and the like +[2011-04-13 23:19:37] <cehteh> cant you just give a max-width=200em for a container for example? +[2011-04-13 23:19:47] <fsiddi> yes +[2011-04-13 23:19:53] <fsiddi> it is possible +[2011-04-13 23:19:59] <ichthyo> and then you set an "overflow mode" +[2011-04-13 23:20:14] <cehteh> and whats overflow mode? +[2011-04-13 23:20:28] <ichthyo> overflow mode is: adjust, clip, scrollbars +[2011-04-13 23:20:17] <ichthyo> and if I recall correct, then the trick was to put a second container in that, with witdh 100% +[2011-04-13 23:20:54] <fsiddi> i think we can set this +[2011-04-13 23:21:03] <fsiddi> i'll investigate the possibilities we mentioned +[2011-04-13 23:21:08] <fsiddi> and make a report in 1 week +[2011-04-13 23:21:35] <ichthyo> fsiddi: that would be cool +[2011-04-13 23:21:52] <ichthyo> I'll too try to dig in my old notes, maybe I'll find the example I have in mind + +[2011-04-13 23:21:41] <fsiddi> now i'l like to mention the 2nd and final point +[2011-04-13 23:22:52] <fsiddi> my 2nd point is: navigation +[2011-04-13 23:23:33] <fsiddi> can somebody help me with reimplementing the original nav system +[2011-04-13 23:23:50] <cehteh> the menu? +[2011-04-13 23:23:56] <ichthyo> yes, of course +[2011-04-13 23:23:58] <fsiddi> that reads the url and opens up the tree at the right point +[2011-04-13 23:24:07] <fsiddi> that's pretty important +[2011-04-13 23:24:26] <fsiddi> after that works, it'll be just fixes in the layout +[2011-04-13 23:24:45] <ichthyo> of course I'll help, just I don't know the new menu system so well +[2011-04-13 23:24:54] <ichthyo> so we'll should just pair up on that +[2011-04-13 23:25:06] <fsiddi> cool +[2011-04-13 23:25:29] <ichthyo> maybe we should just set up a separate meeting here on IRC, where we can discuss that? +[2011-04-13 23:25:41] <ichthyo> (you and me, that is) +[2011-04-13 23:25:44] <fsiddi> so will you have time to work on it next week? +[2011-04-13 23:26:45] <fsiddi> i'll poke you after my report on the 1st point then + +[2011-04-13 23:28:23] <ichthyo> well I'd like to bring up the question regarding color +[2011-04-13 23:28:36] <ichthyo> and I'll ask especially you, fsiddi +[2011-04-13 23:28:44] <ichthyo> you know, colours are a matter of taste +[2011-04-13 23:29:15] <ichthyo> thus I'd say, as you did the general layout, you have an important say in that +[2011-04-13 23:28:59] <fsiddi> ah yes colors +[2011-04-13 23:29:11] <fsiddi> i try to keep it neutral +[2011-04-13 23:29:25] <ichthyo> you you would popose to stick to just gray shades? +[2011-04-13 23:29:34] <ichthyo> what is with the links? +[2011-04-13 23:29:39] <ichthyo> currently they are slate blue +[2011-04-13 23:29:45] <cehteh> thats ok for me +[2011-04-13 23:29:47] <fsiddi> i still have to work in them +[2011-04-13 23:29:55] <fsiddi> yes I did not color the links +[2011-04-13 23:30:13] <ichthyo> generally speaking, we should always try to limit the number of colours in a layout, IMHO +[2011-04-13 23:30:18] <cehteh> i dont think we shall use any fancy colors and maybe/if required then we add colors as markup +[2011-04-13 23:30:29] <ichthyo> but we could think of one or two "leading colours" +[2011-04-13 23:30:35] <fsiddi> i want to +[2011-04-13 23:30:43] <fsiddi> but did not put them in the css yet +[2011-04-13 23:30:53] <fsiddi> i'll do that soon +[2011-04-13 23:31:38] <fsiddi> so this is also for review soon :) +[2011-04-13 23:32:07] <cehteh> but colors are fixable and we dont have an official lumiera color (do we need one?) +[2011-04-13 23:32:22] <ichthyo> well... we could think about that +[2011-04-13 23:32:19] <fsiddi> i'll try to make up one +[2011-04-13 23:32:28] <cehteh> dunno :) +[2011-04-13 23:32:37] <ichthyo> according to my experience +[2011-04-13 23:32:47] <ichthyo> it helps a lot when you set a clear style guide early +[2011-04-13 23:33:41] <ichthyo> ok +[2011-04-13 23:33:51] <ichthyo> so what was the conclusion regarding the scrollbars? +[2011-04-13 23:34:19] <ichthyo> do we want scrollbars on the content area, or do we want the header, footer just to scroll away +[2011-04-13 23:34:33] <ichthyo> and do we want the navigation block fixed (relative to the screen) +[2011-04-13 23:34:42] <ichthyo> or let it scroll away too? +[2011-04-13 23:35:59] <cehteh> for general content, have it fixed, for documentation scroll it away? +[2011-04-13 23:36:10] <ichthyo> the simplest solution is just to leave evertything scroll away of course +[2011-04-13 23:36:27] <cehteh> documentation pages need to be able to navigate within this documentation +[2011-04-13 23:36:45] <cehteh> next/previous/top and maybe few related pages +[2011-04-13 23:36:58] <cehteh> and back to home/home of documentation +[2011-04-13 23:37:06] <cehteh> but not more i think +[2011-04-13 23:37:21] <ichthyo> thats the point, it can get cluttered +[2011-04-13 23:37:52] <cehteh> yes, leave only the minimal necessary things +[2011-04-13 23:38:08] <ichthyo> well... *if* we want to keep the navigation (vertical menu) fixed, there are some problems +[2011-04-13 23:38:12] <cehteh> at least the documentation should be readable on a small device, webpad, netbook even smartphone +[2011-04-13 23:38:46] <ichthyo> namely: what to do on unexpectedly small pages, and what to do when the menu tree itself + gets very large, so it doesn't fit on one page, even in half collapsed state, that is +[2011-04-13 23:39:29] <cehteh> you cant fix/address everything +[2011-04-13 23:39:42] <ichthyo> of course, but how to degrade then +[2011-04-13 23:39:49] <ichthyo> allow a scrollbar to appear? +[2011-04-13 23:39:51] <cehteh> there should be some safety marigin but otherwise just the browsers default fallbacks shall apply +[2011-04-13 23:40:45] <cehteh> in the worst case then the defaults are the best, the user is used how his device handles this +[2011-04-13 23:40:57] <ichthyo> good point +[2011-04-13 23:41:00] <fsiddi> right now the tree expands, scrollbars automatically appear +[2011-04-13 23:41:20] <ichthyo> ok +[2011-04-13 23:41:47] <fsiddi> and atm i would not consider portable devices for accessing the documentation +[2011-04-13 23:43:59] <fsiddi> anyway, i am about to leave for tonight +[2011-04-13 23:44:08] <ichthyo> ok +[2011-04-13 23:44:19] <ichthyo> I think we're through with the web page design questions for now +[2011-04-13 23:44:26] <fsiddi> good +[2011-04-13 23:44:33] <ichthyo> :) + +[2011-04-13 23:45:32] <skangas> Hi. +[2011-04-13 23:45:40] <ichthyo> Hello skangas ! +[2011-04-13 23:47:53] <skangas> I will actually just pop in to say hi this time. +[2011-04-13 23:48:18] <ichthyo> skangas: how's life? had a busy time? +[2011-04-13 23:48:24] <skangas> I have late nights and early mornings at the moment, so I need my sleep. ;-) +[2011-04-13 23:48:33] <ichthyo> ;-) +[2011-04-13 23:48:42] <skangas> ichthyo, Yeah, I am quite busy for the rest of this semester. +[2011-04-13 23:49:06] <skangas> I am hoping things will change once summer comes. They usually do. +[2011-04-13 23:49:42] <ichthyo> hopefully you've got interesting things to learn and program right now... +[2011-04-13 23:49:46] <skangas> And, I decided not to apply for GSoC, so I know there will be time. :-) +[2011-04-13 23:50:09] <cehteh> Lumiera Summer of Code :P +[2011-04-13 23:50:12] <skangas> Yeah, it is basically math and compilers currently. And even a bit of Prolog. +[2011-04-13 23:50:18] <ichthyo> LuSoC +[2011-04-13 23:50:19] <cehteh> cool :) +[2011-04-13 23:50:43] <ichthyo> heh, I really enjoyed that compiler building lections +[2011-04-13 23:50:46] <cehteh> http://dtai.cs.kuleuven.be/problog/index.html stomped on that recently .. would be fun to play with it +[2011-04-13 23:51:35] <cehteh> http://www.dcc.fc.up.pt/~vsc/Yap/clpbn/ is also cool .. unfortunally i think development stalled a bit +[2011-04-13 23:51:40] <skangas> cehteh: This looks like (from skimming) exactly like the mathematical models I have been playing around with all day in school. + +[2011-04-13 23:51:46] <cehteh> uhm ok lets go on with the metting +[2011-04-13 23:52:21] <ichthyo> two further topics, related: +[2011-04-13 23:52:25] <ichthyo> the "impressum" +[2011-04-13 23:52:27] <ichthyo> the license +[2011-04-13 23:52:46] <cehteh> ah yes, i seen you added serveral licenses .. +[2011-04-13 23:53:08] <cehteh> we should make more clear which license lumiera is under +[2011-04-13 23:53:25] <cehteh> only one 'license' page .. with gplv2 +[2011-04-13 23:53:50] <cehteh> and then 'other licenses' pages and explain where they are used +[2011-04-13 23:52:56] <ichthyo> for the impressum, as said +[2011-04-13 23:53:08] <ichthyo> I volunteer to put my name in there +[2011-04-13 23:53:25] <ichthyo> so we sort-of share the consequences +[2011-04-13 23:54 ] <cehteh> for the impressum .. fine if you do, if you want you can add me too +[2011-04-13 23:54 ] <cehteh> and we have to figure out where to place the impressum .. iirc it must be on the homepage +[2011-04-13 23:54 ] <cehteh> but it doesnt need to be in the menu +[2011-04-13 23:55 ] <cehteh> just a very tiny links in the footer is enough + + +[2011-04-13 23:54:14] <skangas> For the record, I agree with what ichthyo said in his first e-mail. +[2011-04-13 23:54:55] <skangas> That "dual licensing under GPL and something comparable" is the best choice. +[2011-04-13 23:55:03] <skangas> Probably CC-BY-SA. +[2011-04-13 23:55:10] <ichthyo> my thinking too +[2011-04-13 23:56:05] <ichthyo> that is, for the web content, and the documentation +[2011-04-13 23:57:00] <ichthyo> cehteh: would that be ok for you too? +[2011-04-13 23:59:29] <cehteh> [23:58] <cehteh> mhm? +[2011-04-13 23:59:31] <cehteh> .. any answers beyond that? +[2011-04-13 23:59:55] <ichthyo> seems we had a short split or so +[2011-04-14 00:00:07] <ichthyo> skangas and myself just talked about licenses +[2011-04-14 00:00:09] <cehteh> i got completely disconnected +[2011-04-14 00:00:41] <ichthyo> [23:57] <ichthyo> cehteh: would that be ok for you too? +[2011-04-14 00:01:29] <cehteh> CC-BY-SA means? +[2011-04-14 00:01:47] <ichthyo> Creative commons 3.0 attribution share alike +[2011-04-14 00:02:39] <ichthyo> cehteh: CC-BY-SA is considered "equivalent in spirit" with the GPL +[2011-04-14 00:01:50] <cehteh> copyleft .. attribution .. share-alike? +[2011-04-14 00:02:25] <cehteh> this dualcicensing would be ok for me .. but a note +[2011-04-14 00:02:48] <cehteh> how about putting the documentation under a non-commercial license? +[2011-04-14 00:03:03] <ichthyo> that would throw us out of debian main +[2011-04-14 00:03:13] <cehteh> that is no one can use the lumiera docs for release printed docs and make profit from it +[2011-04-14 00:03:14] <ichthyo> debian considers such a license "non-free" +[2011-04-14 00:03:49] <ichthyo> well, I am not so much concerened about that +[2011-04-14 00:04:03] <ichthyo> because, the docs still remain available +[2011-04-14 00:04:16] <ichthyo> and if someone really goes through all the pain of creating printed stuff +[2011-04-14 00:04:31] <ichthyo> and does it well, then he deserves to make profit from that, IMHO +[2011-04-14 00:04:25] <cehteh> but i dislike the idea that someone makes profit with no work and no giving back +[2011-04-14 00:04:48] <ichthyo> I am sure that *does require* substantial amount of work +[2011-04-14 00:05:03] <ichthyo> if that person wants any chance to sell something +[2011-04-14 00:05:23] <ichthyo> just print a crappy web dump won't make anyone sell much copies, IMHO +[2011-04-14 00:04:59] <cehteh> then its ok for me +[2011-04-14 00:05:33] <cehteh> but i've seen publisher which just took the free docs do *little* editing and formating + and sell it for a lot money +[2011-04-14 00:05:53] <ichthyo> and, will he make substantial turnover with that? +[2011-04-14 00:06:15] <skangas> Yeah, I think the point here is that poor quality high prices do not sell. +[2011-04-14 00:06:16] <cehteh> btw GPL only would prevent that too .. the publisher has to release his tex, + m$ -word or whatever sources then +[2011-04-14 00:06:36] <cehteh> does -sa prevent that too? +[2011-04-14 00:06:36] <ichthyo> making modifications available under the same conditions, yes. +[2011-04-14 00:06:46] <cehteh> besides, he needs to point out *within* that printed docs, that the license is free +[2011-04-14 00:07:44] <cehteh> but possibly some people get upset when they contribute a lot and someone else makes the profit +[2011-04-14 00:07:44] <skangas> I do not see how it would damage our goals. +[2011-04-14 00:08:06] <skangas> It would be more in the spirit of not allowing the free-loaders easy access. +[2011-04-14 00:08:20] <cehteh> well as i saied, that was just a note/idea .. i am fine with the dual-licensing +[2011-04-14 00:08:39] <ichthyo> well... here is the standard argument: if someone creates additional value on top of what + we provide, i.e. make a quality print, process orders, ship the copies, + then fine for me, if he/she makes turnover with that +[2011-04-14 00:09:01] <cehteh> yes +[2011-04-14 00:09:08] <skangas> I do not care much for that argument. +[2011-04-14 00:09:16] <skangas> For me, it is more about getting into Debian main. +[2011-04-14 00:09:59] <skangas> And the fact that I do not care if some marginal publisher makes a bit of money off + of the Lumiera documentation. +[2011-04-14 00:09:10] <ichthyo> the online docs will always be more acurate, more up-to-date +[2011-04-14 00:09:13] <skangas> Not even if a big one does it; that would only mean more attention to the project. +[2011-04-14 00:09:32] <cehteh> i would like this if we even can point to him and announce that as the offical lumiera book +[2011-04-14 00:10:10] <cehteh> but there are so much publishers which just try to make money without being really involved +[2011-04-14 00:10:31] <skangas> cehteh, Yes, even publishers which only print Wikipedia articles. +[2011-04-14 00:10:46] <ichthyo> and btw, what is more realistic, given that lumiera becomes a real, professional app, + then maybe someone will provide lectures and training + and that is much more apt to create a good turnover and benefit +[2011-04-14 00:11:14] <cehteh> ichthyo: lectures and training involves work .. thats ok +[2011-04-14 00:11:42] <cehteh> anyeays ... dual license gpl and cc-by-sa +[2011-04-14 00:11:46] <cehteh> ok for me +[2011-04-14 00:11:55] <ichthyo> fine, seems case is settled +[2011-04-14 00:12:04] <cehteh> eh which gpl ? gpl2+ .. same as lumiera +[2011-04-14 00:12:08] <ichthyo> yes +[2011-04-14 00:12:15] <ichthyo> thats important +[2011-04-14 00:12:32] <cehteh> and we may bump lumiera to gplv3 when we release it (and we know all its dependencies) +[2011-04-14 00:12:39] <ichthyo> so you can move code / doc in both directions without problems +[2011-04-14 00:12:42] <skangas> GPLv2+ not GPLv2, right? +[2011-04-14 00:12:48] <cehteh> skangas: yes +[2011-04-14 00:12:51] <skangas> OK. Great. +[2011-04-14 00:13:14] <cehteh> we only use gplv2 now because we dont want to rule out to use external libs which may be v2 only +[2011-04-14 00:13:25] <ichthyo> and a rather firm promise to bump it to gpl3 when this works and we're approaching a release +[2011-04-14 00:13:40] <cehteh> or gplv4 :P +[2011-04-14 00:13:47] <ichthyo> yay! +[2011-04-14 00:13:51] <cehteh> btw duke nukem is delayed +[2011-04-14 00:14:02] <ichthyo> ouch, I'm surprised + +[2011-04-14 00:14:41] <cehteh> ok lets summarise: +[2011-04-14 00:14:47] <cehteh> ichthyo: you add the impressum +[2011-04-14 00:14:51] <ichthyo> ok +[2011-04-14 00:15:00] <ichthyo> I clarify the actual licenses we use +[2011-04-14 00:15:07] <cehteh> yes +[2011-04-14 00:15:34] <cehteh> currently its not easily visible which license lumiera falls under +[2011-04-14 00:16:05] <ichthyo> well, it's in the first senctence, and even in bold font +[2011-04-14 00:16:10] <ichthyo> http://lumiera.org/project/legal/legal.html +[2011-04-14 00:16:40] <cehteh> yes but imo there should be only one License menu point, pointing to the gplv2 and our rationale document +[2011-04-14 00:16:56] <skangas> ichthyo, Error on that page Webiste -> Website +[2011-04-14 00:17:09] <cehteh> and then maybe other sub items exactly stating "other licenses" or "license for the documentation" +[2011-04-14 00:17:09] <skangas> I really need to sleep now... Good night! +[2011-04-14 00:17:09] <ichthyo> thanks, noted +[2011-04-14 00:17:45] <ichthyo> skangas: good night, sleep well! +[2011-04-14 00:18:03] <cehteh> if i click on license for some project i dont want to read much there should be just "this is licensed under foolicense" as first prominent sentence +[2011-04-14 00:18:08] <cehteh> n8 skangas +[2011-04-14 00:18:57] <ichthyo> ok +[2011-04-14 00:18:58] <cehteh> for me now when i seen the 'license' menu after you added it, it unfolded to a list of licenses .. +[2011-04-14 00:19:15] <cehteh> me alreadly thought "wtf" ... guess what some outsider will think :) +[2011-04-14 00:19:45] <ichthyo> :) + +[2011-04-14 00:20:03] <ichthyo> anyway, I think there are still some minor points left to discuss for this meeting +[2011-04-14 00:20:14] <cehteh> yes .. next one: +[2011-04-14 00:20:30] <cehteh> - Trac spam, solved, whats left to do (delete unused accounts) +[2011-04-14 00:21:00] <cehteh> you told me that its easily to delete the unused accounts .. + but from some i know that they are real users +[2011-04-14 00:21:20] <ichthyo> well.. it is easy to tell those apart +[2011-04-14 00:21:23] <cehteh> i tihnk we should notify this at least on the ml +[2011-04-14 00:21:32] <ichthyo> just need to improve the SQL a bit +[2011-04-14 00:21:55] <ichthyo> the trick is: those "old" inactive accounts are by definition older than + the spam accounts we delete +[2011-04-14 00:21:45] <cehteh> how about creating a category 'people' on trac +[2011-04-14 00:22:03] <cehteh> where everyone who is new is instructed to fill a first ticket .. +[2011-04-14 00:22:29] <cehteh> puts a bit burden on the people, not really a good idea +[2011-04-14 00:22:46] <cehteh> but anyways meanwhile there are a lot more spam accounts, we should regulary wipe them +[2011-04-14 00:22:58] <ichthyo> yes, so for now I'd just run that SQL once a month manually +[2011-04-14 00:23:34] <cehteh> prolly you should do that weekly :P +[2011-04-14 00:23:38] <ichthyo> after some months, if we see it works well always, we can do a little shell script + to issue that SQL +[2011-04-14 00:23:42] <cehteh> crontab ftw +[2011-04-14 00:23:50] <ichthyo> or so, weekly, no prob +[2011-04-14 00:24:02] <cehteh> you stay tuned and care for that? +[2011-04-14 00:24:17] <ichthyo> yes, for the next time, and sometime in summer we make a cronjob +[2011-04-14 00:25:28] <cehteh> eh just logged in. .. prolly 80% are spam meanwhile +[2011-04-14 00:25:32] <ichthyo> hehe + +[2011-04-14 00:25:59] <cehteh> ok next point: +[2011-04-14 00:26:07] <cehteh> - Webserver update to squeeze (new asciidoc, keep ichthyos hand +[2011-04-14 00:26:07] <cehteh> installed trac) +[2011-04-14 00:26:07] <cehteh> - Do we want to bump our 'reference' distribution to squeeze too? +[2011-04-14 00:26:19] <cehteh> ... webserver .. as soon as possible, but no urge +[2011-04-14 00:26:34] <cehteh> reference .. i just wanted to bring this up, imo there is no need +[2011-04-14 00:26:46] <ichthyo> personally, I will upgrade soon, next 2 weeks hopefully +[2011-04-14 00:27:13] <cehteh> yes i am on squeeze and even with backports already +[2011-04-14 00:27:29] <cehteh> so its prolly even better to have the reference on the devel server a bit behind +[2011-04-14 00:27:28] <ichthyo> I would propose to bump the "reference" the moment when we actually upgrade the + devserver + builddrone +[2011-04-14 00:27:51] <cehteh> well the devserver will be upgraded when we bump the reference +[2011-04-14 00:28:10] <cehteh> builddrone will be upgraded sometime next but thats not related to the reference +[2011-04-14 00:28:12] <ichthyo> but for now there is no problem also supporting lenny, but with the note that we'll + drop that support once we run into serious problems +[2011-04-14 00:28:22] <cehteh> yes +[2011-04-14 00:28:30] <cehteh> iirc that would be the reason to bump it +[2011-04-14 00:29:05] <ichthyo> well, IMHO, when we both are on squeeze, then effectively the reference is bumped :-P +[2011-04-14 00:29:40] <cehteh> nah .. the reference is about what builddrone reports to us too +[2011-04-14 00:29:15] <cehteh> i'd stay with lenny as long as we can so .. or maybe if the next stable gets froozen + then we can go to squeeze +[2011-04-14 00:29:51] <cehteh> and what skangas and other gui coders need also +[2011-04-14 00:30:15] <cehteh> i expect that gavl and gui dependencies will be a cause for a bump + +[2011-04-14 00:32:50] <cehteh> summarize: bump it someday .. as need arises? +[2011-04-14 00:33:26] <cehteh> or even better. .. no decision yet .. we'll see when its time + +[2011-04-14 00:33:40] <cehteh> ok next point: +[2011-04-14 00:34:57] <cehteh> - Go over pending RFC's (quick, not in detail this time) +[2011-04-14 00:35:11] <cehteh> should become regular on each meeting + +[2011-04-14 00:48:25] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/ApplicationInstall.html +[2011-04-14 00:48:40] <ichthyo> maybe only pick out some interestin ones or some which are quick to decide +[2011-04-14 00:49:06] <cehteh> well i want to go over all pending .. then we can put notes there "boring for the next meeting" +[2011-04-14 00:49:24] <cehteh> and next time we pcik only the interesting ones +[2011-04-14 00:49:33] <ichthyo> ok +[2011-04-14 00:49:42] <cehteh> for example this application install .. is boring .. you did a lot work, imo you can finalize it +[2011-04-14 00:50:04] <cehteh> (i dint read it in detail now) +[2011-04-14 00:50:25] <cehteh> maybe we want another state "accepted" .. +[2011-04-14 00:50:44] <cehteh> that is the interesting things which we know we will not drop but which are not finalized yet +[2011-04-14 00:53:02] <cehteh> adding that to rfc.sh would be trivial +[2011-04-14 00:53:25] <ichthyo> I think, the existing states are enough +[2011-04-14 00:53:26] <cehteh> yeah .. i think we dont need to 'finalize' and decide finally now +[2011-04-14 00:53:44] <ichthyo> either really discuss something and then decide, or just leave it in draft +[2011-04-14 00:53:50] <cehteh> well i just started alphabetically +[2011-04-14 00:54:05] <ichthyo> lets just postpone the application install and leave it in draft! +[2011-04-14 00:54:29] <cehteh> the application install came first... we need it, you did it well .. it could be 'finalized' or rather that would be some 'acceepted' candidate .. + +[2011-04-14 00:54:24] <ichthyo> Delectus? +[2011-04-14 00:54:40] <cehteh> delectus can be parked until someone else comes up with it +[2011-04-14 00:54:48] <cehteh> (btw i can do this right here and commit it) +[2011-04-14 00:54:51] <ichthyo> yes, so thats an decision, lets park it +[2011-04-14 00:54:57] <ichthyo> please do + +[2011-04-14 00:55:42] <ichthyo> question: do the parked onees also go into a different directory? +[2011-04-14 00:55:49] <ichthyo> I'm asking because of the menu +[2011-04-14 00:56:04] <cehteh> iirc not .. but i can do that +[2011-04-14 00:56:13] <cehteh> (adding to rfc.sh) +[2011-04-14 00:56:17] <cehteh> let me look +[2011-04-14 00:56:46] <cehteh> no ... i make a rfc_parked/ dir +[2011-04-14 00:57:05] <ichthyo> ok that would be nice +[2011-04-14 00:57:34] <cehteh> ok noted + +[2011-04-14 00:57:34] <ichthyo> next one +[2011-04-14 00:57:47] <ichthyo> http://lumiera.org/documentation/devel/rfc_pending/DesignParamAutomation.html +[2011-04-14 00:57:55] <cehteh> keep pending? +[2011-04-14 00:57:57] <ichthyo> well, its Idea, I have to expand on that +[2011-04-14 00:57:57] <ichthyo> yes +[2011-04-14 00:58:01] <ichthyo> please keep pending + +[2011-04-14 00:58:11] <cehteh> Design Process : Clip Cataloging System +[2011-04-14 00:58:16] <cehteh> park +[2011-04-14 00:58:20] <ichthyo> park + +[2011-04-14 00:58:31] <ichthyo> LumieraForwardIterator +[2011-04-14 00:58:37] <cehteh> Design Process: Lumiera Forward Iterator +[2011-04-14 00:58:39] <cehteh> pending + +[2011-04-14 00:58:48] <ichthyo> well, this is entirely an C++ topic +[2011-04-14 00:58:58] <ichthyo> I for my part vote for accepting it now +[2011-04-14 00:59:11] <ichthyo> I use this concept now since almost a year and it worked out well +[2011-04-14 00:59:25] <cehteh> yeah i think its more a trac ticket than a rfc +[2011-04-14 00:59:55] <ichthyo> well, it *is* something which would need discussion if there where more than one C++ developer +[2011-04-14 01:00:14] <cehteh> but if it works for you, i put it on 'maybe finalize' .. means i read through it and finalize it + when i have no objections +[2011-04-14 01:00:24] <ichthyo> yes, agreed + +[2011-04-14 01:00:45] <cehteh> Design the Render Nodes interface +[2011-04-14 01:01:04] <cehteh> thats definitely pending .. needs discussion +[2011-04-14 01:01:33] <ichthyo> yes +[2011-04-14 01:01:54] <cehteh> maybe we park it to get rid of it for now since this is months ahead? +[2011-04-14 01:02:28] <ichthyo> we could even drop it, but parking is ok +[2011-04-14 01:02:44] <ichthyo> that RfC basically sais: PLING PLING PLING, we need to discuss that +[2011-04-14 01:02:53] <cehteh> well, I wont drop it, its a nice place to document the intention about the design +[2011-04-14 01:03:10] <ichthyo> ok, so lets park it + +[2011-04-14 01:03:13] <cehteh> Developer Documentation Structure +[2011-04-14 01:03:23] <cehteh> i check that, bring it up to date and finalize it? +[2011-04-14 01:03:36] <ichthyo> i have some objections agains that, see my comment +[2011-04-14 01:04:00] <ichthyo> I think, the current structure is better than what that RfC proposes +[2011-04-14 01:04:05] <cehteh> yes .. thats what i meant with bring it up to date +[2011-04-14 01:04:39] <cehteh> i can keep it pending .. and then we can finalize it when you agree +[2011-04-14 01:04:49] <ichthyo> ok, so you will update it? +[2011-04-14 01:05:32] <cehteh> yes .. maybe not for next time but i put it on todo + +[2011-04-14 01:05:35] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/EngineInterfaceOverview.html +[2011-04-14 01:05:50] <cehteh> pending .. there is lot to do? +[2011-04-14 01:06:02] <ichthyo> sort of +[2011-04-14 01:06:10] <ichthyo> basically that is a high level outline +[2011-04-14 01:06:19] <cehteh> this is a rather big thing maybe we drop it in favor of smaller rfc's +[2011-04-14 01:06:23] <ichthyo> it doesn't contain details +[2011-04-14 01:06:27] <cehteh> but for now leave it pending +[2011-04-14 01:06:45] <ichthyo> at the time I wrote that, you said it looks okish for you +[2011-04-14 01:06:58] <cehteh> yes .. quite possible +[2011-04-14 01:06:55] <ichthyo> this one would be really important to discuss soon +[2011-04-14 01:07:11] <cehteh> i need to catch up first ..duh +[2011-04-14 01:07:23] <ichthyo> note: it doesnt go into details, just sets a very high level outline how the overall process works +[2011-04-14 01:07:39] <ichthyo> I think *that one* is really needed to be accepted/ reworked soon +[2011-04-14 01:07:43] <cehteh> yes .. lets talk next meeting about that (or some time else) +[2011-04-14 01:07:57] <ichthyo> ok +[2011-04-14 01:08:05] <cehteh> so pending for now + +[2011-04-14 01:08:11] <ichthyo> FeatureBundle +[2011-04-14 01:08:14] <ichthyo> park +[2011-04-14 01:08:26] <cehteh> park +[2011-04-14 01:08:26] <ichthyo> very important, but far future +[2011-04-14 01:08:33] <cehteh> yes + +[2011-04-14 01:08:39] <ichthyo> MarbleMode +[2011-04-14 01:08:51] <ichthyo> this is also a high level one +[2011-04-14 01:08:57] <ichthyo> I am much in favour of that +[2011-04-14 01:09:09] <ichthyo> but its really kind of conceptual +[2011-04-14 01:09:20] <cehteh> me too ... but its too early to finalize it or? +[2011-04-14 01:09:52] <cehteh> that would be a 'accepted' candidate too :) +[2011-04-14 01:09:55] <ichthyo> well, I would accept it... +[2011-04-14 01:10:23] <ichthyo> but I wrote it so you (and others) also think that over +[2011-04-14 01:10:27] <cehteh> i think 'final' should somethnig which wont be changed or discussed anymore unless we see problems +[2011-04-14 01:10:40] <cehteh> thats why i am thinking we may need an 'accepted' state +[2011-04-14 01:11:07] <cehteh> and this marble mode certainly needs a lot more discussion and work to be final +[2011-04-14 01:11:51] <ichthyo> mhm, but that RfC just proposes to go for that direction, not how to do all the details +[2011-04-14 01:11:58] <cehteh> or we just finalize it as 'concept' we want no matter how we implement it finally +[2011-04-14 01:12:05] <cehteh> yes ok then +[2011-04-14 01:12:10] <ichthyo> yes, ok then + +[2011-04-14 01:12:49] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/NormalizedDeviceCoordinates.html +[2011-04-14 01:12:54] <cehteh> very rough +[2011-04-14 01:13:17] <cehteh> makes a lot of sense .. but unfinished, pending or park? +[2011-04-14 01:13:37] <ichthyo> I'd say park +[2011-04-14 01:13:51] <ichthyo> (I am also much in favour of that one) +[2011-04-14 01:13:42] <cehteh> ok + +[2011-04-14 01:14:16] <ichthyo> ProcHighLevel +[2011-04-14 01:14:24] <cehteh> thats rather final now? +[2011-04-14 01:14:26] <ichthyo> my vote goes for accept +[2011-04-14 01:14:35] <cehteh> is it up to date? +[2011-04-14 01:14:41] <ichthyo> no significant addition since almost two years +[2011-04-14 01:14:46] <ichthyo> yes, its up to date +[2011-04-14 01:14:52] <cehteh> ok final +[2011-04-14 01:15:37] <ichthyo> and, btw, I know that you also supported many of those ideas + +[2011-04-14 01:15:39] <cehteh> placement .. +[2011-04-14 01:15:48] <ichthyo> I think same for that +[2011-04-14 01:15:57] <cehteh> up to date? +[2011-04-14 01:16:00] <ichthyo> if you don't have a problem with it, I vote for accept +[2011-04-14 01:16:28] <ichthyo> yes, as far as I can see, its up to date +[2011-04-14 01:16:32] <cehteh> yes for me the question is only if you need to refine some final things before accepting it +[2011-04-14 01:17:06] <cehteh> ok accept + +[2011-04-14 01:17:18] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/RenderOptimizer.html +[2011-04-14 01:17:19] <cehteh> park +[2011-04-14 01:17:32] <ichthyo> accept for me +[2011-04-14 01:17:56] <ichthyo> that is so much our common understanding meanwhile +[2011-04-14 01:18:02] <ichthyo> so, maybe just polish a bit +[2011-04-14 01:18:18] <cehteh> yes park because it needs some polishing before final +[2011-04-14 01:18:24] <ichthyo> (remove the "pro and con") and then we could accept it right away +[2011-04-14 01:18:28] <cehteh> i can put a note that its basically accepted +[2011-04-14 01:18:38] <cehteh> parking isnt neccessary bad :P + +[2011-04-14 01:18:46] <ichthyo> ResourceManagement +[2011-04-14 01:18:49] <cehteh> (well again that would be a 'accept' candidate) +[2011-04-14 01:18:53] <ichthyo> needs some more work +[2011-04-14 01:19:01] <cehteh> that are 2 things .. yes +[2011-04-14 01:19:10] <cehteh> profiling and budgeting .. +[2011-04-14 01:19:21] <cehteh> i think i will implement them and then see how it works +[2011-04-14 01:19:40] <cehteh> (unless someone else shows better alternatives) +[2011-04-14 01:19:47] <ichthyo> if you change that and e.g. remove the code sample and make it really short and high level, + then we could accept it as a concept; but the implementation details need certainly more work +[2011-04-14 01:20:17] <cehteh> hey that code is the code i lost with the hdd crash .. i am happy that i put it there +[2011-04-14 01:20:27] <cehteh> yes it was a very rough idea +[2011-04-14 01:20:37] <cehteh> and not on schedule to implemented soon +[2011-04-14 01:20:45] <cehteh> i just sketched some code down +[2011-04-14 01:21:04] <cehteh> so pending or park? +[2011-04-14 01:21:08] <ichthyo> as said, would it be ok for you to remove that sketch and just leave it on that level of the + first sentences, because then we could accept it right away +[2011-04-14 01:21:45] <cehteh> ok pending for now .. i dont want to work on this currently .. other things are more important +[2011-04-14 01:21:47] <ichthyo> we both pretty much agree that we *want* some kind of budget managing and resource usage + +[2011-04-14 01:22:41] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/Roadmap-first.html +[2011-04-14 01:22:45] <cehteh> final? +[2011-04-14 01:22:51] <ichthyo> oops! my fault +[2011-04-14 01:22:55] <cehteh> anything not up to date? +[2011-04-14 01:23:00] <ichthyo> that *was* accepted long ago +[2011-04-14 01:23:13] <cehteh> haha ok +[2011-04-14 01:23:47] <ichthyo> we discussed and accepted that 2009, judging from the comments + +[2011-04-14 01:23:49] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/StreamTypeSystem.html +[2011-04-14 01:24:06] <ichthyo> very important for me -- my vote is for accept +[2011-04-14 01:24:16] <cehteh> we had some discussion how to maintain metadata .. +[2011-04-14 01:24:40] <cehteh> i vote for accept too but this metadata (which may decribe the type) needs work +[2011-04-14 01:24:49] <ichthyo> well, this one is really a heavyweight conceptual RfC +[2011-04-14 01:25:00] <ichthyo> it is not about *how* to maintain metadata +[2011-04-14 01:25:05] <cehteh> yes +[2011-04-14 01:25:08] <cehteh> so accept +[2011-04-14 01:25:18] <ichthyo> but it really contains far reaching decisions +[2011-04-14 01:25:27] <ichthyo> and terminology +[2011-04-14 01:25:51] <ichthyo> please see, I would be glad if there was some discussion about that +[2011-04-14 01:26:36] <ichthyo> but well, at the moment I am the only one thinking into more details of this whole topic +[2011-04-14 01:26:24] <cehteh> shall i leave it pending just to nag us? +[2011-04-14 01:26:50] <ichthyo> yes, leave it pending +[2011-04-14 01:27:10] <cehteh> i was thinking too about this .. but not consistently ... +[2011-04-14 01:27:24] <ichthyo> as said, I vote for accept, but I would really ask you to think it over +[2011-04-14 01:27:38] <cehteh> i think that needs some time to settle to the right point [tm] +[2011-04-14 01:27:43] <ichthyo> I'm not right away implementing it, but the implementation is rather trivial +[2011-04-14 01:27:48] <ichthyo> so leave it pending + +[2011-04-14 01:28:00] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/ThreadsSignalsAndImportantManagementTasks.html +[2011-04-14 01:28:33] <cehteh> we need to work together to implement this on the main .. but generally i think this can be + accepted with some refinements +[2011-04-14 01:28:14] <ichthyo> some time ago, we had a short discussion about that +[2011-04-14 01:28:55] <ichthyo> The bottom line was: I am in favour of that, but I rather would like not to build it + directly into that main thread, but have a dedicated thread for it + and at that time you said, that would not be a fundamental problem +[2011-04-14 01:29:30] <cehteh> i wonder why not the main thread .. but its ok for me +[2011-04-14 01:29:53] <ichthyo> my argument is keeping the code simple and dedicated to one purpose +[2011-04-14 01:30:05] <cehteh> the main thread is a kindof 'service thread' right? +[2011-04-14 01:30:09] <ichthyo> the main thread's purpose is to wake up and shut down the system +[2011-04-14 01:30:25] <ichthyo> and I'd prefer to let him do only that and nothing else +[2011-04-14 01:30:26] <cehteh> i dont want the cinelerra fail .. opening a thread for every purpose +[2011-04-14 01:30:58] <ichthyo> generally yes, but for the more fine grained things we have the scheduler +[2011-04-14 01:31:08] <cehteh> well ok .. from my pov the main thread is more than just that but a general sheepheard +[2011-04-14 01:31:39] <cehteh> not only starting and shutdown but also control the direction +[2011-04-14 01:32:08] <cehteh> but really this isnt much a difference, if you want an extra thread then let it be so +[2011-04-14 01:32:46] <ichthyo> I see the code complexity. It would just look okish for me if we split it into these two +[2011-04-14 01:33:26] <cehteh> there is not much complexity, the main thread waits currently .. +[2011-04-14 01:34:02] <cehteh> and it can get woken by signals or condvar (convars return when a signal arrives) +[2011-04-14 01:34:29] <cehteh> actually having an extra thread wont make the code simpler because then the main thread + needs to be rigged to ignore the signals. All other threads are created by us and can + implicitly be rigged in this way +[2011-04-14 01:34:53] <ichthyo> main thread handles that already, if I recall correct +[2011-04-14 01:35:05] <ichthyo> because it loops on the condition and goes to sleep again +[2011-04-14 01:35:29] <cehteh> yes but you didnt add any sigmask handling / signal blocking right? +[2011-04-14 01:35:44] <ichthyo> ah I see, thats still todo +[2011-04-14 01:35:51] <cehteh> anyways .. i accept it .. implementation pending +[2011-04-14 01:37:38] <cehteh> signal handling becomes a 'subsystem' then ... :) +[2011-04-14 01:37:46] <ichthyo> yes, thats what I mean + +[2011-04-14 01:36:50] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/TimelineSequenceOutput.html +[2011-04-14 01:36:52] <cehteh> final? +[2011-04-14 01:37:00] <ichthyo> definitively final +[2011-04-14 01:38:08] <ichthyo> TimelineSequence: the key point is: we have multiple timelines +[2011-04-14 01:38:28] <ichthyo> and a sequence can be used in multiple timelines +[2011-04-14 01:38:36] <cehteh> yes .. ok for me i think +[2011-04-14 01:38:37] <ichthyo> I think we pretty much agree on that + +[2011-04-14 01:39:01] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/UseCases.html +[2011-04-14 01:39:08] <cehteh> nobody cares :P +[2011-04-14 01:39:20] <ichthyo> nobody cares +[2011-04-14 01:39:34] <cehteh> that means parked? or drop? +[2011-04-14 01:39:35] <ichthyo> park it, until we have someone working on the workflow + +[2011-04-14 01:40:01] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/VersionNumberScheme.html +[2011-04-14 01:40:10] <cehteh> accept (after you explained it to me .. ) +[2011-04-14 01:40:35] <ichthyo> :-D +[2011-04-14 01:40:14] <ichthyo> accept + +[2011-04-14 01:40:41] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/WebsiteNavigation.html +[2011-04-14 01:40:50] <cehteh> is that final? +[2011-04-14 01:40:55] <cehteh> (up to date) +[2011-04-14 01:41:15] <cehteh> do others need to discuss this .. fsiddi? +[2011-04-14 01:41:27] <ichthyo> there is one point: the tagging of pages +[2011-04-14 01:41:37] <ichthyo> if we remove that, the rest is implemented right now +[2011-04-14 01:42:08] <cehteh> leave it pending and when you meet with fsiidi next time you discuss and fix this? +[2011-04-14 01:42:18] <ichthyo> ok + +[2011-04-14 01:47:47] <ichthyo> so meeting is finished now, officially... +[2011-04-14 01:47:59] <cehteh> n8 :) +[2011-04-14 01:48:10] <cehteh> well i work a bit .. night owl mode :P +[2011-04-14 01:48:11] <ichthyo> next meeting on 11.5.2011 +[2011-04-14 01:48:42] <ichthyo> btw, I'm quite sure I skip LAC this time +[2011-04-14 01:48:52] <cehteh> me too +[2011-04-14 01:48:58] <ichthyo> just overall too much to do right now +[2011-04-14 01:49:15] <cehteh> no lumiera at lac +[2011-04-14 01:49:33] <ichthyo> but I'd be quite interested to come to that FSCONS conference skangas told us about +[2011-04-14 01:49:38] <ichthyo> in october or so +[2011-04-14 01:49:57] <cehteh> lets see .. time & money +[2011-04-14 01:51:12] <ichthyo> ok, going off now +[2011-04-14 01:51:15] * cehteh goes hunting some food +[2011-04-14 01:51:19] <ichthyo> (and hopefully going to bed soon) +[2011-04-14 01:51:21] <cehteh> see you +[2011-04-14 01:51:24] <ichthyo> see you! +---------------------------- + diff --git a/doc/devel/meeting_summary/index.txt b/doc/devel/meeting_summary/index.txt index 181ec605e..b8e3b42d5 100644 --- a/doc/devel/meeting_summary/index.txt +++ b/doc/devel/meeting_summary/index.txt @@ -12,6 +12,25 @@ Anyone interested in Lumiera development is also encouraged to read mailing list archives and other documentation. + + +13 Apr 2011 +----------- + +Topics +~~~~~~ + * boilerplate (protocol, what's left from last meeting) + * Go through Ideas and Drafts in design process + - processing node interface + - Placement concept + +Summary +^^^^^^^ + * link:2011-04-13.html[Summary (written by Hermann Voßeler)] + + + + 9 Mar 2011 ---------- diff --git a/doc/devel/template/YYYY-MM-DD-developerMeetingSummary.txt b/doc/devel/template/YYYY-MM-DD-developerMeetingSummary.txt index 64d72138f..28c0fafad 100644 --- a/doc/devel/template/YYYY-MM-DD-developerMeetingSummary.txt +++ b/doc/devel/template/YYYY-MM-DD-developerMeetingSummary.txt @@ -17,7 +17,7 @@ _Protocol written by XXXXX_ Recurring Topics ---------------- -Discussion of open http://www.pipapo.org/pipawiki/Lumiera/DesignProcess[design process] drafts. +Discussion of open link:/documentation/devel/rfc.html[design process] drafts. Prop1 From 7af4779b0e5eee258ebe6df21a1df0fa41be4e25 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 19 Apr 2011 05:33:55 +0200 Subject: [PATCH 025/496] Finish the meeting summary Further reduced the quoted transcript, summarise some discussions, add conclusions and subheadings --- doc/devel/meeting_summary/2011-04-13.txt | 612 ++++++++++------------- 1 file changed, 252 insertions(+), 360 deletions(-) diff --git a/doc/devel/meeting_summary/2011-04-13.txt b/doc/devel/meeting_summary/2011-04-13.txt index 8ccd5dea7..949b644c8 100644 --- a/doc/devel/meeting_summary/2011-04-13.txt +++ b/doc/devel/meeting_summary/2011-04-13.txt @@ -24,85 +24,29 @@ _Protocol written by Ichthyo_ New Website Page Layout ----------------------- -Summary what is discussed +_Francesco Siddi_ has augmented his Layout proposal and already built up two page templtes to +cover most of the layout needs of the Lumiera website and documentation resources. However, +some points with the code generated by Asciidoc turned out to be problematic. -_cehteh_ points out that... +In a link:http://permalink.gmane.org/gmane.comp.video.lumiera.general/2330[message preceeding the meeting], +today _ichthyo_ highlighted some general concerns which might need further discussion and maybe +a decision. -_joelholdsworth_ adds.... - - -Conclusion -~~~~~~~~~~ - - * do this - * do that - - - -Recurring Topics ----------------- -Discussion of open link:/documentation/devel/rfc.html[design process] drafts. - - -Prop1 -~~~~~ -link:/documentation/devel/rfc_pending/SomeProposal[descriptive name] -Summary what issues are discussed -..Details.. - -Conclusion:: drop it - - - -Next meeting ------------- - -The next meeting will be at Wednesday May 11, 20:00 UTC - - -'''' +utilising horizontal space +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Resolution of displays has been largely increased, especially on desktop computers, with +a tendency towards ``widescreen'' aspect ratio. While, on the other hand, text content +is still mostly vertically oriented. This leads to the question how to make use +of this additional space in horizontal direction, while still also supporting +the not-so large displays. .-- Discussion of details -- [caption="☉Transcript☉ "] ---------------------------- -[2011-04-13 22:48:05] <fsiddi> there are still some layout issues -[2011-04-13 22:48:13] <fsiddi> i'm working on them -[2011-04-13 22:48:45] <cehteh> that comes with the point about upgrading the webserver ... -[2011-04-13 22:49:00] <cehteh> does newer asciidoc improve this soemhow already? -[2011-04-13 22:49:05] <fsiddi> no -[2011-04-13 22:49:13] <fsiddi> so there is no need on my side to upgrade -[2011-04-13 22:49:24] <cehteh> good to know -[2011-04-13 22:49:37] <cehteh> i had the impression it may make your life easier -[2011-04-13 22:50:02] <cehteh> for the nobug documentation upgrading fixed a lot of bugs -[2011-04-13 22:50:32] <fsiddi> anyway if you'll find the time to upgrade, it will be good maybe for other things -[2011-04-13 22:49:18] <cehteh> ok -... -[2011-04-13 22:51:42] <fsiddi> the vertical navigation template -[2011-04-13 22:51:53] <fsiddi> i read ichthyo notes -[2011-04-13 22:52:32] <fsiddi> and i'm not sure about this horizontal space concept -[2011-04-13 22:52:45] <fsiddi> could you clarify, please? -[2011-04-13 22:53:09] <ichthyo> todays, the screens can get pretty wide -[2011-04-13 22:53:24] <ichthyo> so there is a huge amount of horizontal space -[2011-04-13 22:53:39] <ichthyo> while most documents are rater organised vertically (for good reasons) -[2011-04-13 22:53:45] <cehteh> 23" 16:9 with 2048x1152 in front of me -[2011-04-13 22:56:19] <cehteh> http://www.spiegel.de/ looks already ugly on my 12" laptop by default -[2011-04-13 22:54:01] <ichthyo> e.g. if I enlarge my browser here to full screen -[2011-04-13 22:54:13] <ichthyo> the current layout just covers less then half the space -[2011-04-13 22:54:13] <cehteh> is it possibly to flow text in 2 columns on wide screens? -[2011-04-13 22:54:35] <ichthyo> cehteh: thats rather tricky and involved -[2011-04-13 22:54:41] <fsiddi> exactly -[2011-04-13 22:54:51] <ichthyo> guess that won't work without entering more java script coding -[2011-04-13 22:54:53] <fsiddi> it is possible, but very tough -[2011-04-13 22:55:01] <cehteh> well i dislike pages which dont use most of the screen and leave it empty -[2011-04-13 22:55:03] <fsiddi> CSS3 can do it almost on its own -[2011-04-13 22:55:22] <fsiddi> but it's not cross browser yet -.... [2011-04-13 22:55:35] <ichthyo> so my idea was just to let us discuss how we could use that additional space, when its available +[2011-04-13 22:56:08] <ichthyo> I mean, just lets discuss open ended -- what possibilities do we see for that? [2011-04-13 22:55:42] <cehteh> if the screen is wide enough they should make 'some' use of it .. maybe just using biggier fonts -[2011-04-13 22:55:43] <ichthyo> and if that is feasible -[2011-04-13 22:56:08] <ichthyo> I mean, just lets discuss open ended -[2011-04-13 22:56:25] <ichthyo> what possibilities do we see for that? [2011-04-13 22:56:46] <ichthyo> using 2 columns would be one possibility, but that is tough and demading to get to work properly [2011-04-13 22:57:02] <fsiddi> there are basically 2 ways [2011-04-13 22:57:18] <fsiddi> 1 is to use liquid layout @@ -116,7 +60,15 @@ The next meeting will be at Wednesday May 11, 20:00 UTC [2011-04-13 22:59:52] <fsiddi> (from a design point of view) [2011-04-13 22:59:54] <fsiddi> :) [2011-04-13 23:00:01] * cehteh likes (or rather demands) that browser zoom (ctrl-+) works well on the lumiera page -... +--------------------- + + +using a »liquid layout«? +^^^^^^^^^^^^^^^^^^^^^^^^ + +.-- liquid <--> not liquid -- +[caption="☉Transcript☉ "] +---------------------------- [2011-04-13 23:05:14] <fsiddi> ok, so apart from that, the discussion is liquid vs not liquid [2011-04-13 23:05:31] <ichthyo> and also what possibilities there are [2011-04-13 23:05:43] <fsiddi> ichthyo: what do you mean? @@ -189,47 +141,29 @@ The next meeting will be at Wednesday May 11, 20:00 UTC [2011-04-13 23:18:03] <ichthyo> i.e. limiting the maximum width, but allowing some liquid expansion below that [2011-04-13 23:17:55] <fsiddi> i'm not sure that just CSS is possible [2011-04-13 23:18:20] <ichthyo> If I recall right, it used several nested containers -[2011-04-13 23:18:17] <cehteh> wtf is liquid? -[2011-04-13 23:18:23] * cehteh is no web developer -[2011-04-13 23:18:27] <cehteh> free floating text? -[2011-04-13 23:18:32] <ichthyo> cehteh: liquid means, that the sizes adjust -[2011-04-13 23:18:55] <cehteh> boxes where the content is rendered? -[2011-04-13 23:18:36] <fsiddi> sorry guys -[2011-04-13 23:18:42] <fsiddi> let me clarify -[2011-04-13 23:18:51] <fsiddi> I thought you were familiar with the term -[2011-04-13 23:18:55] <ichthyo> I am -[2011-04-13 23:19:03] <cehteh> yes i am the web noob here -[2011-04-13 23:19:20] <ichthyo> ... while I did quite a lot in the past, but mostly web applications, shops and the like -[2011-04-13 23:19:37] <cehteh> cant you just give a max-width=200em for a container for example? -[2011-04-13 23:19:47] <fsiddi> yes -[2011-04-13 23:19:53] <fsiddi> it is possible -[2011-04-13 23:19:59] <ichthyo> and then you set an "overflow mode" -[2011-04-13 23:20:14] <cehteh> and whats overflow mode? -[2011-04-13 23:20:28] <ichthyo> overflow mode is: adjust, clip, scrollbars -[2011-04-13 23:20:17] <ichthyo> and if I recall correct, then the trick was to put a second container in that, with witdh 100% -[2011-04-13 23:20:54] <fsiddi> i think we can set this -[2011-04-13 23:21:03] <fsiddi> i'll investigate the possibilities we mentioned -[2011-04-13 23:21:08] <fsiddi> and make a report in 1 week -[2011-04-13 23:21:35] <ichthyo> fsiddi: that would be cool -[2011-04-13 23:21:52] <ichthyo> I'll too try to dig in my old notes, maybe I'll find the example I have in mind +--------------------- -[2011-04-13 23:21:41] <fsiddi> now i'l like to mention the 2nd and final point -[2011-04-13 23:22:52] <fsiddi> my 2nd point is: navigation -[2011-04-13 23:23:33] <fsiddi> can somebody help me with reimplementing the original nav system -[2011-04-13 23:23:50] <cehteh> the menu? -[2011-04-13 23:23:56] <ichthyo> yes, of course -[2011-04-13 23:23:58] <fsiddi> that reads the url and opens up the tree at the right point -[2011-04-13 23:24:07] <fsiddi> that's pretty important -[2011-04-13 23:24:26] <fsiddi> after that works, it'll be just fixes in the layout -[2011-04-13 23:24:45] <ichthyo> of course I'll help, just I don't know the new menu system so well -[2011-04-13 23:24:54] <ichthyo> so we'll should just pair up on that -[2011-04-13 23:25:06] <fsiddi> cool -[2011-04-13 23:25:29] <ichthyo> maybe we should just set up a separate meeting here on IRC, where we can discuss that? -[2011-04-13 23:25:41] <ichthyo> (you and me, that is) -[2011-04-13 23:25:44] <fsiddi> so will you have time to work on it next week? -[2011-04-13 23:26:45] <fsiddi> i'll poke you after my report on the 1st point then -[2011-04-13 23:28:23] <ichthyo> well I'd like to bring up the question regarding color +Regarding the handling of *vertical scrolling*, the conclusion was to stick to the defaults +as much as possible. Especially, scrollbars should rather be left to the browser, not added +to the content area. We might consider to fix the navigation block relative to the browser window +though, if that is doable with too much complications. Moreover, this navigation block, holding +the (vertical) tree-like menu, should be made sufficiently large in vertical direction, but might +show scrollbars in case of overflow. + +Navigation menu +^^^^^^^^^^^^^^^ +we still need to find a way how to make the new +JQuery+ based navigation menu open and highlight +the _current_ page automatically. _fsiddi_ and _ichthyo_ will work out a solution in a separate meeting +on IRC next week. + +Colour scheme +^^^^^^^^^^^^^ + +.gray shades or using a colour scheme? +[caption="☉Transcript☉ "] +---------------------------- +[2011-04-13 23:28:23] <ichthyo> I'd like to bring up the question regarding color [2011-04-13 23:28:36] <ichthyo> and I'll ask especially you, fsiddi [2011-04-13 23:28:44] <ichthyo> you know, colours are a matter of taste [2011-04-13 23:29:15] <ichthyo> thus I'd say, as you did the general layout, you have an important say in that @@ -255,73 +189,52 @@ The next meeting will be at Wednesday May 11, 20:00 UTC [2011-04-13 23:32:37] <ichthyo> according to my experience [2011-04-13 23:32:47] <ichthyo> it helps a lot when you set a clear style guide early [2011-04-13 23:33:41] <ichthyo> ok -[2011-04-13 23:33:51] <ichthyo> so what was the conclusion regarding the scrollbars? -[2011-04-13 23:34:19] <ichthyo> do we want scrollbars on the content area, or do we want the header, footer just to scroll away -[2011-04-13 23:34:33] <ichthyo> and do we want the navigation block fixed (relative to the screen) -[2011-04-13 23:34:42] <ichthyo> or let it scroll away too? -[2011-04-13 23:35:59] <cehteh> for general content, have it fixed, for documentation scroll it away? -[2011-04-13 23:36:10] <ichthyo> the simplest solution is just to leave evertything scroll away of course -[2011-04-13 23:36:27] <cehteh> documentation pages need to be able to navigate within this documentation -[2011-04-13 23:36:45] <cehteh> next/previous/top and maybe few related pages -[2011-04-13 23:36:58] <cehteh> and back to home/home of documentation -[2011-04-13 23:37:06] <cehteh> but not more i think -[2011-04-13 23:37:21] <ichthyo> thats the point, it can get cluttered -[2011-04-13 23:37:52] <cehteh> yes, leave only the minimal necessary things -[2011-04-13 23:38:08] <ichthyo> well... *if* we want to keep the navigation (vertical menu) fixed, there are some problems -[2011-04-13 23:38:12] <cehteh> at least the documentation should be readable on a small device, webpad, netbook even smartphone -[2011-04-13 23:38:46] <ichthyo> namely: what to do on unexpectedly small pages, and what to do when the menu tree itself - gets very large, so it doesn't fit on one page, even in half collapsed state, that is -[2011-04-13 23:39:29] <cehteh> you cant fix/address everything -[2011-04-13 23:39:42] <ichthyo> of course, but how to degrade then -[2011-04-13 23:39:49] <ichthyo> allow a scrollbar to appear? -[2011-04-13 23:39:51] <cehteh> there should be some safety marigin but otherwise just the browsers default fallbacks shall apply -[2011-04-13 23:40:45] <cehteh> in the worst case then the defaults are the best, the user is used how his device handles this -[2011-04-13 23:40:57] <ichthyo> good point -[2011-04-13 23:41:00] <fsiddi> right now the tree expands, scrollbars automatically appear -[2011-04-13 23:41:20] <ichthyo> ok -[2011-04-13 23:41:47] <fsiddi> and atm i would not consider portable devices for accessing the documentation -[2011-04-13 23:43:59] <fsiddi> anyway, i am about to leave for tonight -[2011-04-13 23:44:08] <ichthyo> ok -[2011-04-13 23:44:19] <ichthyo> I think we're through with the web page design questions for now -[2011-04-13 23:44:26] <fsiddi> good -[2011-04-13 23:44:33] <ichthyo> :) - -[2011-04-13 23:45:32] <skangas> Hi. -[2011-04-13 23:45:40] <ichthyo> Hello skangas ! -[2011-04-13 23:47:53] <skangas> I will actually just pop in to say hi this time. -[2011-04-13 23:48:18] <ichthyo> skangas: how's life? had a busy time? -[2011-04-13 23:48:24] <skangas> I have late nights and early mornings at the moment, so I need my sleep. ;-) -[2011-04-13 23:48:33] <ichthyo> ;-) -[2011-04-13 23:48:42] <skangas> ichthyo, Yeah, I am quite busy for the rest of this semester. -[2011-04-13 23:49:06] <skangas> I am hoping things will change once summer comes. They usually do. -[2011-04-13 23:49:42] <ichthyo> hopefully you've got interesting things to learn and program right now... -[2011-04-13 23:49:46] <skangas> And, I decided not to apply for GSoC, so I know there will be time. :-) -[2011-04-13 23:50:09] <cehteh> Lumiera Summer of Code :P -[2011-04-13 23:50:12] <skangas> Yeah, it is basically math and compilers currently. And even a bit of Prolog. -[2011-04-13 23:50:18] <ichthyo> LuSoC -[2011-04-13 23:50:19] <cehteh> cool :) -[2011-04-13 23:50:43] <ichthyo> heh, I really enjoyed that compiler building lections -[2011-04-13 23:50:46] <cehteh> http://dtai.cs.kuleuven.be/problog/index.html stomped on that recently .. would be fun to play with it -[2011-04-13 23:51:35] <cehteh> http://www.dcc.fc.up.pt/~vsc/Yap/clpbn/ is also cool .. unfortunally i think development stalled a bit -[2011-04-13 23:51:40] <skangas> cehteh: This looks like (from skimming) exactly like the mathematical models I have been playing around with all day in school. - -[2011-04-13 23:51:46] <cehteh> uhm ok lets go on with the metting -[2011-04-13 23:52:21] <ichthyo> two further topics, related: -[2011-04-13 23:52:25] <ichthyo> the "impressum" -[2011-04-13 23:52:27] <ichthyo> the license -[2011-04-13 23:52:46] <cehteh> ah yes, i seen you added serveral licenses .. -[2011-04-13 23:53:08] <cehteh> we should make more clear which license lumiera is under -[2011-04-13 23:53:25] <cehteh> only one 'license' page .. with gplv2 -[2011-04-13 23:53:50] <cehteh> and then 'other licenses' pages and explain where they are used -[2011-04-13 23:52:56] <ichthyo> for the impressum, as said -[2011-04-13 23:53:08] <ichthyo> I volunteer to put my name in there -[2011-04-13 23:53:25] <ichthyo> so we sort-of share the consequences -[2011-04-13 23:54 ] <cehteh> for the impressum .. fine if you do, if you want you can add me too -[2011-04-13 23:54 ] <cehteh> and we have to figure out where to place the impressum .. iirc it must be on the homepage -[2011-04-13 23:54 ] <cehteh> but it doesnt need to be in the menu -[2011-04-13 23:55 ] <cehteh> just a very tiny links in the footer is enough +--------------------- + + +Webserver upgrade and the »reference distribution« +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.-- Server upgrade discussion -- +[caption="☉Transcript☉ "] +---------------------------- +[2011-04-14 00:25:59] <cehteh> ok next point: +[2011-04-14 00:26:07] <cehteh> - Webserver update to squeeze (new asciidoc, keep ichthyos hand +[2011-04-14 00:26:07] <cehteh> installed trac) +[2011-04-14 00:26:07] <cehteh> - Do we want to bump our 'reference' distribution to squeeze too? +[2011-04-14 00:26:19] <cehteh> ... webserver .. as soon as possible, but no urge +[2011-04-14 00:26:34] <cehteh> reference .. i just wanted to bring this up, imo there is no need +[2011-04-14 00:26:46] <ichthyo> personally, I will upgrade soon, next 2 weeks hopefully +[2011-04-14 00:27:13] <cehteh> yes i am on squeeze and even with backports already +[2011-04-14 00:27:29] <cehteh> so its prolly even better to have the reference on the devel server a bit behind +[2011-04-14 00:27:28] <ichthyo> I would propose to bump the "reference" the moment when we actually upgrade the + devserver + builddrone +[2011-04-14 00:27:51] <cehteh> well the devserver will be upgraded when we bump the reference +[2011-04-14 00:28:10] <cehteh> builddrone will be upgraded sometime next but thats not related to the reference +[2011-04-14 00:28:12] <ichthyo> but for now there is no problem also supporting lenny, but with the note that we'll + drop that support once we run into serious problems +[2011-04-14 00:28:22] <cehteh> yes +[2011-04-14 00:28:30] <cehteh> iirc that would be the reason to bump it +[2011-04-14 00:29:05] <ichthyo> well, IMHO, when we both are on squeeze, then effectively the reference is bumped :-P +[2011-04-14 00:29:40] <cehteh> nah .. the reference is about what builddrone reports to us too +[2011-04-14 00:29:15] <cehteh> i'd stay with lenny as long as we can so .. or maybe if the next stable gets froozen + then we can go to squeeze +[2011-04-14 00:29:51] <cehteh> and what skangas and other gui coders need also +[2011-04-14 00:30:15] <cehteh> i expect that gavl and gui dependencies will be a cause for a bump + +[2011-04-14 00:32:50] <cehteh> summarize: bump it someday .. as need arises? +[2011-04-14 00:33:26] <cehteh> or even better. .. no decision yet .. we'll see when its time +--------------------- + + +Website licensing and legal questions +------------------------------------- + +.-- Discussion regarding the Web content license -- +[caption="☉Transcript☉ "] +---------------------------- [2011-04-13 23:54:14] <skangas> For the record, I agree with what ichthyo said in his first e-mail. [2011-04-13 23:54:55] <skangas> That "dual licensing under GPL and something comparable" is the best choice. [2011-04-13 23:55:03] <skangas> Probably CC-BY-SA. @@ -399,131 +312,84 @@ The next meeting will be at Wednesday May 11, 20:00 UTC [2011-04-14 00:13:47] <ichthyo> yay! [2011-04-14 00:13:51] <cehteh> btw duke nukem is delayed [2011-04-14 00:14:02] <ichthyo> ouch, I'm surprised +--------------------- -[2011-04-14 00:14:41] <cehteh> ok lets summarise: -[2011-04-14 00:14:47] <cehteh> ichthyo: you add the impressum -[2011-04-14 00:14:51] <ichthyo> ok -[2011-04-14 00:15:00] <ichthyo> I clarify the actual licenses we use -[2011-04-14 00:15:07] <cehteh> yes -[2011-04-14 00:15:34] <cehteh> currently its not easily visible which license lumiera falls under -[2011-04-14 00:16:05] <ichthyo> well, it's in the first senctence, and even in bold font -[2011-04-14 00:16:10] <ichthyo> http://lumiera.org/project/legal/legal.html -[2011-04-14 00:16:40] <cehteh> yes but imo there should be only one License menu point, pointing to the gplv2 and our rationale document -[2011-04-14 00:16:56] <skangas> ichthyo, Error on that page Webiste -> Website -[2011-04-14 00:17:09] <cehteh> and then maybe other sub items exactly stating "other licenses" or "license for the documentation" -[2011-04-14 00:17:09] <skangas> I really need to sleep now... Good night! -[2011-04-14 00:17:09] <ichthyo> thanks, noted -[2011-04-14 00:17:45] <ichthyo> skangas: good night, sleep well! -[2011-04-14 00:18:03] <cehteh> if i click on license for some project i dont want to read much there should be just "this is licensed under foolicense" as first prominent sentence -[2011-04-14 00:18:08] <cehteh> n8 skangas -[2011-04-14 00:18:57] <ichthyo> ok -[2011-04-14 00:18:58] <cehteh> for me now when i seen the 'license' menu after you added it, it unfolded to a list of licenses .. -[2011-04-14 00:19:15] <cehteh> me alreadly thought "wtf" ... guess what some outsider will think :) -[2011-04-14 00:19:45] <ichthyo> :) +Conclusion +~~~~~~~~~~ -[2011-04-14 00:20:03] <ichthyo> anyway, I think there are still some minor points left to discuss for this meeting -[2011-04-14 00:20:14] <cehteh> yes .. next one: -[2011-04-14 00:20:30] <cehteh> - Trac spam, solved, whats left to do (delete unused accounts) -[2011-04-14 00:21:00] <cehteh> you told me that its easily to delete the unused accounts .. - but from some i know that they are real users -[2011-04-14 00:21:20] <ichthyo> well.. it is easy to tell those apart -[2011-04-14 00:21:23] <cehteh> i tihnk we should notify this at least on the ml -[2011-04-14 00:21:32] <ichthyo> just need to improve the SQL a bit -[2011-04-14 00:21:55] <ichthyo> the trick is: those "old" inactive accounts are by definition older than - the spam accounts we delete -[2011-04-14 00:21:45] <cehteh> how about creating a category 'people' on trac -[2011-04-14 00:22:03] <cehteh> where everyone who is new is instructed to fill a first ticket .. -[2011-04-14 00:22:29] <cehteh> puts a bit burden on the people, not really a good idea -[2011-04-14 00:22:46] <cehteh> but anyways meanwhile there are a lot more spam accounts, we should regulary wipe them -[2011-04-14 00:22:58] <ichthyo> yes, so for now I'd just run that SQL once a month manually -[2011-04-14 00:23:34] <cehteh> prolly you should do that weekly :P -[2011-04-14 00:23:38] <ichthyo> after some months, if we see it works well always, we can do a little shell script - to issue that SQL -[2011-04-14 00:23:42] <cehteh> crontab ftw -[2011-04-14 00:23:50] <ichthyo> or so, weekly, no prob -[2011-04-14 00:24:02] <cehteh> you stay tuned and care for that? -[2011-04-14 00:24:17] <ichthyo> yes, for the next time, and sometime in summer we make a cronjob -[2011-04-14 00:25:28] <cehteh> eh just logged in. .. prolly 80% are spam meanwhile -[2011-04-14 00:25:32] <ichthyo> hehe + * Website content and documentation will be dual-licensed GPL 2+ and CC-3.0-BY-SA + * _Ichthyo_ will clarify the actual licenses used on the ``license'' page + * moreover he'll care to add an _Impressum_ -- as required by german law -[2011-04-14 00:25:59] <cehteh> ok next point: -[2011-04-14 00:26:07] <cehteh> - Webserver update to squeeze (new asciidoc, keep ichthyos hand -[2011-04-14 00:26:07] <cehteh> installed trac) -[2011-04-14 00:26:07] <cehteh> - Do we want to bump our 'reference' distribution to squeeze too? -[2011-04-14 00:26:19] <cehteh> ... webserver .. as soon as possible, but no urge -[2011-04-14 00:26:34] <cehteh> reference .. i just wanted to bring this up, imo there is no need -[2011-04-14 00:26:46] <ichthyo> personally, I will upgrade soon, next 2 weeks hopefully -[2011-04-14 00:27:13] <cehteh> yes i am on squeeze and even with backports already -[2011-04-14 00:27:29] <cehteh> so its prolly even better to have the reference on the devel server a bit behind -[2011-04-14 00:27:28] <ichthyo> I would propose to bump the "reference" the moment when we actually upgrade the - devserver + builddrone -[2011-04-14 00:27:51] <cehteh> well the devserver will be upgraded when we bump the reference -[2011-04-14 00:28:10] <cehteh> builddrone will be upgraded sometime next but thats not related to the reference -[2011-04-14 00:28:12] <ichthyo> but for now there is no problem also supporting lenny, but with the note that we'll - drop that support once we run into serious problems -[2011-04-14 00:28:22] <cehteh> yes -[2011-04-14 00:28:30] <cehteh> iirc that would be the reason to bump it -[2011-04-14 00:29:05] <ichthyo> well, IMHO, when we both are on squeeze, then effectively the reference is bumped :-P -[2011-04-14 00:29:40] <cehteh> nah .. the reference is about what builddrone reports to us too -[2011-04-14 00:29:15] <cehteh> i'd stay with lenny as long as we can so .. or maybe if the next stable gets froozen - then we can go to squeeze -[2011-04-14 00:29:51] <cehteh> and what skangas and other gui coders need also -[2011-04-14 00:30:15] <cehteh> i expect that gavl and gui dependencies will be a cause for a bump -[2011-04-14 00:32:50] <cehteh> summarize: bump it someday .. as need arises? -[2011-04-14 00:33:26] <cehteh> or even better. .. no decision yet .. we'll see when its time -[2011-04-14 00:33:40] <cehteh> ok next point: -[2011-04-14 00:34:57] <cehteh> - Go over pending RFC's (quick, not in detail this time) -[2011-04-14 00:35:11] <cehteh> should become regular on each meeting + +Trac spam +--------- +After an increasing amount of Spam tickets in the link:http://issues.lumiera.org[Trac], +_Ichthyo_ installed the Trac antispam plugin (which required an upgrade to Trac 0.12, +actually repackaging a current version into an pre-release debian package, as the +official debian package isn't on the required level). After a bit of training, +the Bayesian filter successfully blocked any further spam tickets. + +The remaining problem are spam user accounts though. To deal with that problem, _Ichthyo_ +designed a custom SQL query based on some obvious heuristics, which seems to pinpoint +all spurious accounts. We'll try to execute that SQL for some time manually, and -- +in case it behaves sane -- automate that cleanup as a cronjob in some months. + +_Cehteh_ points out that this new policy should at least be anounced on the Mailinglist. + + + +Recurring Topic: Design process entries +--------------------------------------- +Discussion of open link:/documentation/devel/rfc.html[design process] drafts. + +Since some time, no further discussion happened regarding the currently _pending_ +RfC entries. Agreement is that we should again return to the former routine and +revisit the relevant design process entries in each developer meeting. + + +.-- the Application Install proposal -- +[caption="☉Transcript☉ "] +---------------------------- [2011-04-14 00:48:25] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/ApplicationInstall.html [2011-04-14 00:48:40] <ichthyo> maybe only pick out some interestin ones or some which are quick to decide [2011-04-14 00:49:06] <cehteh> well i want to go over all pending .. then we can put notes there "boring for the next meeting" -[2011-04-14 00:49:24] <cehteh> and next time we pcik only the interesting ones -[2011-04-14 00:49:33] <ichthyo> ok [2011-04-14 00:49:42] <cehteh> for example this application install .. is boring .. you did a lot work, imo you can finalize it [2011-04-14 00:50:04] <cehteh> (i dint read it in detail now) [2011-04-14 00:50:25] <cehteh> maybe we want another state "accepted" .. [2011-04-14 00:50:44] <cehteh> that is the interesting things which we know we will not drop but which are not finalized yet -[2011-04-14 00:53:02] <cehteh> adding that to rfc.sh would be trivial +[2011-04-14 00:54:29] <cehteh> the application install came first... we need it, you did it well .. it could be 'finalized' or + rather that would be some 'acceepted' candidate .. [2011-04-14 00:53:25] <ichthyo> I think, the existing states are enough -[2011-04-14 00:53:26] <cehteh> yeah .. i think we dont need to 'finalize' and decide finally now [2011-04-14 00:53:44] <ichthyo> either really discuss something and then decide, or just leave it in draft -[2011-04-14 00:53:50] <cehteh> well i just started alphabetically [2011-04-14 00:54:05] <ichthyo> lets just postpone the application install and leave it in draft! -[2011-04-14 00:54:29] <cehteh> the application install came first... we need it, you did it well .. it could be 'finalized' or rather that would be some 'acceepted' candidate .. +--------------------- -[2011-04-14 00:54:24] <ichthyo> Delectus? -[2011-04-14 00:54:40] <cehteh> delectus can be parked until someone else comes up with it -[2011-04-14 00:54:48] <cehteh> (btw i can do this right here and commit it) -[2011-04-14 00:54:51] <ichthyo> yes, so thats an decision, lets park it -[2011-04-14 00:54:57] <ichthyo> please do -[2011-04-14 00:55:42] <ichthyo> question: do the parked onees also go into a different directory? -[2011-04-14 00:55:49] <ichthyo> I'm asking because of the menu -[2011-04-14 00:56:04] <cehteh> iirc not .. but i can do that -[2011-04-14 00:56:13] <cehteh> (adding to rfc.sh) -[2011-04-14 00:56:17] <cehteh> let me look -[2011-04-14 00:56:46] <cehteh> no ... i make a rfc_parked/ dir -[2011-04-14 00:57:05] <ichthyo> ok that would be nice -[2011-04-14 00:57:34] <cehteh> ok noted -[2011-04-14 00:57:34] <ichthyo> next one -[2011-04-14 00:57:47] <ichthyo> http://lumiera.org/documentation/devel/rfc_pending/DesignParamAutomation.html -[2011-04-14 00:57:55] <cehteh> keep pending? -[2011-04-14 00:57:57] <ichthyo> well, its Idea, I have to expand on that -[2011-04-14 00:57:57] <ichthyo> yes -[2011-04-14 00:58:01] <ichthyo> please keep pending +Delectus Shot Evaluator +~~~~~~~~~~~~~~~~~~~~~~~ +Agreement to _park_ it until someone else comes up to advance this topic further. -[2011-04-14 00:58:11] <cehteh> Design Process : Clip Cataloging System -[2011-04-14 00:58:16] <cehteh> park -[2011-04-14 00:58:20] <ichthyo> park +Clip Cataloging System +~~~~~~~~~~~~~~~~~~~~~~ +similarily to _park_ until someone cares.... -[2011-04-14 00:58:31] <ichthyo> LumieraForwardIterator -[2011-04-14 00:58:37] <cehteh> Design Process: Lumiera Forward Iterator + +DesignParamAutomation +~~~~~~~~~~~~~~~~~~~~~ +Keep _pending_ -- _ichthyo_ will expand on that + + +Lumiera Forward Iterator +~~~~~~~~~~~~~~~~~~~~~~~~ + +[caption="☉Transcript☉ "] +---------------------------- [2011-04-14 00:58:39] <cehteh> pending - [2011-04-14 00:58:48] <ichthyo> well, this is entirely an C++ topic [2011-04-14 00:58:58] <ichthyo> I for my part vote for accepting it now [2011-04-14 00:59:11] <ichthyo> I use this concept now since almost a year and it worked out well @@ -532,8 +398,17 @@ The next meeting will be at Wednesday May 11, 20:00 UTC [2011-04-14 01:00:14] <cehteh> but if it works for you, i put it on 'maybe finalize' .. means i read through it and finalize it when i have no objections [2011-04-14 01:00:24] <ichthyo> yes, agreed +--------------------- -[2011-04-14 01:00:45] <cehteh> Design the Render Nodes interface + + + +Design the Render Nodes interface +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.-- Discussion of details -- +[caption="☉Transcript☉ "] +---------------------------- [2011-04-14 01:01:04] <cehteh> thats definitely pending .. needs discussion [2011-04-14 01:01:33] <ichthyo> yes [2011-04-14 01:01:54] <cehteh> maybe we park it to get rid of it for now since this is months ahead? @@ -541,17 +416,34 @@ The next meeting will be at Wednesday May 11, 20:00 UTC [2011-04-14 01:02:44] <ichthyo> that RfC basically sais: PLING PLING PLING, we need to discuss that [2011-04-14 01:02:53] <cehteh> well, I wont drop it, its a nice place to document the intention about the design [2011-04-14 01:03:10] <ichthyo> ok, so lets park it +--------------------- -[2011-04-14 01:03:13] <cehteh> Developer Documentation Structure + +Developer Documentation Structure +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--> see link:http://issues.lumiera.org/ticket/763[Ticket #763] + +.-- Discussion of details -- +[caption="☉Transcript☉ "] +--------------------- [2011-04-14 01:03:23] <cehteh> i check that, bring it up to date and finalize it? -[2011-04-14 01:03:36] <ichthyo> i have some objections agains that, see my comment +[2011-04-14 01:03:36] <ichthyo> i have some objections against that, see my comment [2011-04-14 01:04:00] <ichthyo> I think, the current structure is better than what that RfC proposes [2011-04-14 01:04:05] <cehteh> yes .. thats what i meant with bring it up to date [2011-04-14 01:04:39] <cehteh> i can keep it pending .. and then we can finalize it when you agree [2011-04-14 01:04:49] <ichthyo> ok, so you will update it? [2011-04-14 01:05:32] <cehteh> yes .. maybe not for next time but i put it on todo +--------------------- -[2011-04-14 01:05:35] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/EngineInterfaceOverview.html + + + +Engine Interface Overview +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.-- Discussion of details -- +[caption="☉Transcript☉ "] +---------------------------- [2011-04-14 01:05:50] <cehteh> pending .. there is lot to do? [2011-04-14 01:06:02] <ichthyo> sort of [2011-04-14 01:06:10] <ichthyo> basically that is a high level outline @@ -567,14 +459,21 @@ The next meeting will be at Wednesday May 11, 20:00 UTC [2011-04-14 01:07:43] <cehteh> yes .. lets talk next meeting about that (or some time else) [2011-04-14 01:07:57] <ichthyo> ok [2011-04-14 01:08:05] <cehteh> so pending for now +--------------------- -[2011-04-14 01:08:11] <ichthyo> FeatureBundle -[2011-04-14 01:08:14] <ichthyo> park -[2011-04-14 01:08:26] <cehteh> park -[2011-04-14 01:08:26] <ichthyo> very important, but far future -[2011-04-14 01:08:33] <cehteh> yes -[2011-04-14 01:08:39] <ichthyo> MarbleMode +Feature Bundle +~~~~~~~~~~~~~~ +Expected to be very important in the far future, but we don't have the +resources to work on that right now, so _park_ it. + + +Marble Mode +~~~~~~~~~~~ + +.-- Discussion of details -- +[caption="☉Transcript☉ "] +---------------------------- [2011-04-14 01:08:51] <ichthyo> this is also a high level one [2011-04-14 01:08:57] <ichthyo> I am much in favour of that [2011-04-14 01:09:09] <ichthyo> but its really kind of conceptual @@ -589,32 +488,31 @@ The next meeting will be at Wednesday May 11, 20:00 UTC [2011-04-14 01:11:58] <cehteh> or we just finalize it as 'concept' we want no matter how we implement it finally [2011-04-14 01:12:05] <cehteh> yes ok then [2011-04-14 01:12:10] <ichthyo> yes, ok then +--------------------- -[2011-04-14 01:12:49] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/NormalizedDeviceCoordinates.html -[2011-04-14 01:12:54] <cehteh> very rough -[2011-04-14 01:13:17] <cehteh> makes a lot of sense .. but unfinished, pending or park? -[2011-04-14 01:13:37] <ichthyo> I'd say park -[2011-04-14 01:13:51] <ichthyo> (I am also much in favour of that one) -[2011-04-14 01:13:42] <cehteh> ok +Normalized Device Coordinates +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +still very rough, but basically agreed. + +While it needs more work, it's a bit out of focus right now, so _park it. -[2011-04-14 01:14:16] <ichthyo> ProcHighLevel -[2011-04-14 01:14:24] <cehteh> thats rather final now? -[2011-04-14 01:14:26] <ichthyo> my vote goes for accept -[2011-04-14 01:14:35] <cehteh> is it up to date? -[2011-04-14 01:14:41] <ichthyo> no significant addition since almost two years -[2011-04-14 01:14:46] <ichthyo> yes, its up to date -[2011-04-14 01:14:52] <cehteh> ok final -[2011-04-14 01:15:37] <ichthyo> and, btw, I know that you also supported many of those ideas -[2011-04-14 01:15:39] <cehteh> placement .. -[2011-04-14 01:15:48] <ichthyo> I think same for that -[2011-04-14 01:15:57] <cehteh> up to date? -[2011-04-14 01:16:00] <ichthyo> if you don't have a problem with it, I vote for accept -[2011-04-14 01:16:28] <ichthyo> yes, as far as I can see, its up to date -[2011-04-14 01:16:32] <cehteh> yes for me the question is only if you need to refine some final things before accepting it -[2011-04-14 01:17:06] <cehteh> ok accept +Proc High Level Model and Placement concept +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +That's rather final by now. This link:http://lumiera.org/documentation/devel/rfc/ProcHighLevelModel.html[proposal] +meanwhile documents the existing design; it's up to date and didn't see significant +additions since almost two years. Generally agreed upon, so it's _final_ now. -[2011-04-14 01:17:18] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/RenderOptimizer.html +The same holds true for the +link:http://lumiera.org/documentation/devel/rfc/ProcPlacementMetaphor.html[Placement] proposal + + + +Render Optimizer, Resource Management and Profiling +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.-- Discussion of details -- +[caption="☉Transcript☉ "] +---------------------------- [2011-04-14 01:17:19] <cehteh> park [2011-04-14 01:17:32] <ichthyo> accept for me [2011-04-14 01:17:56] <ichthyo> that is so much our common understanding meanwhile @@ -642,16 +540,20 @@ The next meeting will be at Wednesday May 11, 20:00 UTC first sentences, because then we could accept it right away [2011-04-14 01:21:45] <cehteh> ok pending for now .. i dont want to work on this currently .. other things are more important [2011-04-14 01:21:47] <ichthyo> we both pretty much agree that we *want* some kind of budget managing and resource usage +--------------------- -[2011-04-14 01:22:41] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/Roadmap-first.html -[2011-04-14 01:22:45] <cehteh> final? -[2011-04-14 01:22:51] <ichthyo> oops! my fault -[2011-04-14 01:22:55] <cehteh> anything not up to date? -[2011-04-14 01:23:00] <ichthyo> that *was* accepted long ago -[2011-04-14 01:23:13] <cehteh> haha ok -[2011-04-14 01:23:47] <ichthyo> we discussed and accepted that 2009, judging from the comments -[2011-04-14 01:23:49] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/StreamTypeSystem.html +Roadmap +~~~~~~~ +The link:http://lumiera.org/documentation/devel/rfc/Roadmap-first.html[Roadmap document] +was erroneously not marked as final; + +Seemingly it was decided upon in 2009 already ... + +Stream Type System +~~~~~~~~~~~~~~~~~~ + +[caption="☉Transcript☉ "] +---------------------------- [2011-04-14 01:24:06] <ichthyo> very important for me -- my vote is for accept [2011-04-14 01:24:16] <cehteh> we had some discussion how to maintain metadata .. [2011-04-14 01:24:40] <cehteh> i vote for accept too but this metadata (which may decribe the type) needs work @@ -670,8 +572,14 @@ The next meeting will be at Wednesday May 11, 20:00 UTC [2011-04-14 01:27:38] <cehteh> i think that needs some time to settle to the right point [tm] [2011-04-14 01:27:43] <ichthyo> I'm not right away implementing it, but the implementation is rather trivial [2011-04-14 01:27:48] <ichthyo> so leave it pending +--------------------- -[2011-04-14 01:28:00] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/ThreadsSignalsAndImportantManagementTasks.html + +Threads Signals and important management tasks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.-- Discussion of details -- +[caption="☉Transcript☉ "] +---------------------------- [2011-04-14 01:28:33] <cehteh> we need to work together to implement this on the main .. but generally i think this can be accepted with some refinements [2011-04-14 01:28:14] <ichthyo> some time ago, we had a short discussion about that @@ -701,50 +609,34 @@ The next meeting will be at Wednesday May 11, 20:00 UTC [2011-04-14 01:35:51] <cehteh> anyways .. i accept it .. implementation pending [2011-04-14 01:37:38] <cehteh> signal handling becomes a 'subsystem' then ... :) [2011-04-14 01:37:46] <ichthyo> yes, thats what I mean +--------------------- -[2011-04-14 01:36:50] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/TimelineSequenceOutput.html -[2011-04-14 01:36:52] <cehteh> final? -[2011-04-14 01:37:00] <ichthyo> definitively final -[2011-04-14 01:38:08] <ichthyo> TimelineSequence: the key point is: we have multiple timelines -[2011-04-14 01:38:28] <ichthyo> and a sequence can be used in multiple timelines -[2011-04-14 01:38:36] <cehteh> yes .. ok for me i think -[2011-04-14 01:38:37] <ichthyo> I think we pretty much agree on that -[2011-04-14 01:39:01] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/UseCases.html -[2011-04-14 01:39:08] <cehteh> nobody cares :P -[2011-04-14 01:39:20] <ichthyo> nobody cares -[2011-04-14 01:39:34] <cehteh> that means parked? or drop? -[2011-04-14 01:39:35] <ichthyo> park it, until we have someone working on the workflow +Session structure -- Timelines, Sequences, Output +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This link:http://lumiera.org/documentation/devel/rfc_pending/TimelineSequenceOutput.html[proposal] +can be considered definitively final. Key point is: we have multiple timelines and a sequence can be used +in multiple timelines. We pretty much agree on that, thus it counts as _finalised_ now. -[2011-04-14 01:40:01] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/VersionNumberScheme.html -[2011-04-14 01:40:10] <cehteh> accept (after you explained it to me .. ) -[2011-04-14 01:40:35] <ichthyo> :-D -[2011-04-14 01:40:14] <ichthyo> accept +Use Cases +~~~~~~~~~ +This is an heavyweight proposal regarding the high-level design and general handling of the +Application. This would be really a topic to be discussed in conjection with the ``Workflow'' +-- the idea was to have a working group focussed these topics entirely, but there is no one +in charge of that right now. Thus _park_ it for the time being. -[2011-04-14 01:40:41] <cehteh> http://lumiera.org/documentation/devel/rfc_pending/WebsiteNavigation.html -[2011-04-14 01:40:50] <cehteh> is that final? -[2011-04-14 01:40:55] <cehteh> (up to date) -[2011-04-14 01:41:15] <cehteh> do others need to discuss this .. fsiddi? -[2011-04-14 01:41:27] <ichthyo> there is one point: the tagging of pages -[2011-04-14 01:41:37] <ichthyo> if we remove that, the rest is implemented right now -[2011-04-14 01:42:08] <cehteh> leave it pending and when you meet with fsiidi next time you discuss and fix this? -[2011-04-14 01:42:18] <ichthyo> ok +Version Number Scheme +~~~~~~~~~~~~~~~~~~~~~ +The proposal for link:http://lumiera.org/documentation/devel/rfc_pending/VersionNumberScheme.html[Version numbering] +is _accepted_ -- it's considered close to common practice and _ichthyo_ relied on it for the debian +package already. + +''''''''''''' + + +Next meeting +------------ + +The next meeting will be as usual, at Wednesday May 11, 20:00 UTC -[2011-04-14 01:47:47] <ichthyo> so meeting is finished now, officially... -[2011-04-14 01:47:59] <cehteh> n8 :) -[2011-04-14 01:48:10] <cehteh> well i work a bit .. night owl mode :P -[2011-04-14 01:48:11] <ichthyo> next meeting on 11.5.2011 -[2011-04-14 01:48:42] <ichthyo> btw, I'm quite sure I skip LAC this time -[2011-04-14 01:48:52] <cehteh> me too -[2011-04-14 01:48:58] <ichthyo> just overall too much to do right now -[2011-04-14 01:49:15] <cehteh> no lumiera at lac -[2011-04-14 01:49:33] <ichthyo> but I'd be quite interested to come to that FSCONS conference skangas told us about -[2011-04-14 01:49:38] <ichthyo> in october or so -[2011-04-14 01:49:57] <cehteh> lets see .. time & money -[2011-04-14 01:51:12] <ichthyo> ok, going off now -[2011-04-14 01:51:15] * cehteh goes hunting some food -[2011-04-14 01:51:19] <ichthyo> (and hopefully going to bed soon) -[2011-04-14 01:51:21] <cehteh> see you -[2011-04-14 01:51:24] <ichthyo> see you! ----------------------------- From 3b91a129f2a72420702376faacafd50f36383d38 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 19 Apr 2011 05:42:21 +0200 Subject: [PATCH 026/496] update the short summary of the Apr.2011 meeting Signed-off-by: Ichthyostega <prg@ichthyostega.de> --- doc/devel/meeting_summary/index.txt | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/doc/devel/meeting_summary/index.txt b/doc/devel/meeting_summary/index.txt index b8e3b42d5..a90a692b2 100644 --- a/doc/devel/meeting_summary/index.txt +++ b/doc/devel/meeting_summary/index.txt @@ -19,10 +19,33 @@ archives and other documentation. Topics ~~~~~~ - * boilerplate (protocol, what's left from last meeting) + * New Website Page Layout + - liquid layout? + - vertical scrolling? + - colour scheme? + - Webserver upgrade? + * Website licensing and legal questions + * Trac spam * Go through Ideas and Drafts in design process - - processing node interface - - Placement concept + - Application Install + - Delectus Shot Evaluator + - Clip Cataloging System + - Design Param Automation + - Lumiera Forward Iterator -> *accepted* + - Design the Render Nodes interface + - Developer Documentation Structure + - Engine Interface Overview + - Feature Bundle + - Marble Mode + - Normalized Device Coordinates + - Proc High Level Model -> *accepted* + - Placement concept -> *accepted* + - Render Optimizer, Resource Management and Profiling + - Stream Type System + - Threads Signal handling + - Session structure -- Timelines, Sequences, Output -> *accepted* + - Use Cases + - Version Number Scheme -> *accepted* Summary ^^^^^^^ From 54afd3f860128c979490776fc3ae6f52fbce7520 Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Fri, 22 Apr 2011 10:45:59 +0200 Subject: [PATCH 027/496] filter asciidoc comments in rfc.sh usage() out --- admin/rfc.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/admin/rfc.sh b/admin/rfc.sh index c7d4dcb79..ce0e4d0ea 100755 --- a/admin/rfc.sh +++ b/admin/rfc.sh @@ -4,10 +4,9 @@ shopt -s extglob function usage() { - less -F <<"EOF" + grep -v '^// ' <<"EOF" | less -F Lumiera RFC maintenance script ============================== - // Note: the source of this documentation is maintained // directly admin/rfc.sh in its usage() function // edit it only there and then regenerate From 48f5e154a05a275647178217eb22b03095f6ee40 Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Fri, 22 Apr 2011 11:47:10 +0200 Subject: [PATCH 028/496] New RFC about factoring liblumiera out into a spinoff subproject --- .../rfc_pending/RefactorLiblumieraOut.txt | 240 ++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 doc/devel/rfc_pending/RefactorLiblumieraOut.txt diff --git a/doc/devel/rfc_pending/RefactorLiblumieraOut.txt b/doc/devel/rfc_pending/RefactorLiblumieraOut.txt new file mode 100644 index 000000000..c31fb06a2 --- /dev/null +++ b/doc/devel/rfc_pending/RefactorLiblumieraOut.txt @@ -0,0 +1,240 @@ +Refactor Liblumiera Out +======================= + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _Fr 22 Apr 2011 10:46:50 CEST_ +*Proposed by* Christian Thaeter <ct@pipapo.org> +------------------------------------- + +[abstract] +******************************************************************************** +liblumiera contains alot useful and reuseable code which is already in use by +other projects +******************************************************************************** + +Description +----------- +//description: add a detailed description: +Over the time we've put some efforts into the liblumiera. I've added +some from my code which predates the lumiera project which I am using +on many other projects. This now caused that I maintain this sources in +different unrelated projects and have to cross merge and update stuff +when I do updates and fixes somewhere. I think its time to factor the +reuseable parts out into a independent library (like glib does for +gtk), in fact I had this plan long ago. + + +.What parts are eligible for a standalone library + +Anything which is something tool alike and useful for other projects and not +tied to Lumiera only. This are the algorithms/datastructures, allocators, tool +macros. Additionally some of the src/common things should be moved into the +library. I give some lists below. + +.How to name it + +Long time ago my plan was to name it 'ctlib' or 'cehlib' but meanwhile there is +enough code done by others. So I'd propose a more neutral name, still +'lumieralib' or 'lulib' would be approbiate. The only thing we have to account +for is that some parts which are too specific for Lumiera and should not be +integrated into this spinoff need either to stay in a lumiera-internal lib +(src/lib/) as currently or being moved to the respective subsystems using them +(src/backend, src/proc, src/common, ...), so the names should not clash. + +.C, C++ ... + +For myself I need the C parts, while there is C++ code which interfaces to the +C implementations and also a lot code which does nice C++ things on its own. +This possibly means that we should in fact make 2 packages out of this, one C +and one C++ library (where the C++ part is more than just the wrappers, but +also the tools and tricks which are currently in src/lib/ and reuseable). + +.Who maintains it + +Despite a spin of I think we don't want to change anything from our current +practice and maintain it by the Lumiera developers. For many parts I feel +responsible for it, but its really a part of the Lumiera codebase, despite +independently useable. + +.How to maintain it + +We need to decide about build system and documentation system. As build system +we may right start using scons. For documentation the situation is a but +different since some of my code uses pipadoc/asciidoc and other uses doxygen. + +.What not to do + +Some of the code is currently quite specific to Lumiera while it could be made +more generic. This is *NOT* subject of this RFC we may or may not do such a +refactoring but this RFC and any work resulting from this should only restrict +to simple things like necessary namespace and variable renaming and integration +in the build system. + + +C Parts +------- + +Library +~~~~~~~ +What belongs to the library + +Containers +^^^^^^^^^^ + * cuckoo hashing (cuckoo.c|h) + * linked lists (llist.h slist.h) + * cache lists (mrucache.c|h) + * splay trees (psplay.c|h) + * priority queues (not done yet) + +Runtime tools +^^^^^^^^^^^^^ + * error handling (error.h error.c) used by the other facilities too + * clib convinience wrapers (safeclib.c|h) needs better name, maybe refactor + into new facilities + +Multithreading +^^^^^^^^^^^^^^ + * locking, condition variables etc. (condition.c|h (rec)mutex.c|h, rwlock ...) + +Memory management +^^^^^^^^^^^^^^^^^ + * Memory pools (mpool.c|h) + * Temporary buffers (tmpbuf.c|h) + +Metaprogramming +^^^^^^^^^^^^^^^ + * preprecessor tools (ppmpl.h) move common preprocessor macros here + * polymorphic call helper for C (vcall.h) + +Interface system and module loader +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +except for some hardcoded references to 'lumiera_org' and '.lum' plugin names +this is quite generic, possibly moving this over could be postponed, but might +eventually be done. + +From 'src/common' +------ +interface.c interfacedescriptor.h interface.h interfaceproxy.cpp +interfaceregistry.c interfaceregistry.h plugin.c plugin_dynlib.c plugin.h +------ + + +The 'config' system could become a candidate too if it ever gets finished and +proved useful, but for the time being its better kept in Lumiera. + + +Not Library +~~~~~~~~~~~ +Too specific to Lumiera: +----- +luid.c luid.h time.h +----- + + +C++ Parts +--------- + +For most of the C++ parts I am not sure, ichthyo should decided upon these +(please edit this here) + +Library +~~~~~~~ +These look 'generic' or wrap the C parts: +------ +singleton-factory.hpp singleton.hpp singleton-policies.hpp +singleton-preconfigure.hpp singleton-ref.hpp singleton-subclass.hpp +sync-classlock.hpp sync.cpp sync.hpp thread-local.hpp +typed-allocation-manager.hpp typed-counter.hpp util.cpp util-foreach.hpp +util.hpp variant.hpp +------ + +Not Sure +~~~~~~~~ +------ +access-casted.hpp advice advice.hpp allocation-cluster.cpp +allocation-cluster.hpp bool-checkable.hpp cmdline.cpp cmdline.hpp del-stash.hpp +diagnostic-context.hpp element-tracker.hpp error.hpp (currently too +lumiera specific) exception.cpp (as before) factory.hpp format.hpp +frameid.hpp functor-util.hpp handle.hpp hash-indexed.hpp iter-adapter.hpp +iter-adapter-stl.hpp iter-source.hpp itertools.hpp iter-type-binding.hpp +lifecycle.cpp lifecycleregistry.hpp lumitime-fmt.hpp lumitime.hpp +multifact-arg.hpp multifact.hpp meta/* null-value.hpp observable-list.hpp +opaque-holder.hpp optional-ref.hpp p.hpp query.cpp query.hpp ref-array.hpp +ref-array-impl.hpp result.hpp scoped-holder.hpp scoped-holder-transfer.hpp +scoped-ptrvect.hpp searchpath.cpp searchpath.hpp sub-id.hpp symbol.hpp +symbol-impl.cpp visitor-dispatcher.hpp visitor.hpp visitor-policies.hpp +wrapper.hpp wrapperptr.hpp appstate.cpp appstate.hpp basic-setup.cpp +basic-setup.hpp DIR_INFO external guifacade.cpp instancehandle.hpp option.cpp +option.hpp query subsys.cpp subsys.hpp subsystem-runner.hpp +---- + + +Not Library +~~~~~~~~~~~ +------ +logging.cpp nobug-init.cpp nobug-init.hpp streamtype.cpp streamtype.hpp test/* +time/* time.cpp tree.hpp +----- + +Tasks +~~~~~ +// List what needs to be done to implement this Proposal: +// * first step ([green]#✔ done#) + + * decide on name, namespaces [,yellow]#WIP# + * create git repository, setup boilerplace (build system, legalese) + [,yellow]#WIP# + * move all code over into the git repos, refactor (namespace renames) () + [,yellow]#WIP# + * make Lumiera use the new lib [,yellow]#WIP# + +Discussion +~~~~~~~~~~ + +Pros +^^^^ +// add a fact list/enumeration which make this suitable: + + * I am already reuse much of its code, making it independent makes maintaining + it less burden + + +Cons +^^^^ +// fact list of the known/considered bad implications: + + * new packages, new dependencies for Lumiera instead 'batteries included' + +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 + + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] From e6cc7d3f99d1a66a706b847433d5a9d3cb1f87c3 Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Thu, 28 Apr 2011 13:14:29 +0200 Subject: [PATCH 029/496] WIP: autotools catchup, make it build again Lumiera builds again, but some tests are missing and the new setup.ini isn't loaded correctly which makes the plugin loader fail. --- configure.ac | 9 ++++++++- src/common/Makefile.am | 2 ++ src/lib/Makefile.am | 13 +++++-------- src/proc/Makefile.am | 34 ++++++++++++++++++---------------- tests/lib/Makefile.am | 12 ++++++------ 5 files changed, 39 insertions(+), 31 deletions(-) diff --git a/configure.ac b/configure.ac index 75185b3d9..8a139aeb8 100644 --- a/configure.ac +++ b/configure.ac @@ -110,7 +110,7 @@ AC_CHECK_FUNCS(clock_gettime, AC_MSG_NOTICE([clock_gettime is in the clib]), AC_CHECK_LIB(rt, clock_gettime, [LUMIERA_LIBS="$LUMIERA_LIBS -lrt"], - AC_MSG_ERROR([No library providing clock_gettime found]) + AC_MSG_ERROR([No library providing clock_gettime found]) ) ) @@ -143,6 +143,13 @@ AC_CHECK_HEADER([boost/regex.hpp], AC_MSG_ERROR([boost::regex missing (http://www.boost.org/)]) ) +AC_CHECK_HEADER([boost/filesystem.hpp], + [LUMIERA_BOOST_LIBS="$LUMIERA_BOOST_LIBS -lboost_filesystem-mt"], + AC_MSG_ERROR([boost::filesystem missing (http://www.boost.org/)]) + ) + + + AC_LANG_POP([C++]) AC_SUBST(LUMIERA_BOOST_CFLAGS) AC_SUBST(LUMIERA_BOOST_LIBS) diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 5f30d3cb1..f84559290 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -28,6 +28,7 @@ liblumieracommon_la_LIBADD = \ liblumieracommon_la_SOURCES = \ $(liblumieracommon_la_srcdir)/appstate.cpp \ + $(liblumieracommon_la_srcdir)/basic-setup.cpp \ $(liblumieracommon_la_srcdir)/config.c \ $(liblumieracommon_la_srcdir)/config_interface.c \ $(liblumieracommon_la_srcdir)/config_lookup.c \ @@ -51,6 +52,7 @@ liblumieracommon_la_SOURCES = \ noinst_HEADERS += \ $(liblumieracommon_la_srcdir)/appstate.hpp \ + $(liblumieracommon_la_srcdir)/basic-setup.hpp \ $(liblumieracommon_la_srcdir)/config.h \ $(liblumieracommon_la_srcdir)/config_interface.h \ $(liblumieracommon_la_srcdir)/config_lookup.h \ diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index fe64f516a..a44b47423 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -54,8 +54,10 @@ liblumiera_la_SOURCES = \ $(liblumiera_la_srcdir)/recmutex.c \ $(liblumiera_la_srcdir)/rwlock.c \ $(liblumiera_la_srcdir)/safeclib.c \ + $(liblumiera_la_srcdir)/searchpath.cpp \ $(liblumiera_la_srcdir)/streamtype.cpp \ $(liblumiera_la_srcdir)/symbol-impl.cpp \ + $(liblumiera_la_srcdir)/sync.cpp \ $(liblumiera_la_srcdir)/test/suite.cpp \ $(liblumiera_la_srcdir)/test/test-helper.cpp \ $(liblumiera_la_srcdir)/test/testoption.cpp \ @@ -88,14 +90,11 @@ noinst_HEADERS += \ $(liblumiera_la_srcdir)/meta/typelist.hpp \ $(liblumiera_la_srcdir)/mpool.h \ $(liblumiera_la_srcdir)/mrucache.h \ - $(liblumiera_la_srcdir)/mrucache.h \ $(liblumiera_la_srcdir)/mutex.h \ $(liblumiera_la_srcdir)/nobug-init.hpp \ $(liblumiera_la_srcdir)/observable-list.hpp \ $(liblumiera_la_srcdir)/p.hpp \ $(liblumiera_la_srcdir)/ppmpl.h \ - $(liblumiera_la_srcdir)/ppmpl.h \ - $(liblumiera_la_srcdir)/psplay.h \ $(liblumiera_la_srcdir)/psplay.h \ $(liblumiera_la_srcdir)/query.hpp \ $(liblumiera_la_srcdir)/reccondition.h \ @@ -103,25 +102,23 @@ noinst_HEADERS += \ $(liblumiera_la_srcdir)/rwlock.h \ $(liblumiera_la_srcdir)/safeclib.h \ $(liblumiera_la_srcdir)/scoped-holder.hpp \ - $(liblumiera_la_srcdir)/singleton-subclass.hpp \ - $(liblumiera_la_srcdir)/singleton.hpp \ + $(liblumiera_la_srcdir)/searchpath.hpp \ $(liblumiera_la_srcdir)/singleton-factory.hpp \ $(liblumiera_la_srcdir)/singleton-policies.hpp \ $(liblumiera_la_srcdir)/singleton-preconfigure.hpp \ + $(liblumiera_la_srcdir)/singleton-subclass.hpp \ + $(liblumiera_la_srcdir)/singleton.hpp \ $(liblumiera_la_srcdir)/sync-classlock.hpp \ $(liblumiera_la_srcdir)/sync.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.h \ $(liblumiera_la_srcdir)/tmpbuf.h \ $(liblumiera_la_srcdir)/tree.hpp \ $(liblumiera_la_srcdir)/util.hpp \ $(liblumiera_la_srcdir)/visitor-dispatcher.hpp \ $(liblumiera_la_srcdir)/visitor-policies.hpp \ $(liblumiera_la_srcdir)/visitor.hpp - diff --git a/src/proc/Makefile.am b/src/proc/Makefile.am index 56ef795df..ddbf2f29f 100644 --- a/src/proc/Makefile.am +++ b/src/proc/Makefile.am @@ -39,22 +39,23 @@ noinst_LTLIBRARIES += liblumiprocasset.la liblumiprocasset_la_CFLAGS = $(AM_CFLAGS) $(LUMIERA_PROC_CFLAGS) -std=gnu99 -Wall -Wextra -Werror liblumiprocasset_la_CXXFLAGS = $(AM_CXXFLAGS) $(LUMIERA_PROC_CFLAGS) -Wall -Wextra -liblumiprocasset_la_SOURCES = \ - $(liblumiprocasset_la_srcdir)/category.cpp \ - $(liblumiprocasset_la_srcdir)/clip.cpp \ - $(liblumiprocasset_la_srcdir)/codec.cpp \ - $(liblumiprocasset_la_srcdir)/compoundmedia.cpp \ - $(liblumiprocasset_la_srcdir)/effect.cpp \ - $(liblumiprocasset_la_srcdir)/media.cpp \ - $(liblumiprocasset_la_srcdir)/meta.cpp \ - $(liblumiprocasset_la_srcdir)/pipe.cpp \ - $(liblumiprocasset_la_srcdir)/preview.cpp \ - $(liblumiprocasset_la_srcdir)/proc.cpp \ - $(liblumiprocasset_la_srcdir)/procpatt.cpp \ - $(liblumiprocasset_la_srcdir)/sequence.cpp \ - $(liblumiprocasset_la_srcdir)/struct.cpp \ - $(liblumiprocasset_la_srcdir)/timeline.cpp \ - $(liblumiprocasset_la_srcdir)/typed-lookup.cpp \ +liblumiprocasset_la_SOURCES = \ + $(liblumiprocasset_la_srcdir)/category.cpp \ + $(liblumiprocasset_la_srcdir)/clip.cpp \ + $(liblumiprocasset_la_srcdir)/codec.cpp \ + $(liblumiprocasset_la_srcdir)/compoundmedia.cpp \ + $(liblumiprocasset_la_srcdir)/effect.cpp \ + $(liblumiprocasset_la_srcdir)/media.cpp \ + $(liblumiprocasset_la_srcdir)/meta.cpp \ + $(liblumiprocasset_la_srcdir)/meta/time-grid.cpp \ + $(liblumiprocasset_la_srcdir)/pipe.cpp \ + $(liblumiprocasset_la_srcdir)/preview.cpp \ + $(liblumiprocasset_la_srcdir)/proc.cpp \ + $(liblumiprocasset_la_srcdir)/procpatt.cpp \ + $(liblumiprocasset_la_srcdir)/sequence.cpp \ + $(liblumiprocasset_la_srcdir)/struct.cpp \ + $(liblumiprocasset_la_srcdir)/timeline.cpp \ + $(liblumiprocasset_la_srcdir)/typed-lookup.cpp \ $(liblumiprocasset_la_srcdir)/unknown.cpp @@ -199,6 +200,7 @@ noinst_HEADERS += \ $(liblumiproc_la_srcdir)/asset/effect.hpp \ $(liblumiproc_la_srcdir)/asset/media.hpp \ $(liblumiproc_la_srcdir)/asset/meta.hpp \ + $(liblumiproc_la_srcdir)/asset/meta/time-grid.hpp \ $(liblumiproc_la_srcdir)/asset/pipe.hpp \ $(liblumiproc_la_srcdir)/asset/preview.hpp \ $(liblumiproc_la_srcdir)/asset/proc.hpp \ diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am index 12d1892cf..347ee36a1 100644 --- a/tests/lib/Makefile.am +++ b/tests/lib/Makefile.am @@ -44,8 +44,9 @@ test_lib_SOURCES = \ $(testlib_srcdir)/advice/advice-multiplicity-test.cpp \ $(testlib_srcdir)/advice/advice-situations-test.cpp \ $(testlib_srcdir)/allocation-cluster-test.cpp \ - $(testlib_srcdir)/appconfigtest.cpp \ + $(testlib_srcdir)/app-config-test.cpp \ $(testlib_srcdir)/bool-checkable-test.cpp \ + $(testlib_srcdir)/cmdline-wrapper-test.cpp \ $(testlib_srcdir)/custom-shared-ptr-test.cpp \ $(testlib_srcdir)/del-stash-test.cpp \ $(testlib_srcdir)/exceptionerrortest.cpp \ @@ -61,6 +62,7 @@ test_lib_SOURCES = \ $(testlib_srcdir)/iter-source-test.cpp \ $(testlib_srcdir)/itertools-test.cpp \ $(testlib_srcdir)/lifecycletest.cpp \ + $(testlib_srcdir)/lumitime-test.cpp \ $(testlib_srcdir)/mainsuite.cpp \ $(testlib_srcdir)/meta/config-flags-test.cpp \ $(testlib_srcdir)/meta/duck-detector-test.cpp \ @@ -83,8 +85,8 @@ test_lib_SOURCES = \ $(testlib_srcdir)/removefromsettest.cpp \ $(testlib_srcdir)/sanitised-identifier-test.cpp \ $(testlib_srcdir)/scoped-holder-test.cpp \ - $(testlib_srcdir)/scoped-ptrvect-test.cpp \ $(testlib_srcdir)/scoped-holder-transfer-test.cpp \ + $(testlib_srcdir)/scoped-ptrvect-test.cpp \ $(testlib_srcdir)/singleton-subclass-test.cpp \ $(testlib_srcdir)/singleton-test.cpp \ $(testlib_srcdir)/singleton-testmock-test.cpp \ @@ -97,15 +99,13 @@ test_lib_SOURCES = \ $(testlib_srcdir)/sync-classlock-test.cpp \ $(testlib_srcdir)/sync-locking-test.cpp \ $(testlib_srcdir)/sync-waiting-test.cpp \ - $(testlib_srcdir)/test/cmdlinewrappertest.cpp \ $(testlib_srcdir)/test/test-helper-test.cpp \ $(testlib_srcdir)/test/testoptiontest.cpp \ $(testlib_srcdir)/thread-wrapper-join-test.cpp \ $(testlib_srcdir)/thread-wrapper-test.cpp \ - $(testlib_srcdir)/lumitime-test.cpp \ - $(testlib_srcdir)/time/time-value-test.cpp \ - $(testlib_srcdir)/time/time-quantisation-test.cpp \ $(testlib_srcdir)/time/time-formats-test.cpp \ + $(testlib_srcdir)/time/time-quantisation-test.cpp \ + $(testlib_srcdir)/time/time-value-test.cpp \ $(testlib_srcdir)/typed-allocation-manager-test.cpp \ $(testlib_srcdir)/typed-counter-test.cpp \ $(testlib_srcdir)/util-foreach-test.cpp \ From d4bb6cc9060ac27efc777748119a05349860b09f Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Fri, 29 Apr 2011 04:04:01 +0200 Subject: [PATCH 030/496] fnv hash functions and tools --- src/lib/hash_fnv.c | 176 +++++++++++++++++++++++++++++++++++++++++++++ src/lib/hash_fnv.h | 171 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 347 insertions(+) create mode 100644 src/lib/hash_fnv.c create mode 100644 src/lib/hash_fnv.h diff --git a/src/lib/hash_fnv.c b/src/lib/hash_fnv.c new file mode 100644 index 000000000..c0cdc8095 --- /dev/null +++ b/src/lib/hash_fnv.c @@ -0,0 +1,176 @@ +/* + hash_fnv.c - FNV hash functions + + Copyright (C) + 2010, 2011, Christian Thaeter <ct@pipapo.org> + + 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, contact Christian Thaeter <ct@pipapo.org>. + +The actual fnv functions are taken from Landon Curt Noll's original code, +slightly modified and adapted to C99; no copyright applies: + *** + * + * Please do not copyright this code. This code is in the public domain. + * + * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * By: + * chongo <Landon Curt Noll> /\oo/\ + * http://www.isthe.com/chongo/ + * + * Share and Enjoy! :-) +*/ + + +#include "hash_fnv.h" + +#include <nobug.h> + +uint64_t +hash_fnv64a_buf (const void *buf, size_t len, uint64_t hval) +{ + const unsigned char *bp = (const unsigned char *)buf; + const unsigned char *be = bp + len; + while (bp < be) + { + hval ^= (uint64_t)*bp++; + hval *= HASH_FNV64_PRIME; + } + + return hval; +} + + + +uint32_t +hash_fnv32a_buf (const void *buf, size_t len, uint32_t hval) +{ + const unsigned char *bp = (const unsigned char *)buf; + const unsigned char *be = bp + len; + while (bp < be) + { + hval ^= (uint32_t)*bp++; + hval *= HASH_FNV32_PRIME; + } + + return hval; +} + + +uint64_t +hash_fnv64a_strn (const char* str, size_t len, uint64_t hval) +{ + const unsigned char *bp = (const unsigned char *)str; + if (bp) + while (*bp && len--) + { + hval ^= (uint64_t)*bp++; + hval *= HASH_FNV64_PRIME; + } + + return hval; +} + + + +uint32_t +hash_fnv32a_strn (const char* str, size_t len, uint32_t hval) +{ + const unsigned char *bp = (const unsigned char *)str; + if (bp) + while (*bp && len--) + { + hval ^= (uint32_t)*bp++; + hval *= HASH_FNV32_PRIME; + } + + return hval; +} + + + +uint64_t +hash_fnv64_xorfold (uint64_t hash, int bits) +{ + REQUIRE(bits <= 64); + + bits = 64-bits; + + uint64_t mask = ~0ULL>>bits; + for (int todo = 32; bits && todo; todo >>= 1) + if (bits >= todo) + { + hash = hash ^ hash>>todo; + bits-=todo; + } + + return hash & mask; +} + + +uint32_t +hash_fnv32_xorfold (uint32_t hash, int bits) +{ + REQUIRE (bits <= 32); + + bits = 32-bits; + + uint32_t mask = ~0ULL>>bits; + for (int todo = 16; bits && todo; todo >>= 1) + if (bits >= todo) + { + hash = hash ^ hash>>todo; + bits-=todo; + } + + return hash & mask; +} + + +uint64_t +hash_fnv64_retry (uint64_t hash, uint64_t limit) +{ + uint64_t retry_level= (UINT64_MAX / limit) * limit; + + while (hash >= retry_level) + hash = (hash * HASH_FNV64_PRIME) + HASH_FNV64_BASE; + + return hash % limit; +} + + +uint32_t +hash_fnv32_retry (uint64_t hash, uint32_t limit) +{ + uint32_t retry_level= (UINT32_MAX / limit) * limit; + + while (hash >= retry_level) + hash = (hash * HASH_FNV32_PRIME) + HASH_FNV32_BASE; + + return hash % limit; +} + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/hash_fnv.h b/src/lib/hash_fnv.h new file mode 100644 index 000000000..aa8db900a --- /dev/null +++ b/src/lib/hash_fnv.h @@ -0,0 +1,171 @@ +/* + hash_fnv.c - FNV hash functions + + Copyright (C) + 2010, 2011, Christian Thaeter <ct@pipapo.org> + + 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, contact Christian Thaeter <ct@pipapo.org>. + +The actual fnv functions are taken from Landon Curt Noll's original code, no copyright applies: + *** + * + * Please do not copyright this code. This code is in the public domain. + * + * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * By: + * chongo <Landon Curt Noll> /\oo/\ + * http://www.isthe.com/chongo/ + * + * Share and Enjoy! :-) +*/ + +/** + * @file + * Fowler / Noll / Vo (FNV) Hashes + */ + + +#ifndef HASH_FNV_H +#define HASH_FNV_H + +#include <stdlib.h> /* for size_t */ +#include <inttypes.h> + +#define HASH_FNV64_BASE ((uint64_t)14695981039346656037ULL) +#define HASH_FNV32_BASE ((uint32_t)2166136261UL) + +#define HASH_FNV64_PRIME ((uint64_t)1099511628211ULL) +#define HASH_FNV32_PRIME ((uint32_t)16777619UL) + + +/** + * FNV-1a 64 bit hash over a buffer. + * @param buf start of the buffer + * @param len size of the buffer + * @param hval previous hash value when incremental hashing or HASH_FNV64_BASE when starting a new hash + * @return new hash value + */ +uint64_t +hash_fnv64a_buf (const void *buf, size_t len, uint64_t hval); + +/** + * FNV-1a 64 bit hash over a zero terminated string. + * @param buf start of the buffer + * @param len maximum size to be processed + * @param hval previous hash value when incremental hashing or HASH_FNV64_BASE when starting a new hash + * @return new hash value + */ +uint64_t +hash_fnv64a_strn (const char* str, size_t len, uint64_t hval); + + +/** + * FNV-1a 32 bit hash over a buffer. + * @param buf start of the buffer + * @param len size of the buffer + * @param hval previous hash value when incremental hashing or HASH_FNV32_BASE when starting a new hash + * @return new hash value + */ +uint32_t +hash_fnv32a_buf (const void *buf, size_t len, uint32_t hval); + + +/** + * FNV-1a 32 bit hash over a zero terminated string. + * @param buf start of the buffer + * @param len maximum size to be processed + * @param hval previous hash value when incremental hashing or HASH_FNV32_BASE when starting a new hash + * @return new hash value + */ +uint32_t +hash_fnv32a_strn (const char* str, size_t len, uint32_t hval); + + + +/** + * reduce a hash value to n bits. + * Uses the xor folding method to stash a hash value together, this preserves unbiased hash distribution + * @param hash result from one of the 64 bit hash functions above + * @param bits number of significat bits for the result + * @result hashvalue with no more than 'bits' significant bits + */ +uint64_t +hash_fnv64_xorfold (uint64_t hash, int bits); + + +/** + * reduce a hash value to n bits. + * Uses the xor folding method to stash a hash value together, this preserves unbiased hash distribution + * @param hash result from one of the 32 bit hash functions above + * @param bits number of significat bits for the result + * @result hashvalue with no more than 'bits' significant bits + */ +uint32_t +hash_fnv32_xorfold (uint32_t hash, int bits); + + +/** + * reduce hash to be within 0 to limit-1. + * Uses the retry method to limit a hash value, this preserves unbiased hash distribution. + * @param hash result from one of the 64 bit hash functions above + * @param limit upper limit plus one for the result + * @result hashvalue in the range from 0 to limit-1 + */ +uint64_t +hash_fnv64_retry (uint64_t hash, uint64_t limit); + + +/** + * reduce hash to be within 0 to limit-1. + * Uses the retry method to limit a hash value, this preserves unbiased hash distribution. + * @param hash result from one of the 32 bit hash functions above + * @param limit upper limit plus one for the result + * @result hashvalue in the range from 0 to limit-1 + */ +uint32_t +hash_fnv32_retry (uint64_t hash, uint32_t limit); + + + +/* +PLANNED? 128 bit and 256bit hashes +128 bit FNV_prime = 288 + 28 + 0x3b = 309485009821345068724781371 + +256 bit FNV_prime = 2168 + 28 + 0x63 = 374144419156711147060143317175368453031918731002211 + +128 bit offset_basis = 144066263297769815596495629667062367629 + +256 bit offset_basis = +100029257958052580907070968620625704837092796014241193945225284501741471925557 + +*/ + + + +#endif + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ From b83c7dea3b0c7671a1dcbbb8b389327698b5ae55 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 8 May 2011 19:12:35 +0200 Subject: [PATCH 031/496] Start collection design elements of the Player subsystem Signed-off-by: Ichthyostega <prg@ichthyostega.de> --- doc/design/architecture/index.txt | 9 +- doc/design/architecture/playRender.txt | 132 +++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 doc/design/architecture/playRender.txt diff --git a/doc/design/architecture/index.txt b/doc/design/architecture/index.txt index 0d6996b8e..b061aa74c 100644 --- a/doc/design/architecture/index.txt +++ b/doc/design/architecture/index.txt @@ -8,6 +8,13 @@ the architecture of the application. See link:ArchitectureSummary.html[here] for a short summary of the core architecture ideas of the envisioned Lumiera Application. +.Rendering and Playback Control +This part of the architecture is concened with how to initiate, coordinate and control +the calculation processes necessary to generate visible / audible data from our source +media. This design is _not so much_ concerned with the actual calculations; indeed it +takes on an ``bird eye's view'', treating the actual render engine mostly just as an +opaque entity providing some service. + +-> link:playRender.html[Design: Playback and Rendering] - diff --git a/doc/design/architecture/playRender.txt b/doc/design/architecture/playRender.txt new file mode 100644 index 000000000..7fcbbab0b --- /dev/null +++ b/doc/design/architecture/playRender.txt @@ -0,0 +1,132 @@ +Design: Playback and Render Control +=================================== +:Author: Ichthyostega +:Date: 5/2011 + +//MENU: label Player subsystem + +************************************************************************************** +This part of the architecture is concened with how to initiate, coordinate and control +the calculation processes necessary to generate visible / audible data from our source +media. This design is _not so much_ concerned with the actual calculations; indeed it +takes on an ``bird eye's view'', treating the actual render engine mostly just as an +opaque entity providing some service. +************************************************************************************** + +Collecting initial drafts +------------------------- + +Over time, several design sketches emerged already, as an offspring of other +discussions and brainstorming. + + * the link:/documentation/devel/rfc_pending/EngineInterfaceOverview.html[Engine Interface draft] + contains some fundamental terms and definitions. + * discussions regarding time values and timecode handling shed some light onto the requirements + to be fulfilled by a player subsystem + + - link:http://article.gmane.org/gmane.comp.video.lumiera.general/2116[Timecode Widget and Framerates] + - link:http://article.gmane.org/gmane.comp.video.lumiera.general/2109[Time, Time-spans, Quatisation and Timecode] + - link:http://lumiera.org/wiki/renderengine.html#TimeQuant[Definition of Time entities] from the implementation + notes in the TiddlyWiki + + +As a start, quoting here from one of my responses in the above mentioned mailinglist threads: + +--------- +maybe I should have clarified the term "player subsystem". +That name is certainly shaped by "programmer think" ;-) + +The full, complete and precise name would be something along +the lines of "render- and playback-process coordination subsystem". +Now, we need to shorten that into a single word. The more obvious +abbreviation (just by the importance) would be "Render", but that +would be strikingly misleading, because everyone would take that +to be the render engine. Which leaves us with "Player". A second +consideration shows us that the structure of that subsystem will +be very close to the structure of any software player. This second +consideration supports that chosen name to be even better then "Render", +because "Player" is aligned with the actual nature of that subsystem. + +To expand on that structure, we need to think which powers are at +work and should be balanced. Here "powers" means powerful influences. +First of all, there is the spirit of modern software development, which +makes us think in terms of well-defined services, which are to be delivered +at a point-of-service, which is an interface (not necessarily the user +interface, but in most cases just an access point used by other internal +facilities). The next influential power is multiplicity, combined with +differentiation in detail. Then last but not least we have time-bound delivery. +Add to this the general requirement of keeping matters under control, which +is vital in a larger system with complex interactions. + +After realising these powers, for an experienced developer several building +blocks almost immediately fall into place, that is within the inner vision: + +We get instances of a "play/render-me"-service. At the front-end of each of +those instances, there will be a play-controller, which is a "state machine". +It provides states and transitions of the kind "play", "pause", "ffwd", "step", +etc., and can be hooked up with a play-control GUI widget (or something simpler +in case of a render process, which is free wheeling). + +Each play-controller in turn gets associated with several play/render-processes, +one for each independent media stream (channel) to be produced. Of course, +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 a "calculation +stream" service, which is capable of delivering a sequence of calculated +data frames in a timely fashion. + + +And now the point which is important for our discussion here: When a client +requests such an instance of the player service, we build up these parts +providing that service, which cascades down to the individual elements. +At that point, we need to pull and combine two informations: +- the "what" to render: this information stems from the session/model. +- the "how" to render: this information is guided by the output configuration. + + +Quantisation is a kind of rounding. This is a dangerous and nasty operation. + +<irony> the so called "ordinary" people have a completely misguided notion +and a bizarre inclination for rounding. In the pitifully shaded state of their +minds they link it to some kind of cleanliness </irony> + +Rounding and quantisation are dangerous, because they kill information content. +Thus there are three fundamental guidelines when it comes to rounding +(1) don't do it +(2) do it at most once +(3) do it as late as possible + +These may guide the process of finding the right place for the Quantiser(s) to +apply. We need some information flows to be joined in order to be able to do +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 "technically", + "get a clean state soon" -- and hasn't frame quantisation something to do + with media data, which is handled in the backend? + Well, actually, all of those are pitfalls to trap the unwary. About + cleanliness, well (sigh). Doing rounding soon will leave us with a huge + 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. +- rounding/quantising in the GUI is extremely common; unfortunately there + seems to be not a single argument supporting that habit. Most of all, + it violates the subsidiarity principle. + +Which leaves us with the player and the session. Both positions could +arguably be supported. I come to prefering the player mostly out of +gut feeling. In any case there seems to be a remaining tension. + +But on a second thought and more careful consideration, the "frame rounding" +can be decomposed in the way I proposed: 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 +the output). This leads to the proposed approach of a collaboration, +which seems to resolve that tension (at least when considering it +now, from birds eyes view) +--------- + From e12d99c6ff9b92021501bfb518f46b667086df8d Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 10 May 2011 02:49:31 +0200 Subject: [PATCH 032/496] expand on the requiremenst for the player subsystem Signed-off-by: Ichthyostega <prg@ichthyostega.de> --- doc/design/architecture/playRender.txt | 86 +++++++++++++++++--------- 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/doc/design/architecture/playRender.txt b/doc/design/architecture/playRender.txt index 7fcbbab0b..42be0a952 100644 --- a/doc/design/architecture/playRender.txt +++ b/doc/design/architecture/playRender.txt @@ -16,7 +16,7 @@ opaque entity providing some service. Collecting initial drafts ------------------------- -Over time, several design sketches emerged already, as an offspring of other +At start of 2011, several design sketches emerged already, as an offspring of other discussions and brainstorming. * the link:/documentation/devel/rfc_pending/EngineInterfaceOverview.html[Engine Interface draft] @@ -26,37 +26,66 @@ discussions and brainstorming. - link:http://article.gmane.org/gmane.comp.video.lumiera.general/2116[Timecode Widget and Framerates] - link:http://article.gmane.org/gmane.comp.video.lumiera.general/2109[Time, Time-spans, Quatisation and Timecode] - - link:http://lumiera.org/wiki/renderengine.html#TimeQuant[Definition of Time entities] from the implementation - notes in the TiddlyWiki + - link:http://lumiera.org/wiki/renderengine.html#TimeQuant[Definition of Time entities] + from the implementation notes in the TiddlyWiki -As a start, quoting here from one of my responses in the above mentioned mailinglist threads: - ---------- -maybe I should have clarified the term "player subsystem". -That name is certainly shaped by "programmer think" ;-) +Reasoning about the Player Subsystem's structure +------------------------------------------------ +At first sight, the name ``Player'' might be surprising. The full, complete and precise name would be something along -the lines of "render- and playback-process coordination subsystem". +the lines of ``_render- and playback-process coordination subsystem_''. Now, we need to shorten that into a single word. The more obvious -abbreviation (just by the importance) would be "Render", but that +abbreviation (just by the importance) would be _Render_, but that would be strikingly misleading, because everyone would take that -to be the render engine. Which leaves us with "Player". A second +to be the render engine. Which leaves us with _Player_. A second consideration shows us that the structure of that subsystem will -be very close to the structure of any software player. This second -consideration supports that chosen name to be even better then "Render", -because "Player" is aligned with the actual nature of that subsystem. +be very close to the structure of any software player -- the chosen term +turns out to be well aligned with the actual nature of that subsystem. -To expand on that structure, we need to think which powers are at -work and should be balanced. Here "powers" means powerful influences. -First of all, there is the spirit of modern software development, which -makes us think in terms of well-defined services, which are to be delivered -at a point-of-service, which is an interface (not necessarily the user -interface, but in most cases just an access point used by other internal -facilities). The next influential power is multiplicity, combined with -differentiation in detail. Then last but not least we have time-bound delivery. -Add to this the general requirement of keeping matters under control, which -is vital in a larger system with complex interactions. +Influences and Requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In accordance with the spirit of modern software development, the analysis +starts by determining which would be the _Service_ to be provided by such +a player. Consideration of the use cases highlights the fundamental forces +to be _multiplicity_, combined with _differentiation in detail_, all under +the government of _time-bound delivery_, combined with _live reconfiguration_. + +The basic service turns out to be *performance of a preconfigured object model*. +This performance is *time based*. Multiple usage instances of this service can +be expected to coexist, each of which can be broken down into a set of *elementary +streams to be delivered synchronously*. The delivery mode can be *reconfigured +on-the-fly*. + +Modes of operation +^^^^^^^^^^^^^^^^^^ +Delivery can be either _free-wheeling_, so to cover a predefined time interval +with fixed quality calculations (final render), and it can be _throttled_ for using +excess computation power (background rendering). Or -- alternatively -- delivery can +be _time-bound_ (for classical playback), following the projection of wall clock time +onto a predefined timeline interval (the so called ``playhead'' or ``playback cursor'' +proceeding along a timeline). + +Reconfiguration +^^^^^^^^^^^^^^^ +Some of these operation modes need to be prepared to 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 +- the linearly proceeding playback can at any time be + + * paused + * reversed in direction + * playback speed adjusted + +- the playback can be looped, with _unlimited_ adjustments of the loop boundaries any time. + + +Resulting Structure +~~~~~~~~~~~~~~~~~~~ After realising these powers, for an experienced developer several building blocks almost immediately fall into place, that is within the inner vision: @@ -93,9 +122,10 @@ minds they link it to some kind of cleanliness </irony> Rounding and quantisation are dangerous, because they kill information content. Thus there are three fundamental guidelines when it comes to rounding -(1) don't do it -(2) do it at most once -(3) do it as late as possible + +. don't do it +. do it at most once +. do it as late as possible These may guide the process of finding the right place for the Quantiser(s) to apply. We need some information flows to be joined in order to be able to do @@ -128,5 +158,5 @@ to form the frame grid, but it is lacking crutial information (about the output). This leads to the proposed approach of a collaboration, which seems to resolve that tension (at least when considering it now, from birds eyes view) ---------- + From c05c9f6fb3ca0408bf5e91e4895aa9a2771fb84e Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 11 May 2011 03:08:27 +0200 Subject: [PATCH 033/496] finish player subsystem design document Signed-off-by: Ichthyostega <prg@ichthyostega.de> --- doc/design/architecture/playRender.txt | 131 +++++++++++++++---------- 1 file changed, 80 insertions(+), 51 deletions(-) diff --git a/doc/design/architecture/playRender.txt b/doc/design/architecture/playRender.txt index 42be0a952..3b062d992 100644 --- a/doc/design/architecture/playRender.txt +++ b/doc/design/architecture/playRender.txt @@ -53,7 +53,7 @@ a player. Consideration of the use cases highlights the fundamental forces to be _multiplicity_, combined with _differentiation in detail_, all under the government of _time-bound delivery_, combined with _live reconfiguration_. -The basic service turns out to be *performance of a preconfigured object model*. +The basic service turns out to be the *performing of a preconfigured object model*. This performance is *time based*. Multiple usage instances of this service can be expected to coexist, each of which can be broken down into a set of *elementary streams to be delivered synchronously*. The delivery mode can be *reconfigured @@ -84,43 +84,74 @@ driven by user interactions: - the playback can be looped, with _unlimited_ adjustments of the loop boundaries any time. -Resulting Structure -~~~~~~~~~~~~~~~~~~~ -After realising these powers, for an experienced developer several building -blocks almost immediately fall into place, that is within the inner vision: +Conclusions for the Design +~~~~~~~~~~~~~~~~~~~~~~~~~~ -We get instances of a "play/render-me"-service. At the front-end of each of -those instances, there will be a play-controller, which is a "state machine". -It provides states and transitions of the kind "play", "pause", "ffwd", "step", -etc., and can be hooked up with a play-control GUI widget (or something simpler -in case of a render process, which is free wheeling). +Based on these observations, the following design looks pretty much obvious: -Each play-controller in turn gets associated with several play/render-processes, -one for each independent media stream (channel) to be produced. Of course, -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 a "calculation -stream" service, which is capable of delivering a sequence of calculated +Overall, the player subsystem can be described as ``play/render-this''-service. +Given an suitable (high-level) object, the Player has the ability to ``perform'' +(play or render) it. + +- the standard case is _playing a timeline_. +- it's conceivable to allow playing a selection of other objects, + like e.g. directly playing a clip or even media asset. In these cases, + its the player's job to prepare the necessary scaffolding on the fly. + +Yet each such performance of an object is a _stateful instance_, a player application: +On application of the player service, the client gets a front-end handle, a *play-controller*, +which is a _state machine_. It provides states and transitions of the kind 'play', 'pause', 'ffwd', +'rew', 'goto', 'step', 'scrub' and similar. Moreover it maintains (or connects to) a distinct +playback location, and it can be hooked up with a play-control GUI widget +(or something simpler in case of a render process, which is free wheeling). + +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 +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 +a *calculation stream service*: the ability to deliver a sequence of calculated data frames in a timely fashion. +When a client requests such an instance of the player service, we build up these +parts providing that service, which cascades down to the individual elements. +At that point, we need to pull and combine two kinds of informations: -And now the point which is important for our discussion here: When a client -requests such an instance of the player service, we build up these parts -providing that service, which cascades down to the individual elements. -At that point, we need to pull and combine two informations: -- the "what" to render: this information stems from the session/model. -- the "how" to render: this information is guided by the output configuration. +- the ``what'' to render: this information stems from the session/model. +- the ``how'' to render: this information is guided by the derived output configuration. +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. +Thus, a viewer connection is prerequisite for creating a player instance. -Quantisation is a kind of rounding. This is a dangerous and nasty operation. +Viewer connections exist as associations in the session/model -- as entities separate +from the player. Usually, a timeline has (at least) one viewer connection. But in case +such a connection is (still) missing, building a player instance recurs to the session +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. -<irony> the so called "ordinary" people have a completely misguided notion -and a bizarre inclination for rounding. In the pitifully shaded state of their -minds they link it to some kind of cleanliness </irony> +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: +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 +reconfigure the backend services. + +Frame quantisation +^^^^^^^^^^^^^^^^^^ +Quantisation is a kind of rounding; like any kind of rounding, quantisation is +a dangerous operation because it kills information content. -Rounding and quantisation are dangerous, because they kill information content. Thus there are three fundamental guidelines when it comes to rounding . don't do it @@ -133,30 +164,28 @@ 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 "technically", - "get a clean state soon" -- and hasn't frame quantisation something to do + We can ``do away'' with nasty things soon, especially if they are technicallities, + ``get a clean state soon'' -- and hasn't frame quantisation something to do with media data, which is handled in the backend? - Well, actually, all of those are pitfalls to trap the unwary. About - cleanliness, well (sigh). Doing rounding soon will leave us with a huge - 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. -- rounding/quantising in the GUI is extremely common; unfortunately there - seems to be not a single argument supporting that habit. Most of all, - it violates the subsidiarity principle. ++ +Well, actually, all of those are pitfalls to trap the unwary. About +cleanliness, well (sigh). Doing rounding soon will leave us with a huge +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. + +- rounding/quantising in the GUI is extremely common within media applications; + unfortunately there seems to be not a single rational argument supporting that habit. + Most of all, it violates the subsidiarity principle. Which leaves us with the player and the session. Both positions could -arguably be supported. I come to prefering the player mostly out of -gut feeling. In any case there seems to be a remaining tension. - -But on a second thought and more careful consideration, the "frame rounding" -can be decomposed in the way I proposed: 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 -the output). This leads to the proposed approach of a collaboration, -which seems to resolve that tension (at least when considering it -now, from birds eyes view) - +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 +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. From 3c9c145f7bc24ff87a55efd110cf6f55001e22f4 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 11 May 2011 21:06:25 +0200 Subject: [PATCH 034/496] promote the Engine Interface Overview to draft state --- doc/design/architecture/playRender.txt | 10 ++-- .../rfc_pending/EngineInterfaceOverview.txt | 57 +++++++++++++------ 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/doc/design/architecture/playRender.txt b/doc/design/architecture/playRender.txt index 3b062d992..afcf2b61f 100644 --- a/doc/design/architecture/playRender.txt +++ b/doc/design/architecture/playRender.txt @@ -40,23 +40,23 @@ Now, we need to shorten that into a single word. The more obvious abbreviation (just by the importance) would be _Render_, but that would be strikingly misleading, because everyone would take that to be the render engine. Which leaves us with _Player_. A second -consideration shows us that the structure of that subsystem will -be very close to the structure of any software player -- the chosen term -turns out to be well aligned with the actual nature of that subsystem. +consideration highlights similarities with the structure of a +typical software player -- the chosen term turns out to be well +aligned with the actual nature of that subsystem. Influences and Requirements ~~~~~~~~~~~~~~~~~~~~~~~~~~~ In accordance with the spirit of modern software development, the analysis starts by determining which would be the _Service_ to be provided by such -a player. Consideration of the use cases highlights the fundamental forces +a player. Consideration of the use cases determines the fundamental forces to be _multiplicity_, combined with _differentiation in detail_, all under the government of _time-bound delivery_, combined with _live reconfiguration_. The basic service turns out to be the *performing of a preconfigured object model*. This performance is *time based*. Multiple usage instances of this service can be expected to coexist, each of which can be broken down into a set of *elementary -streams to be delivered synchronously*. The delivery mode can be *reconfigured +streams to be delivered in sync*. The parameters of delivery can be *reconfigured on-the-fly*. Modes of operation diff --git a/doc/devel/rfc_pending/EngineInterfaceOverview.txt b/doc/devel/rfc_pending/EngineInterfaceOverview.txt index 7e89c4a9c..d33461893 100644 --- a/doc/devel/rfc_pending/EngineInterfaceOverview.txt +++ b/doc/devel/rfc_pending/EngineInterfaceOverview.txt @@ -1,6 +1,12 @@ +Engine Interface Overview +========================= +:Date: 2010 + +// please don't remove the //word: comments + [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Draft_ *Date* _2010-04-16_ *Proposed by* link:Ichthyostega[] ------------------------------------- @@ -8,11 +14,14 @@ Overview Engine Interface(s) ---------------------------- + +******************************************************************************** At the Engine Interfaces, Lumiera's Backend and Session get connected and work together to produce rendered output. This design proposal intends to give an overview of the connection points and facilities involved, to define some terms and concepts and to provide a foundation for discussion and working out the APIs in detail. +******************************************************************************** @@ -26,8 +35,7 @@ Participants *Scheduler*:: cares for calculations actually to happen, in the right order and just in time, if at all *Node*:: abstraction of an processing unit, supports planning by the - dispatcher, + - allows to pull data, thereby driving the actual calculation. + dispatcher, allows to pull data, thereby driving the actual calculation. Render Process ~~~~~~~~~~~~~~ @@ -62,7 +70,7 @@ identifier and _channel selector_. timings:: a definition to translate global model time units in real clock time, - including _alignment_ to an external time grid. + including _alignment_ to an external _time grid_. model port:: a point in the (high level) model where output can be produced. + @@ -166,7 +174,7 @@ prerequisites) Node Invocation API ~~~~~~~~~~~~~~~~~~~ As nodes are stateless, they need to be embedded into an invocation context in -order to be of any use. + The node invocation has two distinct stages and thus +order to be of any use. The node invocation has two distinct stages and thus the invocation API can be partitioned in two groups Planning @@ -175,8 +183,8 @@ During the planning phase, the dispatcher retrieves various informations necessary to _schedule_ the following pull call. These informations include * reproducible invocation identifier, usable to label frames for caching - * opaque source identifier (owned by the backed) when this node represents a - source + * opaque source identifier (owned by the backed) in case this node + represents a source * prerequisite nodes * index (channel) of the prerequisite's output to be fed as input buffer(s) * number and size of the output buffers required @@ -201,11 +209,13 @@ Node pull Tasks ~~~~~ * find out if we need to support variable framerate - * find out about the exact handling of multichannel data streams - * design a buffer descriptor - * design a buffer designation scheme - * expand on the node identification scheme - * clarify how control data frames can be addressed + ([green]#-> yes, implementation deferred#) + * find out about the exact handling of multichannel data streams ([green]#✔ done#) + * design and prototypical implementation of frame quantisation ([green]#✔ done#) + * design a buffer descriptor [red]#TODO# + * design a buffer designation scheme [red]#TODO# + * expand on the node identification scheme [red]#TODO# + * clarify how control data frames can be addressed [red]#TODO# Discussion @@ -229,17 +239,28 @@ Rationale +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: + + + Comments -------- +//comments: append below -//////////////////// +.State -> Draft +Requirements and details of the design are sufficiently clear meanwhile. +Ther seems to be not much room for alternative approaches, given our +general planning for the application -Conclusion -~~~~~~~~~~ -* *accepted* / *dropped* by MMMMMM.YYYY developer meeting. - -//////////////////// + Mi 11 Mai 2011 19:27:12 CEST Ichthyostega <prg@ichthyostega.de> +//endof_comments: + +'''' Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] + From c49c7b5fdde2b45f1d87fd66d2589d03263b9e1a Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 11 May 2011 21:06:45 +0200 Subject: [PATCH 035/496] new RfC: Engine Interface Specification --- doc/devel/rfc_pending/EngineInterfaceSpec.txt | 219 ++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 doc/devel/rfc_pending/EngineInterfaceSpec.txt diff --git a/doc/devel/rfc_pending/EngineInterfaceSpec.txt b/doc/devel/rfc_pending/EngineInterfaceSpec.txt new file mode 100644 index 000000000..f4278929a --- /dev/null +++ b/doc/devel/rfc_pending/EngineInterfaceSpec.txt @@ -0,0 +1,219 @@ +Engine Interface Spec +===================== + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _Mi 11 Mai 2011 17:53:16 CEST_ +*Proposed by* Ichthyostega <prg@ichthyostega.de> +------------------------------------- + +[abstract] +******************************************************************************** +The main service of the Renderengine is to deliver a stream of calculations +bound by timing constraints. The parameters of this delivery can be reconfigured +******************************************************************************** + +Try to start the integration and overall design of the Lumiera Render Engine. +Point of reference is the functionality other parts of the application are relying on. + +Description +----------- +//description: add a detailed description: +The Engine is driven by the Player subsystem and draws on the low-level Model +(Render nodes network) for all local parameters and control data. The goal is +to deliver all the typical playback and rendering operations commonly found +in NLE applications (functional requirements). Moreover, this functionality +shall be delivered in a robust and reliable fashion, while making optimal +use of the available I/O bandwidth and computation power (non-functional +requirements). + +Requirements Specification +~~~~~~~~~~~~~~~~~~~~~~~~~~ +.functional +- simple calculation stream +- with or without defined end point +- deliver to fixed output buffer(s) with high timing precision +- ability to deliver individual data chunks (e.g. single frames) +- ``free wheeling'' operation for maximum calculation throughput +- throttled operation using idle calculation or bandwidth resources +- streams with regular stepping and arbitrary block sizes +- looping, seamless chaining of calculation streams with ongoing timing +- ability to pause and to change / combine any of the above any time +- ability to abort or change, providing reliable feedback on completion +- ability to relocate (shift in time) parts of an ongoing calculation stream +- support for chase-and-lock synchronisation + +.non-functional +- protection against overload and deadlocks +- gracious degradation in case of problems +- maintain a configurable quality-of-service level +- utilise precisely the resources actually available + +Functionality description in detail +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-> see the link:EngineInterfaceOverview.html[Engine/Interface overview] for +a description of the involved entities and for definitions for common terms. + +Definitions +^^^^^^^^^^^ +Calculation Stream:: + A series of similar but parametrised calculations, + bound to deliver results in sequence and in accordance to timing constraints +Timed Delivery:: + Calculation result data has to be delivered within a fixed time interval + immediately preceding the delivery deadline, so it can be consumed by + an (possibly external) output process without the need for further buffering +Output Slot:: + An abstracted facility receiving calculated data; including a scheme + to organise the output buffers, which get handed over to an independent + thread running in parallel, or maybe even to an external process + +Operation description +^^^^^^^^^^^^^^^^^^^^^ +When *creating a calculation stream*, the _exit node_ and an already opened +_output slot_ are provided, plus the _timing parameters_ (frame duration, +stepping interval length, optional start and/or endtime) + +A *free wheeling calculation stream* is created in a similar fashion, just +without timing constraints on the output delivery; i.e. just the output slot +is parametrised differently. This invocation is used to create a ``mixdown'' +or ``final render'' to be saved into an output file. + +A *background rendering mandate* is created analogous, but _without_ providing +an output slot. Rather, it is expected that the engine will cache the generated +data internally. + +When calculation stream definitions are *chained*, the follow-up calculation +stream is expected to be delivered seamlessly after the preceding stream, +without interrupting the output timings. + +*Looping* is a special kind of chained calculations, where the same segment +is delivered continuously. But note, the loop boundaries are not necessarily +aligned with the frame spacing or the output timing requirements. + +For supporting *chase-and-lock*, the engine needs a mechanism to follow an +externally provided synchronisation goal, _without_ altering the output +delivery timings. Obviously, we need to build in a strategy for handling +this problem (because the solution is bound to be different for different +kinds of media). + +The engine can expect the output slot to support *de-clicking* or *flicker +protection* -- yet the engine needs to signal precisely when this is necessary + +Quality of service +^^^^^^^^^^^^^^^^^^ +The Engine is expected to provide different quality-of-service classes, +which are requested as part of the definition parameters for a calculation stream. + +- 'SYNC_PRIORITY' means to keep up to the delivery requirements, + even if this means failing to deliver data altogether. +- 'PERFECT_RESULT' means to deliver data perfect up to the definition, + even if this means violating the timing constraints. +- 'COMPROMISE' allows the engine to take some shortcuts in order to + deliver an roughly satisfactory behaviour. Likely there will be + multiple classes of compromise. + +The quality of service is partially implemented directly by the engine +and partially passed on as parameter to the individual node invocations. +For example, the engine might decide to switch down to proxy media, while +actually the node network will perform the actual switch and reconfiguration. + +Reconfiguration and abort +^^^^^^^^^^^^^^^^^^^^^^^^^ +There needs to be a separate ``control channel'' to cause various reconfigurations +during an ongoing calculation process. With the exception of the output slot, all +parameters defining an calculation stream might be changed on-the-fly -- including +the possibility to abort calculation altogether. + +The engine is _not required_ to react on such change requests immediately or +synchronously. The goal is rather to integrate such changes seamlessly. +Yet we _require_... + +- a guarantee that the change request is observed within some tolerance interval + (i.e. we may block waiting on the change to happen, without risking a deadlock) +- a reliable feedback _after_ the change has happened, by invoking a response signal + (functor/callback provided with the change request) +- a guarantee not to proceed with the original setup after this signalling + (read: after receiving this feedback, resources required only by the initial + setup may be deallocated) + +Especially note that the following things might be changed in the middle of an +ongoing calculation: + +- timing parameters of the calculation stream (frame durations, stepping interval) +- start and end time +- splitting and chaining of calculation streams (e.g introducing jumps) +- adjust the looping boundaries +- toggle _paused_ state +- change the exit node to use for pulling +- relocate the nominal time position of parts of the calculation stream; + especially we expect already calculated and cached data to be re-labeled +- invalidate parts of the (nominal) time axis, forcing recalculation +- abort individual calculation streams without interfering with others. + + +Tasks +~~~~~ +// List what needs to be done to implement this Proposal: +* analyse requirements of the player subsystem ([green]#✔ done#) +* determine further informations needed during calculation [,yellow]#WIP# +* find out about timing requirements and constraints in detaill [red]#TODO# +* define the interface functions in detail [red]#TODO# +* prepare a test fixture with mock-up calculations [red]#TODO# +* implement the invocation backbone with stubbed functionality [red]#TODO# + + +Discussion +~~~~~~~~~~ + +Pros +^^^^ +// add a fact list/enumeration which make this suitable: +// * foo +// * bar ... + + + +Cons +^^^^ +// fact list of the known/considered bad implications: +The requirements placed on life changes are quite high + + + +Alternatives +^^^^^^^^^^^^ +//alternatives: explain alternatives and tell why they are not viable: + + + +Rationale +--------- +//rationale: Give a concise summary why it should be done *this* way: +The expectations for the playback and render functionality of a NLE are +pretty much set. There isn't much room for reducing functionality. +So the goal for this RfC is to precisely define the inevitable +and break it down into tangible functionality on the implementation level. + + + +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: + + + + +Comments +-------- +//comments: append below + + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] From 0f4c88bb0f9659454274178f6e3e78f206fcdbf1 Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Thu, 12 May 2011 19:30:36 +0200 Subject: [PATCH 036/496] Fix treeinfo.sh sorting order only sort by the first field (that is the filename) --- admin/treeinfo.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/admin/treeinfo.sh b/admin/treeinfo.sh index 5108d2e0a..b51d9f688 100755 --- a/admin/treeinfo.sh +++ b/admin/treeinfo.sh @@ -1,2 +1,3 @@ #!/bin/sh -find -name DIR_INFO -printf '%-30h: ' -exec head -1 {} \; | sort | cut -c 1-92 +COLUMNS=$(tput cols) +find -name DIR_INFO -printf '%-30h: ' -exec head -1 {} \; | sort -k1,1 | cut -c 1-${COLUMNS:-92} From 4d7ac84c60fed246441b430010c45e2d2c18b03c Mon Sep 17 00:00:00 2001 From: Christian Thaeter <ct@pipapo.org> Date: Thu, 12 May 2011 20:01:33 +0200 Subject: [PATCH 037/496] trying to symlink the README from the doc --- doc/readme.txt | 1 + 1 file changed, 1 insertion(+) create mode 120000 doc/readme.txt diff --git a/doc/readme.txt b/doc/readme.txt new file mode 120000 index 000000000..59a23c461 --- /dev/null +++ b/doc/readme.txt @@ -0,0 +1 @@ +../README \ No newline at end of file From 54d1e4336cf4e650c43a11599d76100ae50df2e3 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 15 May 2011 00:30:25 +0200 Subject: [PATCH 038/496] symlink to README doesn't make much sense... there is index.txt already for that purpose (overview of documentation section), while README is for the general source tree, and not intended for the website --- doc/readme.txt | 1 - 1 file changed, 1 deletion(-) delete mode 120000 doc/readme.txt diff --git a/doc/readme.txt b/doc/readme.txt deleted file mode 120000 index 59a23c461..000000000 --- a/doc/readme.txt +++ /dev/null @@ -1 +0,0 @@ -../README \ No newline at end of file From f435e8d19c0826f9948c393dd6226cc297ab5f57 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 15 May 2011 01:26:51 +0200 Subject: [PATCH 039/496] Update dev meeting TOC, add raw IRC transcript of May meeting Signed-off-by: Ichthyostega <prg@ichthyostega.de> --- doc/devel/meeting_summary/2011-05-11.txt | 938 ++++++++++++++++++ doc/devel/meeting_summary/index.txt | 42 +- doc/devel/rfc_pending/EngineInterfaceSpec.txt | 15 + .../YYYY-MM-DD-developerMeetingSummary.txt | 2 +- 4 files changed, 973 insertions(+), 24 deletions(-) create mode 100644 doc/devel/meeting_summary/2011-05-11.txt diff --git a/doc/devel/meeting_summary/2011-05-11.txt b/doc/devel/meeting_summary/2011-05-11.txt new file mode 100644 index 000000000..b3aeec242 --- /dev/null +++ b/doc/devel/meeting_summary/2011-05-11.txt @@ -0,0 +1,938 @@ +2011-05-11 Lumiera Developers Meeting +===================================== +:Author: Ichthyostega +:Date: 2012-05-15 + +May 11, 2011 on #lumiera 20:00 - 23:23 UTC + + +__Participants__ + + * cehteh + * ichthyo + +_Transcript prepared by Ichthyo_ + + + +Render Engine Interface +----------------------- +_Ichthyo_ had prepared a new +link:/documentation/devel/rfc_pending/EngineInterfaceSpec.html[RfC] +about the actual interface between the +link:/documentation/design/architecture/playRender.html[Player subsystem] +and the Renderengine/Scheduler. +The following discussion turned into considering several detaled and technical +topics regarding the Jobs to be scheduled, how to create and attach new jobs, +handle memory etc. + +Conclusion +~~~~~~~~~~ +_Ichthyo_ will take on responsibility for that interface and care to translate +the calls into actual Jobs to be handed to the Scheduler. The Scheduler interface +itself will remain rather low-level and straight forward. The _callback_ facilities +described in the mentioned RfC need to be implemented explicitly on top of this +Scheduler interface. Ichthyo will care for that, and the integration with the +Fixture datastructure. + +Next meeting +------------ + +The next meeting will be as usual, at Wednesday June 8, 20:00 UTC + + +'''' + + +.-- Discussion of details -- +[caption="☉Transcript☉ "] +---------------------------- +[2011-05-11 21:08:43] [INFO] Now logging to <file:///Lager/heim/profi/fox/chatzilla/logs/kornbluth.freenode.net/channels/%23lumiera.2011-05-11.log>. +[2011-05-11 21:08:43] [INFO] Channel view for ``#lumiera'' opened. +[2011-05-11 21:08:43] === 7 unknown connection(s) +[2011-05-11 21:08:43] =-= User mode for ichthyo is now +i +[2011-05-11 21:08:44] *NickServ* This nickname is registered. Please choose a different nickname, or identify via /msg NickServ identify <password>. +[2011-05-11 21:08:47] *NickServ* You are now identified for ichthyo. +[2011-05-11 21:08:50] -->| YOU (ichthyo) have joined #lumiera +[2011-05-11 21:08:50] =-= Topic for #lumiera is ``Lumiera, the new emerging NLE for GNU/Linux | http://www.lumiera.org | Become a develper NOW'' +[2011-05-11 21:08:50] =-= Topic for #lumiera was set by hermanr on Sa 12 Sep 2009 21:08:01 CEST +[2011-05-11 21:09:01] <ichthyo> Hello folks +[2011-05-11 21:09:24] <ichthyo> dev meeting in 1 hour? +[2011-05-11 21:11:26] <ichthyo> anyway... finished my RfC +[2011-05-11 21:11:42] <ichthyo> http://lumiera.org/documentation/devel/rfc_pending/EngineInterfaceSpec.html +[2011-05-11 21:12:01] <ichthyo> I'll be away shortly, but back in time for 20:00 UTC +[2011-05-11 21:12:30] [INFO] You are now marked as away (evening stroll). Click the nickname button or use the |/back| command to return from being away. +[2011-05-11 21:32:11] -->| hermanr (~herman@cinelerra.org) has joined #lumiera +[2011-05-11 21:46:18] <hermanr> Meeting in 15? +[2011-05-11 21:46:24] <Velmont> 14 +[2011-05-11 21:46:30] <hermanr> :-) +[2011-05-11 21:47:36] <SimAV> 12! +[2011-05-11 21:49:37] <SvenL-> Hi hermanr, ichthyo, Velmont and SimAV ! +[2011-05-11 21:49:43] <Velmont> hey SvenL-! +[2011-05-11 21:49:59] <hermanr> Good evening, SvenL-! +[2011-05-11 21:50:11] <cehteh> 10 +[2011-05-11 21:50:31] <SimAV> 9 +[2011-05-11 21:50:58] * hermanr is just lurking, out of curiosity +[2011-05-11 21:51:17] * SimAV is busy, as always +[2011-05-11 21:52:35] * SvenL- is hungry +[2011-05-11 21:55:24] <Velmont> http://google.com -- and the sprite: http://www.google.no/logos/2011/graham11-hp-sprite.png -- nice work! +[2011-05-11 21:57:54] <SvenL-> It's not a gif, png don't move, not Flash, what is it ? I wondered about that when I first saw it... +[2011-05-11 21:58:00] <SvenL-> HTML5 ? +[2011-05-11 21:59:05] <hermanr> SvenL-: There are animated PNGs, yes. It's a more capable standard than animated GIFs, but not as widely supported. +[2011-05-11 21:59:19] <cehteh> javascript :P +[2011-05-11 21:59:22] <SvenL-> Oh ?! Okay +[2011-05-11 21:59:24] * ichthyo back +[2011-05-11 21:59:28] [INFO] You are no longer marked as away. +[2011-05-11 21:59:32] <ichthyo> Hi all +[2011-05-11 21:59:34] <hermanr> Yeah, prolly JavaScript. +[2011-05-11 21:59:45] <hermanr> 'evening, ichthyo +[2011-05-11 21:59:50] <hermanr> -1 minute +[2011-05-11 21:59:51] <ichthyo> 'evening hermanr +[2011-05-11 21:59:55] <SvenL-> Forgot to mention it +[2011-05-11 22:00:03] <ichthyo> how embarrasing +[2011-05-11 22:00:08] <ichthyo> dong dong +[2011-05-11 22:00:10] <SvenL-> Reminds me of Pina, the new Wenders' movie. Anybody saw it ? +[2011-05-11 22:00:34] <cehteh> wb ichthyo +[2011-05-11 22:00:36] <Velmont> SvenL-: I gave you the answer, the sprite. :-) +[2011-05-11 22:01:05] <Velmont> It is javascript who does the moving etc, yes. So CSS+Javascript mostly. :-) +[2011-05-11 22:01:32] <SvenL-> Of course, JavaScript, okay +[2011-05-11 22:01:48] <Velmont> But very cool, very fast. +[2011-05-11 22:02:01] <Velmont> Worked on my lowfi computer quick as a fox :] +[2011-05-11 22:02:15] <cehteh> well its not big +[2011-05-11 22:02:24] <cehteh> try that with a fullscreen movie :P +[2011-05-11 22:02:43] <cehteh> and i bet it break on almost any non-mainstream browser +[2011-05-11 22:02:48] <Velmont> Yesyes, -- but I mean, if they'd done it using a flash embed, it wouldn't have been as nice. +[2011-05-11 22:03:13] <cehteh> well .. html5 would be the best way :) +[2011-05-11 22:03:28] <Velmont> Nah, html5 video would also be slower. +[2011-05-11 22:03:31] <Velmont> Not so graceful ;] +[2011-05-11 22:03:41] <cehteh> and fallback to anim gif +[2011-05-11 22:03:58] <Velmont> This start in an instant. --A video would be much more heavy +[2011-05-11 22:04:08] <cehteh> didnt worked for me at a first try because i have js turned off for google +[2011-05-11 22:04:57] <cehteh> ok ... lets start the devel meeting +[2011-05-11 22:05:28] <cehteh> ichthyo: anything else than your engine interface proposal? +[2011-05-11 22:05:57] <ichthyo> maybe fsiddi shows up? +[2011-05-11 22:06:04] -->| daylife (~daylife@5350C42D.cm-6-1d.dynamic.ziggo.nl) has joined #lumiera +[2011-05-11 22:06:15] <cehteh> dunno, if yes then we can talk about something there +[2011-05-11 22:06:18] <cehteh> hey daylife +[2011-05-11 22:06:26] <ichthyo> hi daylife +[2011-05-11 22:06:43] <cehteh> btw www.pipapo.org is back, using the lumiera web infrastructure as well :) +[2011-05-11 22:06:45] <SvenL-> Didn't have the time to read your new pages, ichthyo +[2011-05-11 22:06:46] <ichthyo> well... he wrote some days ago that he is busy, but will continue with the new layout +[2011-05-11 22:06:52] <daylife> hello guys +[2011-05-11 22:07:00] * ichthyo also promised to do something for the template +[2011-05-11 22:07:03] <cehteh> i talked with him a few words past days ... +[2011-05-11 22:07:10] <ichthyo> and didn't get around to that yet +[2011-05-11 22:07:12] <ichthyo> ah +[2011-05-11 22:07:31] <cehteh> also xsinick wants to help the project, dunno when he will be back here (non programmer) +[2011-05-11 22:07:55] <cehteh> http://radar.oreilly.com/2011/05/process-kills-developer-passion.html << grin +[2011-05-11 22:08:43] <ichthyo> well... we could discuss about process, if you prefer that +[2011-05-11 22:08:46] <ichthyo> :-P +[2011-05-11 22:08:54] <cehteh> ichthyo: how do we want to proceed with the engine interface, discuss it point by point or only a overview and edit comments in the rfc? +[2011-05-11 22:09:08] <ichthyo> well I thought rather the latter +[2011-05-11 22:09:12] <ichthyo> its a rough draft +[2011-05-11 22:10:12] <cehteh> nah i think we are fine with process .. including your coding standards pushing, my reluctance in some detail, .. my formalism and your pushbacks there :) +[2011-05-11 22:10:13] <SvenL-> Reading the first lines is enough to remind me of the basic concepts down here in the low-level model haha +[2011-05-11 22:10:52] <cehteh> well generally i agree with your proposal (i'm just reading it) with some refinements/ideas i want to add +[2011-05-11 22:10:53] <ichthyo> :) +[2011-05-11 22:10:58] <ichthyo> ok +[2011-05-11 22:11:08] <ichthyo> and while we at it +[2011-05-11 22:11:27] <ichthyo> last time we went through the RfCs, and there is another Rfc, which is closely related +[2011-05-11 22:11:31] <ichthyo> but more high-level +[2011-05-11 22:11:38] <ichthyo> http://lumiera.org/documentation/devel/rfc_pending/EngineInterfaceOverview.html +[2011-05-11 22:11:59] <cehteh> yes but you are right that we need to go into details soon +[2011-05-11 22:12:48] <ichthyo> well, probably the most important new view angle is that I looked on all those 'play mode changes' +[2011-05-11 22:12:51] <cehteh> QoS: do we want constants there or could you think about a 'functor' which reacts more dynamic? +[2011-05-11 22:13:02] <ichthyo> of course +[2011-05-11 22:13:20] <cehteh> well your proposal sounded like constants (to be defined) +[2011-05-11 22:13:35] <ichthyo> yes, its constants, but it seems natural to extend that +[2011-05-11 22:13:47] <cehteh> i think about a functor or even a trait-class where you can query things +[2011-05-11 22:14:19] <cehteh> this class can then react more dynamic +[2011-05-11 22:14:22] <ichthyo> ok, yes, so that you could e.g. combine several independent degrees of freedom +[2011-05-11 22:14:31] <cehteh> yes +[2011-05-11 22:16:02] <cehteh> then we can ask precisely "shall we render this frame now?" .. instead generally "do we render frames under timing pressure?" +[2011-05-11 22:16:47] <cehteh> and of course also other questions, accuracy, syncronization and so on +[2011-05-11 22:18:17] <cehteh> the other thing is about sequences ... the backend delivers only single frames, you set up relations (this frame comes after that frame) +[2011-05-11 22:18:44] <cehteh> i would like if we can put prefetching and driving sequences into the player/scheduler and not in the backend at all +[2011-05-11 22:18:56] <ichthyo> yeah... I figured that there is some discrepancy +[2011-05-11 22:19:33] <cehteh> well for me there is no much difference, i am already optimizing for sequences of course but there is no need to pass that explicitly +[2011-05-11 22:19:33] <ichthyo> so somehow we'd fine a way to come from such sequences down to the individual frames which can be deliverd +[2011-05-11 22:19:46] <ichthyo> find* +[2011-05-11 22:20:22] <ichthyo> yet there is one nasty problem +[2011-05-11 22:20:40] <cehteh> my thinking is that every frame gets a 'job' ... all managed by player/scheduler and this jobs become canceled if not actual anymore +[2011-05-11 22:20:42] <ichthyo> all those changes I'm writing about here are more or less connected to such a sequence a s awhole +[2011-05-11 22:20:54] <ichthyo> yes, that was the general idea +[2011-05-11 22:20:59] <ichthyo> each frame is a job +[2011-05-11 22:21:08] <ichthyo> maybe even parts of a frame +[2011-05-11 22:21:14] <ichthyo> e.g. fetching data +[2011-05-11 22:22:02] <cehteh> a 'job' .. or more precicely a 'frame-render-job' can have some state, like references to related (following) jobs or so on +[2011-05-11 22:22:12] <ichthyo> ah ok +[2011-05-11 22:22:15] <cehteh> i am not sure yet if we want to put that there or in the player +[2011-05-11 22:22:22] <ichthyo> so we could traverse that like a data structure +[2011-05-11 22:22:27] <cehteh> yes +[2011-05-11 22:22:30] <ichthyo> yes both would be possible +[2011-05-11 22:22:49] <ichthyo> e.g think about the following scenario: +[2011-05-11 22:23:04] <cehteh> maybe even the player is better but that makes the player a little more complex as it doesnt just need to shoot and forget but also care for a lot (potentially aborted) jobs +[2011-05-11 22:23:06] <ichthyo> we're in the middle of playing, and now we want to switch to double speed playback +[2011-05-11 22:23:39] <cehteh> yes then the player cancels every other frame job and changes its timebase +[2011-05-11 22:23:56] <cehteh> or instead canceling it could put them on a lower priority +[2011-05-11 22:24:01] <ichthyo> that means, the player/scheduler/whatever needs to find out a point in time, *when* this switch can be performed +[2011-05-11 22:24:07] <cehteh> (bacground/caching) +[2011-05-11 22:24:13] <ichthyo> and then cancle all existing jobs after that point and reschedule new jobs +[2011-05-11 22:24:14] |<-- SimAV has left kornbluth.freenode.net (Quit: signal_entry: got SIGSEGV) +[2011-05-11 22:24:20] <cehteh> yes +[2011-05-11 22:24:40] <cehteh> so that means the player might get considerably more heavy with all this bookkeeping +[2011-05-11 22:24:40] <ichthyo> note I thought it helps if we don't guarantee immediate reaction +[2011-05-11 22:25:00] <ichthyo> for the user it doesn't count if the speedup happens 100ms later +[2011-05-11 22:25:13] <cehteh> 100ms yes ... thats noticeable +[2011-05-11 22:25:22] <ichthyo> ok, 50ms or so +[2011-05-11 22:25:45] <daylife> 30 ms is noticable +[2011-05-11 22:25:48] <cehteh> well it can almost instantly switch .. this jobs are 'free wheeling' as you saied +[2011-05-11 22:25:56] <ichthyo> anyway, such a change of playback might require some preparation, i.e. fetch other raw data +[2011-05-11 22:26:15] <cehteh> yes +[2011-05-11 22:26:40] <ichthyo> so we might trade a (tiny) delay against a smooth continuation without jerk +[2011-05-11 22:26:41] <cehteh> there should be a fast-path for time and priority rescheduling +[2011-05-11 22:27:33] <cehteh> fast path == explcit interface for only that purpose +[2011-05-11 22:27:36] <daylife> I'm not sure what you're discussing exactly, but one of the things that bothers me most about the Lightworks beta is the noticable lag when navigating the timeline, playing, etc, in contrast to Avid, which immediately, no delay whatsoever, responds to any user action +[2011-05-11 22:27:57] <cehteh> daylife: haha :P .. +[2011-05-11 22:28:16] <cehteh> dinosaurs have a lot inertia :) +[2011-05-11 22:28:31] <ichthyo> :-D +[2011-05-11 22:28:33] <cehteh> well yes thats related to what we discuss ... +[2011-05-11 22:29:01] <cehteh> user actions should give *instant* feedback .. even if this feedback is only fake feedback in corner cases +[2011-05-11 22:29:21] * ichthyo nods +[2011-05-11 22:29:22] <daylife> editing is on that part a bit like playing piano. if there were any delay, no matter how short, between pressing a key and hearing a note, it would be very irritating +[2011-05-11 22:29:32] <cehteh> exacty +[2011-05-11 22:29:39] <hermanr> Indeed +[2011-05-11 22:29:53] <cehteh> well .. except you play pipe organ :P +[2011-05-11 22:30:01] <daylife> :D +[2011-05-11 22:30:01] <ichthyo> hah +[2011-05-11 22:30:32] <ichthyo> thats the reason why today the organ players largely prefer mechanical action +[2011-05-11 22:30:46] <cehteh> anyways .. you press a key and you hear that note (which might be syntezized, instead the real pipe if not deliverable) +[2011-05-11 22:30:51] <ichthyo> because then you can at least feel when the valve in the air channel opens +[2011-05-11 22:31:24] <cehteh> and i bet even avid sometimes can not deliver +[2011-05-11 22:31:26] <ichthyo> thats an example for such a 'fake' feedback +[2011-05-11 22:31:32] <ichthyo> anyway +[2011-05-11 22:31:32] <cehteh> yes +[2011-05-11 22:31:44] <ichthyo> so we might consider what is possible for us in that area +[2011-05-11 22:31:54] <ichthyo> reduced latency, that is +[2011-05-11 22:32:37] <cehteh> ichthyo: so an explicit interface for rescheduling jobs (without changing other parameters) is possiblee and required +[2011-05-11 22:33:02] <cehteh> but caveat .. maybe we need to reschedule dependent jobs too +[2011-05-11 22:33:24] <ichthyo> yeah... likely +[2011-05-11 22:33:38] <cehteh> so far you wanted to provide jobs in a conflictless nondeadlocking way +[2011-05-11 22:33:56] <ichthyo> at least on some level, yes +[2011-05-11 22:34:08] <cehteh> but for rescheduling that means likely that dependency information needs to be passed and maintained in the scheduler as well +[2011-05-11 22:34:12] <ichthyo> that doesn't need to be immediately the level within the scheduler +[2011-05-11 22:34:29] <cehteh> well it would be much easier if it is +[2011-05-11 22:35:09] <ichthyo> well... thus the idea to allow for a (small) tolerance interval +[2011-05-11 22:35:14] <cehteh> then rescheduling automatically walks all dependencies, reschedules them too and by that it can already see if we can met timing constraints +[2011-05-11 22:35:27] <ichthyo> i.e. the scheduler can take his time to find out *when* he is able to perform the change +[2011-05-11 22:35:58] <cehteh> i think thats not explicitly needed +[2011-05-11 22:36:37] <cehteh> the 'reschedule' can answer if rescheduling is possible +[2011-05-11 22:37:07] <cehteh> so you just iterate over all frame-job to be rescheduled from shortest to longest time +[2011-05-11 22:37:36] <cehteh> the most urgend reschedules may return "hey this job is already running, cant reschedule" +[2011-05-11 22:37:49] <cehteh> or even "already done, nothing to do" +[2011-05-11 22:38:03] <cehteh> to "sorry, cant do that dave" +[2011-05-11 22:38:15] <cehteh> to "ok rescheduling acknowledged" +[2011-05-11 22:38:32] <cehteh> any other returns possible? i think thats all +[2011-05-11 22:39:39] <ichthyo> ok, but now, just by doing that walk, we've found out a time point when the change will happen +[2011-05-11 22:40:33] <ichthyo> and that would be the information interesting for the layers above +[2011-05-11 22:40:56] <ichthyo> e.g. to change the animation speed of the cursor, or whatever +[2011-05-11 22:41:22] <ichthyo> or more concrete +[2011-05-11 22:41:23] <cehteh> so .. rescheduling is fast and synchronous +[2011-05-11 22:41:40] <cehteh> if you want async rescheduling you make that a job by itself +[2011-05-11 22:42:04] <ichthyo> or more concrete... when we do an 'abort' +[2011-05-11 22:42:12] <ichthyo> e.g. because the user changed the model +[2011-05-11 22:42:24] <ichthyo> then I'm sure I need that asynchroneous feedback +[2011-05-11 22:42:41] <cehteh> so for the player it might reschedule the next 500ms worth of frames synchronously and any later frames (background rendering) asynchronously +[2011-05-11 22:43:14] <ichthyo> ...i.e. that other part doesn't have a timer or scheduler on its own, but rather relies on that callback +[2011-05-11 22:43:19] <cehteh> we never abort 'running' jobs .. only waiting or scheduled jobs +[2011-05-11 22:43:28] <ichthyo> of course +[2011-05-11 22:43:48] <ichthyo> but we can (and need) to 'abort' whole calculation streams +[2011-05-11 22:43:49] <cehteh> this aborts are not the fast-path rescheduling, prolly fast to but another interface +[2011-05-11 22:43:57] <ichthyo> yeah +[2011-05-11 22:44:09] <cehteh> thats a method on the job in OO speak +[2011-05-11 22:44:18] <cehteh> aborting is really fast +[2011-05-11 22:44:28] <cehteh> because it only sets a flag in the job +[2011-05-11 22:44:29] <ichthyo> and, as said, in that case I'd need such a callback to happen *when* the abort is done +[2011-05-11 22:45:01] <ichthyo> because only then can I release the nodes and other wiring tied to the old state prior to the change +[2011-05-11 22:45:01] <cehteh> the job stays in the scheduler (or wait list) and if it becomes disposed when it would be schedules +[2011-05-11 22:45:06] <ichthyo> thats just an example +[2011-05-11 22:45:27] <cehteh> do you really need that feedback? +[2011-05-11 22:45:47] <cehteh> how about 'abort' gives instant feedback .. +[2011-05-11 22:45:49] <ichthyo> not 100% sure, but I rather think yes +[2011-05-11 22:45:53] <cehteh> or even 'never fails' +[2011-05-11 22:46:09] <cehteh> just abort a job and consider it dead from your point of view +[2011-05-11 22:46:14] <ichthyo> note: the point is: in that other part triggering the 'abort' +[2011-05-11 22:46:23] <ichthyo> there is no timer or scheduler running +[2011-05-11 22:46:44] <ichthyo> so it doesn't help to know *when* the abort is executed +[2011-05-11 22:47:03] <ichthyo> but I need to be notified *after* it has happened +[2011-05-11 22:47:09] <cehteh> jobs are self containing, any resources they need are attached to the job, they should not reference any volatile data up in proc +[2011-05-11 22:47:39] <ichthyo> directly, yes of course +[2011-05-11 22:47:47] <ichthyo> but indirectly thats impossible +[2011-05-11 22:47:58] <cehteh> yes so thats the question .. you just call 'job_abort()' which returns immediately and the job can be considered aborted from your pow +[2011-05-11 22:48:36] <ichthyo> well.. I'd prefer to get an async callback, *after* the abort has really happened +[2011-05-11 22:48:44] <cehteh> well can do that +[2011-05-11 22:48:52] <ichthyo> i.e. schedule a callback job? +[2011-05-11 22:48:56] <cehteh> (but why?) +[2011-05-11 22:49:24] <cehteh> each job has a 'job-function' callback .. that can be anything +[2011-05-11 22:49:46] <cehteh> when you abort a job you can dispose the intended function by a abort callback +[2011-05-11 22:49:49] <ichthyo> in that concrete example, quite simply, because the higher layers can do some stuff only after the jobs are gone +[2011-05-11 22:49:57] <ichthyo> e.g. close the session +[2011-05-11 22:50:03] <cehteh> really? +[2011-05-11 22:50:25] <ichthyo> well... I can't just blow away a session which is still used by ongoing jobs +[2011-05-11 22:50:30] <cehteh> i was thinking about this beeing very separate +[2011-05-11 22:50:31] <ichthyo> indirectly of course +[2011-05-11 22:50:42] <cehteh> yes i thought so +[2011-05-11 22:51:04] <cehteh> jobs have no backpointers to the session +[2011-05-11 22:51:05] <ichthyo> of course I don't mutate the data which is referred by the jobs +[2011-05-11 22:51:38] <cehteh> then you can just close the session .. if that syncronizes on some resources still in use by jobs (but managed elsewhere) the close will block +[2011-05-11 22:51:42] <ichthyo> ah good point +[2011-05-11 22:52:09] <cehteh> nah not only reference data i am really thinking that jobs have absolutely no references into the session +[2011-05-11 22:52:18] <cehteh> they have references into the backend data +[2011-05-11 22:52:23] <cehteh> but these are managed there +[2011-05-11 22:52:37] <cehteh> while closing a session doesnt require to shutdown the backend +[2011-05-11 22:53:01] <ichthyo> well, so to clarify that: there *is* some kind of back reference for sure. Just it isn't direct +[2011-05-11 22:53:11] <cehteh> which one? +[2011-05-11 22:53:12] <ichthyo> see: a job references an exit node +[2011-05-11 22:53:33] <cehteh> mhm +[2011-05-11 22:53:35] <ichthyo> and it references an output slot +[2011-05-11 22:53:38] <cehteh> really? +[2011-05-11 22:53:47] <cehteh> how about a functor there +[2011-05-11 22:53:50] <ichthyo> both are things which are somehow allocated from the session +[2011-05-11 22:53:55] <cehteh> jobs references a continutation job +[2011-05-11 22:54:16] <cehteh> which is an abstraction of the exit node +[2011-05-11 22:54:36] <cehteh> well anyways .. the callback *is* trivially possible +[2011-05-11 22:54:48] <cehteh> but do not expect that i want to do rescheduling for aborting jobs +[2011-05-11 22:55:30] <ichthyo> well... as said: jobs can't be aborted +[2011-05-11 22:55:38] <ichthyo> but whole calculation streams can +[2011-05-11 22:55:42] <cehteh> if you place a job to be executed in 1hour and then abort this job, this abort callback might be called not earlier than in one hour (or if your really want we can add a time-limit there for rescheduling) +[2011-05-11 22:56:04] <cehteh> from the scheduler pow every single job can be aborted +[2011-05-11 22:56:23] <ichthyo> yes, but only if it hasn't started running +[2011-05-11 22:56:31] <cehteh> makes no sense to implement this otherwise and there will be other maintenance jobs as well +[2011-05-11 22:56:38] <cehteh> yes +[2011-05-11 22:57:34] <ichthyo> so this callback job would not be scheduled to that time of the original job (1 hour in the future) but to that point when the scheduler can guarantee that non of the old jobs will become active anymore +[2011-05-11 22:57:45] <cehteh> well even a running job can be aborted from the interface view, it still continues running but the results will be discarded and it will call the callback +[2011-05-11 22:57:54] <ichthyo> because then I can dispose the data, change connections, deregister output slots and so on +[2011-05-11 22:58:57] <cehteh> rescheduling has some cost, not much but its usualy more efficent not to reschedule something you want to remove from a priority queue +[2011-05-11 22:59:09] <ichthyo> yeah, of course +[2011-05-11 22:59:13] <cehteh> eventually it will become the head of the queue and then you drop it +[2011-05-11 22:59:20] <ichthyo> exactly +[2011-05-11 22:59:32] <cehteh> thats the way i want to implement it +[2011-05-11 22:59:55] <ichthyo> all I want for this respect is a guarantee that the working function of that job can't get active anymore +[2011-05-11 22:59:55] <cehteh> if you want to reschedule a aborted job i can add a flag or a time parameter to that interface +[2011-05-11 23:00:10] <cehteh> get? executed? +[2011-05-11 23:00:41] <cehteh> job_abort() can return "job already running" or "aborted" .. .both in instant +[2011-05-11 23:00:41] <ichthyo> do you see my point? +[2011-05-11 23:00:46] <cehteh> yes i see your point +[2011-05-11 23:00:55] <cehteh> adding that callback functionality +[2011-05-11 23:01:16] <cehteh> well the only case is "job already running" .. +[2011-05-11 23:01:33] <ichthyo> it doesn't help me to know *now* that 3 frames into the future will still be calculated and delivered, but starting with the 4.th frame, the abort will be in effect +[2011-05-11 23:01:44] <ichthyo> this information doesn't help me *now* +[2011-05-11 23:01:48] <cehteh> yes +[2011-05-11 23:02:09] <ichthyo> because, until these 3 frames are delivered, I can't deregister the "output slot" +[2011-05-11 23:02:17] <ichthyo> e.g. disconect an audio client from Jack server +[2011-05-11 23:02:57] <cehteh> RAII :P .. garbage collecton would be better in this case +[2011-05-11 23:03:07] <ichthyo> not the slightest +[2011-05-11 23:03:25] <cehteh> well yes but in a different way +[2011-05-11 23:04:00] <cehteh> anyways ok for me i add the callback +[2011-05-11 23:04:10] <ichthyo> and deregistering an external connection has nothing to do with how the resources are tracked. +[2011-05-11 23:04:22] <ichthyo> rather I need to know *when* its safe to do so +[2011-05-11 23:04:26] -->| fsiddi (~fsiddi@host38-107-dynamic.21-79-r.retail.telecomitalia.it) has joined #lumiera +[2011-05-11 23:04:27] <cehteh> question is: do you want to reschedule aborts explicitly or shall that part of the abort interface? +[2011-05-11 23:04:46] <ichthyo> hello fsiddi ! +[2011-05-11 23:04:49] <fsiddi> hello everyone +[2011-05-11 23:04:52] <fsiddi> sorry for being late +[2011-05-11 23:04:56] <cehteh> job_abort(job); job_reschedule(job, NOW); +[2011-05-11 23:04:58] <ichthyo> no prob +[2011-05-11 23:05:27] <cehteh> looks cleaner to me than making the abort interface heavier +[2011-05-11 23:05:44] <ichthyo> fsiddi: my apologies... I din't find time yet to look into that CSS stuff I proposed +[2011-05-11 23:05:50] <cehteh> hi fsiddi +[2011-05-11 23:05:53] <ichthyo> and promised to do +[2011-05-11 23:06:04] <fsiddi> ichthyo: np. Live's being a mess for me in the last wweks +[2011-05-11 23:06:10] <ichthyo> :-/ +[2011-05-11 23:06:24] <ichthyo> hopefully not too bad... +[2011-05-11 23:07:08] <fsiddi> nono not bad +[2011-05-11 23:07:14] <fsiddi> hei cehteh +[2011-05-11 23:07:44] <fsiddi> I also did not manage to carry on the work. But I'm starting to get past some other jobs i had to do +[2011-05-11 23:07:52] <fsiddi> I really would like to finish it quickly +[2011-05-11 23:07:56] <ichthyo> cehteh: actually I'd put an adaptation layer in between. When viewed from the player (or the session for that), I only want an high-level 'abort' operation. Like I proposed in my RfC. I didn't expect to talk directly to the scheduler or any part of the engine +[2011-05-11 23:08:18] <cehteh> well i am thinking in low level terms (as usual) +[2011-05-11 23:08:31] <ichthyo> so at the interface of the scheduler, that can be implemented so that it fits in best there +[2011-05-11 23:09:09] <cehteh> i write some low-level notes for the interface (nothing concrete) +[2011-05-11 23:09:22] <ichthyo> this approach with the async callback was intended for that high-level interface; just out of practical considerations +[2011-05-11 23:09:43] <ichthyo> I don't think it's a good idea when the clients using that high-level engine interface have to run a timer themselves +[2011-05-11 23:09:50] <ichthyo> just to find out when it is safe to proceed +[2011-05-11 23:09:58] <ichthyo> Just as a striking example +[2011-05-11 23:10:13] <ichthyo> think about the occasional crashes with the dummy player currently in the GUI +[2011-05-11 23:10:24] <ichthyo> that is exactly due to a lack of such a callback +[2011-05-11 23:10:26] <cehteh> yes while i was thinking we can decouple this completely, but maybe not +[2011-05-11 23:10:41] <ichthyo> the GUI closes the window. The only thing I can do is to block that closing +[2011-05-11 23:10:57] <ichthyo> but without external help, I don't know how long to block +[2011-05-11 23:11:33] <ichthyo> and I we don't block, then the last frame gets disposed into an output buffer which is already gone, deregistered from XV and whatsoever +[2011-05-11 23:11:45] <ichthyo> not ever, but sometimes +[2011-05-11 23:12:18] <cehteh> well i was thinking that the scheduler and backend only operate on their own data and you can just drop that on the floor from player and proc (and gui) +[2011-05-11 23:12:37] <cehteh> that means currently the player presents some buffers and the mockup player renders there +[2011-05-11 23:13:02] <cehteh> this renders into the gui provied buffer which the gui may release too early right? +[2011-05-11 23:13:34] <cehteh> but if this buffer is provided by the backend and the gui just closes down, not displaying it anymore then nothing happens, the backend knows how to manage resources +[2011-05-11 23:14:29] <ichthyo> well... true, but a general mismatch +[2011-05-11 23:14:34] <ichthyo> usually it works the other way round +[2011-05-11 23:14:43] <cehteh> well thinking futher, referencing an exit node is kindof violating the idea that proc is stateless :P +[2011-05-11 23:14:49] <ichthyo> I don't know how it's for ALSA, but also for jack it works the other way round +[2011-05-11 23:15:01] <ichthyo> all those systems assume that you have a thread running just to serve them +[2011-05-11 23:15:06] <cehteh> yes +[2011-05-11 23:15:13] <ichthyo> and require you to set up buffers to place things into +[2011-05-11 23:15:24] <ichthyo> Jack is even more rigourous +[2011-05-11 23:15:32] <cehteh> yes i see now +[2011-05-11 23:15:54] <ichthyo> so thats an important general mismatch we should come up with a nice solution maybe +[2011-05-11 23:16:13] <cehteh> but well i still wonder if we may put such a callback even more into the backend "resource released" ... +[2011-05-11 23:16:16] <ichthyo> of course it would be great, if the backend just delivers in time, right into the buffer for output +[2011-05-11 23:16:18] <cehteh> mhm maybe not +[2011-05-11 23:16:39] <ichthyo> so that we don't need another buffering and copy over step on the output side +[2011-05-11 23:16:48] <cehteh> has pros and cons .. not all resources have jobs associated +[2011-05-11 23:16:52] <ichthyo> ok, but maybe a short intermission +[2011-05-11 23:17:06] <cehteh> but still in real the jack buffers will come from the backend +[2011-05-11 23:17:10] <ichthyo> fsiddi: you see, we started to engage into a low level discussion +[2011-05-11 23:17:13] <cehteh> so the backend can send a notify +[2011-05-11 23:17:20] <fsiddi> :) +[2011-05-11 23:17:24] <ichthyo> fsiddi: is there anything we might discuss regarding the gui right now +[2011-05-11 23:17:33] <ichthyo> or need to discuss? +[2011-05-11 23:17:35] <fsiddi> no not really +[2011-05-11 23:17:39] <fsiddi> you can go on +[2011-05-11 23:17:43] <ichthyo> same for me +[2011-05-11 23:17:44] <fsiddi> i just wanted to be present +[2011-05-11 23:18:20] <ichthyo> I just promised to look into that CSS stuff too, because of that question how to make a flexible layout with limitations +[2011-05-11 23:18:25] <ichthyo> as we discussed last time +[2011-05-11 23:18:37] <ichthyo> I still have that on my TODO +[2011-05-11 23:21:03] <ichthyo> ok, so we'll just continue with our thrilling discussion... +[2011-05-11 23:21:23] <ichthyo> cehteh: AFAIK, the jack buffers come from the jack server +[2011-05-11 23:21:25] <fsiddi> yes yes go on with it! +[2011-05-11 23:21:31] <cehteh> whats a good name for a enum value when the constraints are not met? 'invalidated' +[2011-05-11 23:21:44] <ichthyo> and not from the backend +[2011-05-11 23:21:49] <cehteh> ichthyo: the backend can still manage that +[2011-05-11 23:22:02] <ichthyo> same for any gui widgets to display video +[2011-05-11 23:22:06] <cehteh> yes +[2011-05-11 23:22:44] <ichthyo> so why should the backend try to manage buffers, which are actually managed by an other (external) system? +[2011-05-11 23:22:47] <cehteh> generally i want it the other way around that the backend allocates/mmaps buffers, but i know that there are a lot things which do it the other way around +[2011-05-11 23:22:57] <ichthyo> yes +[2011-05-11 23:23:02] <cehteh> that means we can do 2 things: copying into the destination buffer +[2011-05-11 23:23:11] <cehteh> or manage this destination buffers as well +[2011-05-11 23:24:08] <ichthyo> or (3rd option): let the last calculation step work into those external destination buffers +[2011-05-11 23:24:35] <cehteh> thats case 2 .. when we manage them, then we calculate there with our normal toolchain +[2011-05-11 23:25:03] <ichthyo> not quite; we use them, but we don't manage them +[2011-05-11 23:25:27] <cehteh> proc just asks for a buffer and doesnt need to care from where it comes (RAM, Graphics card, audio server ...) +[2011-05-11 23:26:08] <ichthyo> and thats not all +[2011-05-11 23:26:11] <cehteh> if we dont manage them but only 'use' then then we get some more code paths for every different kind of buffer +[2011-05-11 23:26:14] <ichthyo> there are similar problems +[2011-05-11 23:26:26] <ichthyo> e.g. jack doesn't run in our threading sytem +[2011-05-11 23:26:30] <cehteh> yes +[2011-05-11 23:26:34] <cehteh> shared memory +[2011-05-11 23:27:04] <ichthyo> so the question is: can we even be so precise? we don't have low-latency permissions, as the jack server has +[2011-05-11 23:27:27] <ichthyo> so that means some additional buffering or copying anyway +[2011-05-11 23:27:31] <cehteh> we manage buffers provided by jack +[2011-05-11 23:27:49] <cehteh> if we cant render in time, we cant copy in time for sure +[2011-05-11 23:27:55] <ichthyo> ah... if you call that 'managing' then yes +[2011-05-11 23:28:00] <cehteh> err other way around +[2011-05-11 23:28:04] <cehteh> yes +[2011-05-11 23:28:33] <cehteh> so we have different memory backend, first our own mmaped persistent file storage +[2011-05-11 23:28:37] <ichthyo> nah: 'managing' means owning, allocating, deallocating +[2011-05-11 23:28:46] <cehteh> then later maybe memory on the gpu +[2011-05-11 23:29:12] <cehteh> well we ask jack for memory and we tell jack when we dont need it anymore +[2011-05-11 23:29:33] <cehteh> and i think i can do that better in the backend just like any other memory resource +[2011-05-11 23:29:40] <ichthyo> but actually I don't see why we should +[2011-05-11 23:29:49] <ichthyo> why can't we just cooperate with these services? +[2011-05-11 23:29:56] <ichthyo> as everyone else does too? +[2011-05-11 23:30:01] <cehteh> i call that cooperation? +[2011-05-11 23:30:41] <ichthyo> I don't see why the backend needs to pseudo-manage things which are just out of the scope, and actually owned and managed by someone else? +[2011-05-11 23:30:49] <cehteh> the question is only if every single thing does this ad-hoc .. aka the gui cares for video buffers, the audio player cares for sound buffers, proc cares for rendering buffers and so on +[2011-05-11 23:31:02] <cehteh> or we have one backend which cares about all buffers +[2011-05-11 23:31:17] <cehteh> the coding effort is almost the same +[2011-05-11 23:31:46] <cehteh> but we get one 'memory' service which can deliver different classes of memory (and later easily adaptable/extendable to new technologies) +[2011-05-11 23:32:01] <ichthyo> I'd rather doubt that. In all those cases, we're using already existing libraries and solutions. and we're rather integrating with them +[2011-05-11 23:32:23] <cehteh> yes so there is no much effort doing so .. +[2011-05-11 23:32:54] <ichthyo> that extends even more to the media libs, we're intend to use +[2011-05-11 23:32:55] <cehteh> but since the backend always knows what memory is in use it can do a clean shutdown +[2011-05-11 23:33:18] <cehteh> without all this callbacks and asyncronous messages +[2011-05-11 23:33:34] <ichthyo> not really +[2011-05-11 23:33:49] <ichthyo> if we use external services, we're bout to the terms of these +[2011-05-11 23:33:56] <ichthyo> same for external libraries +[2011-05-11 23:34:14] <cehteh> yes +[2011-05-11 23:34:14] <ichthyo> I think its the fundamental wrong approach to try to build up such a huge centralism +[2011-05-11 23:34:30] <cehteh> it isnt huge in any way +[2011-05-11 23:34:33] <ichthyo> and trying to pseudo-manage things which we don't own +[2011-05-11 23:34:43] <ichthyo> instead of just cooperating, as eveyone else does +[2011-05-11 23:35:04] <ichthyo> if we have a nice and blastingly fast mmaped file read and buffer handling for it than fine +[2011-05-11 23:35:06] <cehteh> imo this is cooperating .. but well we are not there yet +[2011-05-11 23:35:41] <ichthyo> then I'd use that exactly for those sevices, which *we* write, provide and manage +[2011-05-11 23:36:08] <cehteh> possibly we just do it as you saying, and we may implement it in the way as i think later (and then try out and compare) .. or stay with your way +[2011-05-11 23:37:29] <ichthyo> maybe yes... probably I'd concentrate first on that core part which we provide, becasue I'd guess it is the most important one +[2011-05-11 23:37:44] <ichthyo> i.e. reading those source media files and the like +[2011-05-11 23:37:46] <cehteh> well i have some reasons to do it in my way .. for example if you connect the viewer somewhere in the graph, then it would be nice if it can transparently use memory on the graphics card instead copying over +[2011-05-11 23:38:10] <cehteh> or think about render nodes and network transparency later +[2011-05-11 23:38:48] <cehteh> but this is future and i think it is not a fundamental design decision can can be incrementally added when it makes sense and turns out to work well +[2011-05-11 23:39:07] <cehteh> first i am going for the mmaped file access and nothing else +[2011-05-11 23:39:07] <ichthyo> yes, probably better approaching it that way +[2011-05-11 23:39:21] <ichthyo> indeed, I think that is the most important thing +[2011-05-11 23:39:35] <cehteh> but this doesnt mean we shall not try it :) +[2011-05-11 23:40:09] <cehteh> so whats a good enum name for a job which we cant do in time? +[2011-05-11 23:40:20] <cehteh> invalidated, expired? .. (it isnt expired yet) +[2011-05-11 23:40:41] <cehteh> rejected? +[2011-05-11 23:41:07] <ichthyo> missed? +[2011-05-11 23:41:15] <ichthyo> floundered? +[2011-05-11 23:41:43] <ichthyo> lapsed? +[2011-05-11 23:42:42] <cehteh> http://pastebin.com/w7Rk8UXx +[2011-05-11 23:42:44] <cehteh> my notes :P +[2011-05-11 23:43:32] <cehteh> ah 'scheduled' state of course +[2011-05-11 23:43:55] <cehteh> well this isnt final .. there are some overlaps its more just to write down what we need +[2011-05-11 23:44:32] <ichthyo> sounds good +[2011-05-11 23:44:34] <ichthyo> question +[2011-05-11 23:44:48] <cehteh> so about QoS.. i am thinking about not caring at all for it +[2011-05-11 23:44:51] <ichthyo> do our jobs have some mechanism to 'yield' or wait or similar? +[2011-05-11 23:45:10] <ichthyo> i.e. go in a wait state (and make room for other jobs to be scheduled meanwhile)? +[2011-05-11 23:45:14] <cehteh> then you put that into the job function, when it becomes running (and can do an early abort there) +[2011-05-11 23:45:25] <cehteh> yes +[2011-05-11 23:45:34] <cehteh> waiting on other jobs +[2011-05-11 23:45:43] <ichthyo> ah ok +[2011-05-11 23:45:49] <cehteh> i am thinking if i make a generic waiting or only on jobs +[2011-05-11 23:45:54] <ichthyo> so you call into a funciton on the lumiera scheduler +[2011-05-11 23:45:55] <cehteh> i think only on jobs is good +[2011-05-11 23:46:06] <ichthyo> which blocks and reschedules other jobs meanwhile? +[2011-05-11 23:46:23] <cehteh> a running job? +[2011-05-11 23:46:34] <ichthyo> just brainstorming +[2011-05-11 23:46:45] <ichthyo> both is thinkable +[2011-05-11 23:46:56] <ichthyo> also it would be thinkable that we disallow any waits +[2011-05-11 23:47:03] <cehteh> nope i do not want that, rather split the jobs up into small pieces +[2011-05-11 23:47:13] <ichthyo> and just say, then the job function has to terminate and another job has to pick up +[2011-05-11 23:47:27] <cehteh> yes thats possible +[2011-05-11 23:48:00] <cehteh> but when a job runs it runs until its done (which might be a errorneous/abort on its own) +[2011-05-11 23:48:20] <cehteh> after running a job wont become rescheduled +[2011-05-11 23:48:39] <ichthyo> yes +[2011-05-11 23:48:49] <cehteh> if it turns out we need a yield i could implement it .. but not prematurely +[2011-05-11 23:48:53] <ichthyo> you know the model Erlang uses for this? +[2011-05-11 23:48:59] <cehteh> yes +[2011-05-11 23:49:06] <ichthyo> thats kind of a middle ground +[2011-05-11 23:49:14] <ichthyo> doesn't allow arbitrary blocking +[2011-05-11 23:49:24] <ichthyo> but can block waiting on the next message +[2011-05-11 23:49:44] <ichthyo> but likely for our purposes we don't even need that +[2011-05-11 23:50:16] <cehteh> yes and thats not domain of the scheduler +[2011-05-11 23:50:32] <cehteh> jobs can block anyways +[2011-05-11 23:50:40] <cehteh> in running state +[2011-05-11 23:50:53] <cehteh> no need to put them on a wit queue or whatever +[2011-05-11 23:51:18] <ichthyo> ok +[2011-05-11 23:51:31] <ichthyo> but of course the goal is to write them such that they don't block +[2011-05-11 23:51:39] <cehteh> yes +[2011-05-11 23:52:11] <cehteh> and the only thing worthwile to wait for in a special wait state is for some other job +[2011-05-11 23:52:26] <ichthyo> btw... how does the scheduler detect that? am I right that this rather works indirectly? +[2011-05-11 23:52:28] <ichthyo> i.e. +[2011-05-11 23:52:38] <cehteh> the next question is .. do we release this waiting jobs by some signal or by a job termination? +[2011-05-11 23:52:41] <ichthyo> through the thread pooö +[2011-05-11 23:52:54] <ichthyo> pool +[2011-05-11 23:52:59] <ichthyo> good question +[2011-05-11 23:53:14] <cehteh> first can be more efficent and will be more complex +[2011-05-11 23:53:30] <cehteh> so for first i opt for the later, there is prolly anyways something to do +[2011-05-11 23:54:02] <ichthyo> how does this 'waiting' actually work? +[2011-05-11 23:54:16] <ichthyo> does the jobfunction call into some API of the scheduler? +[2011-05-11 23:54:21] <cehteh> i make a explicit waiting container (llist) jobs waiting sit there and are not scheduled +[2011-05-11 23:54:47] <cehteh> (or maybe that uses a priority queue to, to detect expired jobs) +[2011-05-11 23:55:02] <cehteh> the sheduler wraps the jobfunction +[2011-05-11 23:55:10] <ichthyo> ok +[2011-05-11 23:55:29] <ichthyo> so that means, he gets control only when the jobfunction exits +[2011-05-11 23:55:32] <cehteh> later, when needed we can add explicit calls .. and signaling over the threads condvar +[2011-05-11 23:55:34] <ichthyo> is that correct= +[2011-05-11 23:55:37] <ichthyo> ? +[2011-05-11 23:55:51] <cehteh> for simplicity and a first implementation yes i think that suffices +[2011-05-11 23:55:59] * ichthyo thinks the same +[2011-05-11 23:56:18] <cehteh> btw you certainly will have some fun when a job throws +[2011-05-11 23:56:25] <ichthyo> haha +[2011-05-11 23:57:00] <ichthyo> well.. I think that pretty much figures for anyone used to such systems +[2011-05-11 23:57:02] <cehteh> make a c++ wraper .. catch the exception .. store it in the job structure, job done with error +[2011-05-11 23:57:06] <ichthyo> exactly +[2011-05-11 23:57:42] <ichthyo> thats valid even for 'regular' theads +[2011-05-11 23:57:46] <cehteh> yes +[2011-05-11 23:58:08] <ichthyo> its rarely a good idea letting an exception leak out of any such parallel runing system in an uncontrolled manner +[2011-05-11 23:58:12] <cehteh> jobs are thread .. just without that overhead in creation and finer timing and priority control +[2011-05-11 23:59:32] <cehteh> can you think about any case where a job must wait on something else than another job? +[2011-05-12 00:00:00] <cehteh> or simplified .. can you think about any resource which can not be turned into a job +[2011-05-12 00:00:47] <ichthyo> I'd say its the other way round. We define our 'jobs' to have that properties +[2011-05-12 00:00:59] <cehteh> ok +[2011-05-12 00:01:01] <cehteh> same here +[2011-05-12 00:01:02] <ichthyo> so that defines what can be done in a job an what can't +[2011-05-12 00:01:27] <ichthyo> but, answering to your question, I think we can cover all the cases usually encountered when rendering +[2011-05-12 00:01:37] <cehteh> so now we talked about the scheduler interface .. which isnt really the engine interface .. BUT: the job structure needs to carry all resources involved around +[2011-05-12 00:02:34] <cehteh> think abut the W there are more interfaces involved +[2011-05-12 00:02:43] <ichthyo> ok... that brings up another closely related question +[2011-05-12 00:02:59] <ichthyo> the engine interface needs something like these calculation streams +[2011-05-12 00:02:59] <cehteh> first, asking the backend for a frame +[2011-05-12 00:03:19] <ichthyo> that means, that the individual job needs to cary a marker, so it can be associated to a specific stream +[2011-05-12 00:03:58] <cehteh> i add at least one generic void* to the job where you can put any data along +[2011-05-12 00:04:12] <cehteh> type safety on the level above please :P +[2011-05-12 00:05:20] <ichthyo> I'm fine with that +[2011-05-12 00:05:27] <cehteh> (caveat about the lifetime and ownership of this void*) +[2011-05-12 00:05:52] <cehteh> if the job owns it then we need also a cleanup hook there +[2011-05-12 00:06:17] <ichthyo> of course that means that this void* e.g. points back to, lets say an CalculationStream object +[2011-05-12 00:06:26] <cehteh> yes +[2011-05-12 00:06:33] <ichthyo> and then yes, we get again at the same situation discussed above +[2011-05-12 00:06:52] <ichthyo> regarding a cleanup hook +[2011-05-12 00:06:57] <cehteh> well i add the cleanup hook but make it optional (helps me and you just leave it NULL) +[2011-05-12 00:07:02] <ichthyo> thats fine +[2011-05-12 00:07:11] <ichthyo> regarding a cleanup hook ... I would be careful +[2011-05-12 00:07:13] <ichthyo> because +[2011-05-12 00:07:25] <ichthyo> we don't know in what thread a job is executed exactly, right? +[2011-05-12 00:07:26] <cehteh> yes i know :) +[2011-05-12 00:07:51] <cehteh> well prolly we can pin that down or make this thread-agnostic +[2011-05-12 00:08:01] <ichthyo> so I rather do not want to start with reference counts or similar (or worse) from within that cleanup hook +[2011-05-12 00:08:10] <cehteh> nah certainly not +[2011-05-12 00:08:18] <cehteh> this cleanup hook should be short and precise +[2011-05-12 00:09:01] <ichthyo> so again: I its possible to find out when the whole calculation stream is done +[2011-05-12 00:09:02] <cehteh> ah another point against yield() .. forgotten to say .. +[2011-05-12 00:09:10] <cehteh> jobs should not migrate between thread +[2011-05-12 00:09:15] <ichthyo> than we don't need to care for resource management on the level of individual jobs +[2011-05-12 00:09:27] <ichthyo> yes, good point +[2011-05-12 00:09:55] <cehteh> so KISS +[2011-05-12 00:10:05] <ichthyo> yes +[2011-05-12 00:10:11] <cehteh> a job is the smallest entity for scheduling +[2011-05-12 00:10:25] <cehteh> (the only entity) +[2011-05-12 00:10:44] <cehteh> next would be a interface how to setup a job... possibly in multiple stages +[2011-05-12 00:11:59] <cehteh> job_new(); job_wait_for(another_job); job_data()=some_calculation_stream; +[2011-05-12 00:12:06] <cehteh> job_arm() +[2011-05-12 00:12:14] <cehteh> something like that +[2011-05-12 00:12:38] <ichthyo> sounds ok +[2011-05-12 00:12:53] <cehteh> well .. now i think it becomes clear that 'jobs' are our message passing interface between proc and backend +[2011-05-12 00:13:15] <cehteh> you dont ask the backend directly for a frame you schedule a job delivering a frame +[2011-05-12 00:13:29] <cehteh> the backend schedules a job rendering a frame if its not cached +[2011-05-12 00:13:35] <ichthyo> yes, my thinking too +[2011-05-12 00:14:50] <cehteh> so 'job' is a baseclass .. but there can be highlevel subclasses cache_fetch_job and render_frame_job +[2011-05-12 00:15:04] <ichthyo> really? +[2011-05-12 00:15:11] <cehteh> i wonder .. makes sense or? +[2011-05-12 00:15:22] <cehteh> (even implemnting them in C :P) +[2011-05-12 00:15:38] <ichthyo> *could* be done, but somehow goes against that simplicity +[2011-05-12 00:15:53] <cehteh> i mean each of this class shares a lot, how abortion is handled and more +[2011-05-12 00:16:43] <cehteh> ok on the low level you can always put a job together with all the little details needed and then arm it +[2011-05-12 00:17:03] <ichthyo> not only on the low level. That would also be the correct OO aproach +[2011-05-12 00:17:14] <ichthyo> "prefer composition over inheritance" +[2011-05-12 00:17:35] <cehteh> i expect that we have maybe half a dozen classes of jobs +[2011-05-12 00:17:43] <cehteh> yes +[2011-05-12 00:17:53] <ichthyo> because, if you have a care_fetch_job, etc etc, you end up switching on types +[2011-05-12 00:18:13] <cehteh> ok .. leave it simple +[2011-05-12 00:18:27] <ichthyo> better investigate data at the start, and then assemble a job which can just run as-is +[2011-05-12 00:19:01] <cehteh> ok +[2011-05-12 00:20:00] <ichthyo> to expand on that +[2011-05-12 00:20:11] <ichthyo> when we create a 'calculation stream' +[2011-05-12 00:20:18] <ichthyo> we get the following informations: +[2011-05-12 00:20:30] <ichthyo> - the timings (frame duration, stepping) +[2011-05-12 00:20:36] <ichthyo> - the exit node to use +[2011-05-12 00:20:42] <cehteh> i wonder if i have to care anything about a caclulation stream .. thats purely a player/render thing +[2011-05-12 00:21:15] <ichthyo> *If* your realm just starts at the scheduler, then the answer is "no" +[2011-05-12 00:21:22] <ichthyo> then you don't have to care for that +[2011-05-12 00:21:42] <ichthyo> that means, I would implement that engine interface, kindof like a facade +[2011-05-12 00:21:43] <cehteh> except i would prolly take some hints for optimization +[2011-05-12 00:22:17] <cehteh> so you can add at least add flags like ONESHOT, FORWARD, BACKWARD, RANDOM +[2011-05-12 00:22:19] <ichthyo> taking that approach would mean that I would also have to set up the times for the jobs, right? +[2011-05-12 00:22:48] <cehteh> we once talked about that we want a timer facility (outside of player) +[2011-05-12 00:23:04] <ichthyo> yes, thats the next sub-topic I wanted to discuss +[2011-05-12 00:23:16] <cehteh> and a 3rd prio queue in the scheduler which is short and hard(er) realtime +[2011-05-12 00:23:21] <ichthyo> because e.g. we need such for animating the GUI +[2011-05-12 00:23:28] <ichthyo> i.e. show the playback position +[2011-05-12 00:23:29] <cehteh> but only the player knows the times +[2011-05-12 00:24:18] <cehteh> this 'timer' possibly wont be singleton too .. you can play unrelated things in parallel +[2011-05-12 00:24:51] <ichthyo> well... I'd rather expect that to be quite similar to the jobs +[2011-05-12 00:25:26] <cehteh> well i wonder if the normal jobs become bit more heavyweight +[2011-05-12 00:25:40] <cehteh> and the high prio queue gets its own interface +[2011-05-12 00:26:00] <cehteh> no full blown jobs, no waiting (inter-jobs) +[2011-05-12 00:26:10] <ichthyo> what do you think to add to the normal jobs, beyond what we just discussed? +[2011-05-12 00:26:15] <ichthyo> ah maybe the dependencies +[2011-05-12 00:26:23] <ichthyo> but besides that? +[2011-05-12 00:26:30] <cehteh> just deliver function calls as precice as possible with hard constraints +[2011-05-12 00:26:55] <cehteh> dunno .. lets see could use the same job structure, or a special one +[2011-05-12 00:27:00] <ichthyo> ok +[2011-05-12 00:27:02] <cehteh> no aborting there too +[2011-05-12 00:27:23] <ichthyo> but maybe lets first finish the question above +[2011-05-12 00:27:39] <ichthyo> where starts the part which you care for? +[2011-05-12 00:27:52] <cehteh> struct tinyjob { time when; void (*function)(void* data); void* data;} +[2011-05-12 00:27:59] <ichthyo> did I understand you correct, that you rather don't want to care for the calculation streams +[2011-05-12 00:28:07] <ichthyo> the callbacks and this stuff? +[2011-05-12 00:28:31] <cehteh> yes i think you can abstract them better, you have knowledge about the session and user interaction +[2011-05-12 00:28:37] <ichthyo> (I have no problem taking that responsibility, and implement it on top of the bare jobs) +[2011-05-12 00:28:50] <ichthyo> ok +[2011-05-12 00:29:03] <cehteh> so there will be the full blown jobs +[2011-05-12 00:29:27] <cehteh> which can record dependencies on other jobs, have their state, a jobfunction +[2011-05-12 00:29:38] <ichthyo> so that means, that I will also care for setting up the render nodes/invocation, before packaging that into a job function, right? +[2011-05-12 00:29:55] <cehteh> (and scheduling times, possibly a job can be in both queues!) +[2011-05-12 00:29:57] <ichthyo> which would make sense, because I also implemented the render nodes themselves +[2011-05-12 00:30:02] <cehteh> yes +[2011-05-12 00:30:26] <cehteh> possibly this setting up is a job on its own +[2011-05-12 00:30:26] <daylife> i'm going to sleep. g'night everybody +[2011-05-12 00:30:31] <cehteh> n8 daylife :) +[2011-05-12 00:30:32] <ichthyo> and we've discussed that topic already several times. This invocation will have kind-of two stages +[2011-05-12 00:30:38] <ichthyo> good night daylife ! +[2011-05-12 00:30:40] <cehteh> or even more +[2011-05-12 00:30:43] |<-- daylife has left kornbluth.freenode.net (Quit: gotta go) +[2011-05-12 00:30:43] <ichthyo> yes +[2011-05-12 00:31:08] <ichthyo> I mean, the first stage is just finding out the dependencies, other constraints etc and it ends with having created a suitable job +[2011-05-12 00:31:19] <cehteh> yes +[2011-05-12 00:31:22] <ichthyo> ok +[2011-05-12 00:31:42] <cehteh> you can do things syncronously if you know that they are very fast in all cases +[2011-05-12 00:31:54] <cehteh> or async with jobs if thats not known +[2011-05-12 00:32:33] <ichthyo> and, besides, there is also the possibility to use a job as a "continuation" (or functor) +[2011-05-12 00:32:43] <cehteh> yes absolutely +[2011-05-12 00:32:55] <skangas> Hello! +[2011-05-12 00:33:02] <cehteh> prefetching would be also a job btw +[2011-05-12 00:33:08] <ichthyo> Hello skangas ! +[2011-05-12 00:33:11] <cehteh> hi skangas +[2011-05-12 00:33:16] <ichthyo> is that good morning or good night? +[2011-05-12 00:33:30] <ichthyo> ;-) +[2011-05-12 00:33:33] <skangas> Good night :-) +[2011-05-12 00:33:41] <skangas> But now I am here finally at least. +[2011-05-12 00:34:00] <cehteh> the exact setup for scheduling will likely become little more complicated +[2011-05-12 00:34:46] <cehteh> you dont just set a time when to render but timespans and priorities, possibly twice for each queue +[2011-05-12 00:35:33] <cehteh> at least my plan is that a job can be in the background render queue and in the high prio queue (we should give this a better name) at once +[2011-05-12 00:36:52] <cehteh> this interface needs to be discussed ... but for now it suffices that we know its there +[2011-05-12 00:37:08] <cehteh> (also a reason to make the tinyjobs which are much simpler) +[2011-05-12 00:38:44] <ichthyo> skangas: how's life? everything ok-ish for you? +[2011-05-12 00:41:34] <ichthyo> cehteh: how would you deal with that 'animating' of the gui during playback? +[2011-05-12 00:41:49] <ichthyo> that is, updating the time, moving the playhead cursor etc. +[2011-05-12 00:42:02] <ichthyo> could that be a job too? +[2011-05-12 00:42:21] <cehteh> for blinking cursors? :) +[2011-05-12 00:42:31] <ichthyo> ;-) +[2011-05-12 00:42:38] <cehteh> well summarize: +[2011-05-12 00:43:08] <cehteh> we use the 'normal' queues for rendering .. there are job dependencies which eventually yield in a frame rendered +[2011-05-12 00:44:08] <cehteh> the actualy 'playout' whatever that means is done by the realtime tinyjobs from a special time-syncronized queue +[2011-05-12 00:44:32] <cehteh> this is continutation based +[2011-05-12 00:45:09] <cehteh> the last thing in the normal queues is setup this playout (if still in time) +[2011-05-12 00:45:43] <cehteh> blinking the gui doesnt need to be hard realtime it could be set up there too .. or can you inject gui messages from other threads? +[2011-05-12 00:46:05] <cehteh> we wanted a short queue between gui and proc right? +[2011-05-12 00:46:18] <ichthyo> thats something different IMHO +[2011-05-12 00:46:22] <ichthyo> it is GUI -> proc +[2011-05-12 00:46:28] <cehteh> ah not bidir? +[2011-05-12 00:46:32] <cehteh> (2 queues) +[2011-05-12 00:46:45] <ichthyo> well, didn't think about that yet +[2011-05-12 00:46:49] <ichthyo> see +[2011-05-12 00:46:56] <ichthyo> in GTK, you invoke so called "signals" +[2011-05-12 00:47:22] <ichthyo> which are functors. And they are wired such that the actual function is then dispatched from the event thread +[2011-05-12 00:47:32] <cehteh> yes but you can also integrate some custom queue which generates signals i guess +[2011-05-12 00:47:57] <cehteh> in some or another way +[2011-05-12 00:48:02] <ichthyo> you can create that signals from any thread +[2011-05-12 00:48:18] <ichthyo> GTK cares to execute them through his event mechanism +[2011-05-12 00:48:44] <cehteh> this signals can carry some payload (rendered thumbnail?) +[2011-05-12 00:48:56] <ichthyo> they are function objects +[2011-05-12 00:49:02] <ichthyo> so they can do anything they want +[2011-05-12 00:49:32] <ichthyo> typically you bind a method from some existing object +[2011-05-12 00:50:35] <ichthyo> thus +[2011-05-12 00:50:50] <cehteh> so we may add a notification layer there to give it the right distance for abstraction .. and the gui can maybe subscribe to certain notification as well as things which are queried by the gui are send back there +[2011-05-12 00:51:02] <ichthyo> for our problem that means that there will be some signal(s) to update the timecode +[2011-05-12 00:51:07] <ichthyo> yes, my thinking too +[2011-05-12 00:51:23] <ichthyo> there is already even an interface called "GuiNotificationFacade" +[2011-05-12 00:51:30] <ichthyo> its defined through the interface system +[2011-05-12 00:51:41] <ichthyo> just Joel didn't get around filling it with life +[2011-05-12 00:51:44] |<-- andrewjames has left kornbluth.freenode.net (Ping timeout: 258 seconds) +[2011-05-12 00:51:49] <cehteh> so you just hook that into the jobs you (the player) creates +[2011-05-12 00:51:51] <ichthyo> that was shortly before he left +[2011-05-12 00:51:54] <ichthyo> yes +[2011-05-12 00:52:02] <ichthyo> point is: it won't block +[2011-05-12 00:52:11] <ichthyo> but it /has/ some overhead for sure +[2011-05-12 00:52:31] <ichthyo> (so nothing for a realy realtime constrained dispatch) +[2011-05-12 00:53:05] <ichthyo> e.g. ther will be heap allocations involed, somewhere within the framework, and so on +[2011-05-12 00:53:11] <cehteh> well if we miss a deadline we miss it, there is no time machine or other magic build in +[2011-05-12 00:53:37] <ichthyo> well ok +[2011-05-12 00:53:41] <cehteh> and the gui doesnt run in realtime +[2011-05-12 00:53:45] <cehteh> the sound does +[2011-05-12 00:53:58] <ichthyo> I was just concerned if that time queue has tight constraints +[2011-05-12 00:54:01] <cehteh> and maybe the video player (just blitting rendered frames) does +[2011-05-12 00:54:28] <ichthyo> ...then I rather would put these GUI update actions into a normal foreground job queue +[2011-05-12 00:54:32] <cehteh> there is nothing we can do than reducing the load +[2011-05-12 00:54:38] <cehteh> yes +[2011-05-12 00:54:49] <cehteh> i wont put these into the time queue +[2011-05-12 00:55:17] <cehteh> by definition you can never bink the cursor too early :) +[2011-05-12 00:56:13] <cehteh> and anything not sound and video playback, just gui update related can be lets say 50ms off time +[2011-05-12 00:56:24] <ichthyo> yeah +[2011-05-12 00:56:41] <cehteh> for sure it would be a bit pita when automation curves lag noticeable +[2011-05-12 00:56:55] <ichthyo> so it could even be enqueued with a lower priority than frame calculations scheduled for the same time +[2011-05-12 00:57:05] <cehteh> but if we cant render them in time then we cant do that, nothing we can fix there than reducing the load somehow +[2011-05-12 00:57:49] <cehteh> (besides automation curves might be rendered by the gui alone, no need for backend and proc) +[2011-05-12 00:58:09] <ichthyo> yeah +[2011-05-12 00:58:28] <ichthyo> well... cinelerra e.g. scrolls the whole timeline pane keeping the cursor in the middle +[2011-05-12 00:58:36] <cehteh> there are no much things which are time constraind in the gui but not video or sound +[2011-05-12 00:58:37] <ichthyo> this is nice for the handling +[2011-05-12 00:58:46] <cehteh> and depend on backend/proc +[2011-05-12 00:59:16] <ichthyo> anyway... that means that there might be thumbnails to process, and I take it that we handle that somehow through our engine as well, just with lower priority +[2011-05-12 00:59:23] <cehteh> yes that costs a lot performance there .. +[2011-05-12 01:00:01] <ichthyo> as a *user* I really don't know what to prefer +[2011-05-12 01:00:10] <cehteh> btw if frames or thumbnails have higher priority might be a feature of 'perspectives' +[2011-05-12 01:00:24] <ichthyo> an application which burns quite some resources, but gets me a steady updating display +[2011-05-12 01:00:44] <ichthyo> or an application which freezes every 10 seconds when the cursor hits the right window border +[2011-05-12 01:00:54] <cehteh> haha +[2011-05-12 01:00:56] <ichthyo> (e.g. ardour has this behaviour) +[2011-05-12 01:00:59] <cehteh> neither is good +[2011-05-12 01:02:03] <ichthyo> probably the best solution (also the rather expensive to implement) is to make that GUI update in several stages, and each with a different priority +[2011-05-12 01:02:18] <cehteh> i dont think so +[2011-05-12 01:02:22] <ichthyo> e.g. a wireframe has high priority, thumbnails and waveforms have low priority +[2011-05-12 01:02:36] <cehteh> because if you cant do it in time, you cant do it in time :P +[2011-05-12 01:03:29] <cehteh> well the timeline can be rendere into an extra canvas .. then displaying it with scrolling is certainly more efficent than redraw it completely 60 times a second +[2011-05-12 01:03:37] <ichthyo> well... you could skip the thumbnails, yet still have an animated cursor and running timecode +[2011-05-12 01:04:15] <cehteh> yes but you dont need to care in the gui .. you just send the jobs with configured piority and deadline and render when the data arrives +[2011-05-12 01:04:29] <ichthyo> yeah, thats what I mean +[2011-05-12 01:04:30] <cehteh> if it couldnt be rendered then there are no thumbails +[2011-05-12 01:04:47] <cehteh> ah ok i took that for granted +[2011-05-12 01:05:01] <ichthyo> I too take that for granted, but actually +[2011-05-12 01:05:10] <ichthyo> it doesn't seem to be obvious for many people +[2011-05-12 01:05:18] <ichthyo> e.g. Joel resisted to that idea +[2011-05-12 01:05:33] <ichthyo> and if you look into the ardour sourcecode, they didn't get it right either +[2011-05-12 01:06:00] <cehteh> heh +[2011-05-12 01:06:06] <cehteh> i am not gui programmer +[2011-05-12 01:06:53] <cehteh> but if, i would make a timeline in a canvas (or even multiple layers, automation on top) and render that in background even with some safe areas around wich are not visible +[2011-05-12 01:07:09] <cehteh> then scolling in is just a fast hardware operation +[2011-05-12 01:07:36] <ichthyo> exactly, thats the way I think we should do it +[2011-05-12 01:07:51] <cehteh> well i was already suprised that no one pre-renders menus when the mouse is hovering over them and when the user clicks displaying them in instant +[2011-05-12 01:07:52] <ichthyo> ok... the key point is to do that rendering asynchroneously +[2011-05-12 01:08:05] <cehteh> thats how i would do it +[2011-05-12 01:08:47] <ichthyo> makes many GUI programmers feel queasy +[2011-05-12 01:08:58] <cehteh> hey we all like snappy guis +[2011-05-12 01:09:48] <ichthyo> actually, I must say it's kind of embarrasing when we consider the level of functionality provided by many GUI toolkits even today +[2011-05-12 01:10:30] <ichthyo> basically the technology from 20 years ago +[2011-05-12 01:10:35] <cehteh> yes +[2011-05-12 01:10:53] <cehteh> and even with wayland and all new things it not going to change that fundamentals +[2011-05-12 01:11:02] <ichthyo> yes, indeed +[2011-05-12 01:11:20] <ichthyo> that is just an attempt to make that old model running faster, by offloading to the hardware +[2011-05-12 01:12:03] <cehteh> i would like a compositor on the toolkit level ... not as window server +[2011-05-12 01:13:01] <ichthyo> anyway +[2011-05-12 01:13:02] <cehteh> every little widget state is some canvas somewhere which i can update async (playing video on a button?) and then the whole gui is composited in a final step with all building blocks +[2011-05-12 01:13:14] <cehteh> instead re-rendered as whole +[2011-05-12 01:13:14] <ichthyo> yes +[2011-05-12 01:13:19] <ichthyo> and even more +[2011-05-12 01:13:28] <ichthyo> the parts should be composed in a functional way +[2011-05-12 01:13:47] <ichthyo> and the toolkit would then invoke that functors in the right order and async +[2011-05-12 01:13:53] <cehteh> yep +[2011-05-12 01:14:07] <cehteh> ok .. anyways something else for today? +[2011-05-12 01:14:21] <ichthyo> seems we're through with that discussion +[2011-05-12 01:14:39] <ichthyo> let's call it a day (or a night) +[2011-05-12 01:14:46] <cehteh> the 'actual' interfaces how the backend queries a frame from proc and how proc asks the backend for a frame .. do we need to work this out? +[2011-05-12 01:14:51] <cehteh> not today +[2011-05-12 01:15:26] <cehteh> but i think about not working it offically out at all in a big way, this interfaces prolly just happen and can be improved in small details +[2011-05-12 01:16:10] <ichthyo> in the (half finished) code in the ProcNode, I used placeholder / facade interfaces +[2011-05-12 01:16:21] <ichthyo> that is enough to set up the backbone +[2011-05-12 01:16:52] <ichthyo> that is, for node -> backend (to get a frame) +[2011-05-12 01:17:12] <cehteh> ah ok +[2011-05-12 01:17:18] <ichthyo> the other one will turn out, when I care for wiring up such a job, as we discussed today +[2011-05-12 01:17:31] <ichthyo> that is engine/player -> proc +[2011-05-12 01:17:43] <cehteh> well i wonder if we have to pass timing information there ? +[2011-05-12 01:17:48] <cehteh> well see that next +[2011-05-12 01:17:50] <ichthyo> "node invocation interface" +[2011-05-12 01:18:22] <ichthyo> well, personally I think I'll first concentrate on the player, to get a rough outline there +[2011-05-12 01:18:36] <cehteh> yes that will show us the requirements +[2011-05-12 01:18:43] <ichthyo> and from there to this engine interface we discussed today +[2011-05-12 01:19:01] <cehteh> which is more a concept than a interface :P +[2011-05-12 01:19:15] <ichthyo> and if I recall right, you were rather engaged into the framecache / frame database +[2011-05-12 01:20:10] <cehteh> the general interface is simple (in my head) .. but there are some dirty corners to worked out +[2011-05-12 01:20:33] <ichthyo> for creating jobs, and the scheduler? +[2011-05-12 01:20:42] <ichthyo> or do you mean the framecache? +[2011-05-12 01:20:48] <cehteh> if proc asks the backend for a frame, the backend may ask proc for rendering it .. and this going pingpoing with all dependenices +[2011-05-12 01:21:32] <cehteh> but the inital query must provide enough information to construct all of the rest which involves setting up jobs which need deadlines and priorities +[2011-05-12 01:22:05] <cehteh> or we may rething this somehow +[2011-05-12 01:22:17] <cehteh> doing the ping-pong more in the higher levels +[2011-05-12 01:22:26] <cehteh> (no W then) +[2011-05-12 01:22:56] <ichthyo> well... still a W, but between two function levels a bit higher then +[2011-05-12 01:22:59] <cehteh> asking the cache if it has a frame, if not then the initiator is responsible for setting up the rendering job +[2011-05-12 01:23:08] <cehteh> might become a M then :) +[2011-05-12 01:23:15] <ichthyo> yes, kindof that +[2011-05-12 01:23:56] <cehteh> well this needs some careful thinkin thats an important design decision we can not easily revert +[2011-05-12 01:26:00] <ichthyo> well... until now I've kept that decision rather flexible. That is, in the part I wrote in the nodes +[2011-05-12 01:26:40] <cehteh> for now .. eventually this becomes nailed down +[2011-05-12 01:26:44] <ichthyo> yes +[2011-05-12 01:27:35] <ichthyo> you recall, there was the controversy if we should split and schedule every calculation step, even if it doesn't include fetching some aditional data +[2011-05-12 01:28:43] <cehteh> huh? ... well it wasnt about the fetching it was about finer granulared planning for rendering +[2011-05-12 01:28:55] <ichthyo> yes +[2011-05-12 01:29:00] <cehteh> note that when you render too early the caches cool down +[2011-05-12 01:29:15] <cehteh> (or fetch too early) +[2011-05-12 01:29:59] <cehteh> so i was about discovery before fetching, and fetching in time, and then rendering +[2011-05-12 01:30:12] <cehteh> and possibly add some more steps as need arises +[2011-05-12 01:30:27] <ichthyo> yes... that was kind of the middle ground we agreed on +[2011-05-12 01:31:10] <ichthyo> I retained the possibility to decide if and where a chain of calculations for a single frame should be split and scheduled as separate jobs +[2011-05-12 01:31:24] <cehteh> ah +[2011-05-12 01:31:37] <cehteh> now i understand .. hehe processing chain is a single job :) +[2011-05-12 01:31:46] <ichthyo> yes +[2011-05-12 01:32:01] <cehteh> well dunno if that scales badly +[2011-05-12 01:32:03] <ichthyo> and of course this requires a discovery step beforehand +[2011-05-12 01:32:11] <cehteh> imagine you can run every other frame on another core +[2011-05-12 01:32:27] <ichthyo> so thats kind of the point where we both agree +[2011-05-12 01:32:46] <cehteh> and you dont want to move data between cores if avoidable +[2011-05-12 01:33:34] <cehteh> can you do that with your processing chain? split them up into jobs rendering every other frame +[2011-05-12 01:34:01] <cehteh> or well ... +[2011-05-12 01:34:11] <ichthyo> oops, no, I think we misunderstand +[2011-05-12 01:34:15] <cehteh> run only the heavy lifting in single jobs +[2011-05-12 01:34:25] <cehteh> but the processing chain is one job +[2011-05-12 01:34:26] <ichthyo> I don't talk about 'calculation streams' +[2011-05-12 01:34:38] <ichthyo> I allways assume that 1 frame is one job +[2011-05-12 01:34:40] <ichthyo> at least +[2011-05-12 01:34:42] <cehteh> ah ok +[2011-05-12 01:34:53] <cehteh> well refine 'rendering one frame is one job' +[2011-05-12 01:35:02] <ichthyo> the controversy was to what extend a single frame should be split into multiple jobs +[2011-05-12 01:35:10] <cehteh> i dont care if discovery over a sequence is one job for multiple frames +[2011-05-12 01:36:02] <cehteh> you mean its dependencies? +[2011-05-12 01:36:41] <cehteh> ah .. now i get it caclulation streams are vertical not horizontal :P +[2011-05-12 01:36:54] <ichthyo> at that point (more than a year ago), you insisted that every calculation be done in a separate job, and I didn't agree to that +[2011-05-12 01:37:00] <cehteh> yes +[2011-05-12 01:37:08] <ichthyo> that is, even if its just the application of yet another effect to the same frame +[2011-05-12 01:37:09] <cehteh> i think we can try out and see +[2011-05-12 01:37:16] <ichthyo> yes, that was the conclusion +[2011-05-12 01:37:17] <cehteh> yes +[2011-05-12 01:37:37] <ichthyo> while it is pretty much clear that the fetching of data should be a separate job +[2011-05-12 01:37:45] <ichthyo> i.e. the IO wait +[2011-05-12 01:37:50] <cehteh> heh i assumed you meant horizonal rendering .. frames in sequenve with caclulation stream +[2011-05-12 01:38:08] <ichthyo> yes, thats what I mean +[2011-05-12 01:38:11] <cehteh> now i see you mean going down the render graoh +[2011-05-12 01:38:12] <ichthyo> but a calculation stream +[2011-05-12 01:38:26] <ichthyo> is then split into jobs, at least one job per frame +[2011-05-12 01:38:37] <cehteh> yes ok +[2011-05-12 01:38:49] <cehteh> lets see ... +[2011-05-12 01:39:12] <cehteh> prolly scales well this way, but we may need refinements for branches in the graph +[2011-05-12 01:39:18] |<-- hermanr has left kornbluth.freenode.net (Quit: hermanr) +[2011-05-12 01:39:22] <ichthyo> yeah +[2011-05-12 01:39:41] <ichthyo> especially for those cases, which combine several frames (e.g. time average, motion tracking) +[2011-05-12 01:39:58] <cehteh> with just one job per single render operation i wanted to avoid this special cases .. this then just becomes jobs waiting on other jobs +[2011-05-12 01:40:44] <ichthyo> well... the way I approach it for the ProcNode code also avoids special cases +[2011-05-12 01:40:54] <ichthyo> but requires the builder to set up things appropriately +[2011-05-12 01:41:12] <cehteh> of course if you make a vertical computation stream then you have less overhead than splitting this up into many small jobs +[2011-05-12 01:41:15] <ichthyo> hehe.... and the builder isn't written yet :-P +[2011-05-12 01:41:33] <cehteh> but i think compared to the heavy lifting in the effects this overhead will be neglectibe +[2011-05-12 01:42:30] <cehteh> and this caclulation streams shouldnt become too long as they bind resources +[2011-05-12 01:42:49] <ichthyo> you mean, "horizontally"? +[2011-05-12 01:42:57] <cehteh> nope vertically +[2011-05-12 01:43:21] <cehteh> btw i forgot something for the job_*() interface +[2011-05-12 01:43:25] <ichthyo> ok, than that would be rather chained nodes in the graph +[2011-05-12 01:43:35] <cehteh> resource transfer .. we talked about that +[2011-05-12 01:43:36] <ichthyo> calculation stream = sequence of frames +[2011-05-12 01:43:46] <cehteh> waiting for resources not jobs +[2011-05-12 01:44:26] <ichthyo> yes, of course .... I took that for granted +[2011-05-12 01:44:44] <ichthyo> otherwise it wouldn't make sense to have a separate job for fetching input data +[2011-05-12 01:45:09] <cehteh> well you remember the that writing has to be committed .. and then i wanted a short circuit to transfer a resource to another receiver than giving it back to the backend +[2011-05-12 01:45:47] <cehteh> i still dont get your calculation stream +[2011-05-12 01:46:10] <cehteh> when thats a sequence of frames and a single job then you cant calculate frames in parallel +[2011-05-12 01:46:21] <ichthyo> no, it isn't a single job +[2011-05-12 01:46:25] <cehteh> ah ok +[2011-05-12 01:46:37] <ichthyo> look at it from the viewangle of the player +[2011-05-12 01:46:52] <ichthyo> a calculation stream eventually produces a data stream (channel) +[2011-05-12 01:46:58] <ichthyo> that is the idea +[2011-05-12 01:47:31] <cehteh> if you want to accumulate render jobs then i strongly opt for doing this vertically (if ever) +[2011-05-12 01:47:40] <cehteh> never horizontally +[2011-05-12 01:47:43] <ichthyo> yes +[2011-05-12 01:48:00] <cehteh> ok then we are fine .. no matter how you name it :P +[2011-05-12 01:48:11] <ichthyo> with the exception you just mentioned: planning further jobs could be done in a sway for a segment of time +[2011-05-12 01:48:44] <cehteh> yes but with a limit .. not a full sequence from the session +[2011-05-12 01:48:49] <ichthyo> e.g. the 2* 25 jobs for the next second +[2011-05-12 01:49:03] <ichthyo> (2 per frame, one for the resourece, one for the calculation) +[2011-05-12 01:49:08] <ichthyo> in a typical standard case +[2011-05-12 01:49:28] <ichthyo> and then add a followup job to schedule the next jobs for the next second +[2011-05-12 01:49:40] <ichthyo> just as an example +[2011-05-12 01:49:52] <cehteh> lets say dont run discovery for more than 20 frames .. and dont fetch more than 5 frames and render every single frame +[2011-05-12 01:50:07] <cehteh> even if the span between cuts is longer +[2011-05-12 01:50:13] <ichthyo> yes +[2011-05-12 01:50:22] <cehteh> this values of course may be configureable +[2011-05-12 01:50:26] <ichthyo> and its clear that the actual numbers are for optimisation +[2011-05-12 01:50:28] <ichthyo> yes +[2011-05-12 01:50:39] <ichthyo> ok +[2011-05-12 01:50:44] <ichthyo> lets call it a night! +[2011-05-12 01:50:48] <cehteh> feierabend :P +[2011-05-12 01:50:50] <cehteh> n8 +[2011-05-12 01:50:54] <ichthyo> n8 +[2011-05-12 01:50:58] <cehteh> well i play abit around +[2011-05-12 01:51:13] [QUIT] Disconnected from irc://kornbluth.freenode.net/ (irc://kornbluth.freenode.net/). [[Reconnect][Reconnect to kornbluth.freenode.net][reconnect]] +---------------------------- + + diff --git a/doc/devel/meeting_summary/index.txt b/doc/devel/meeting_summary/index.txt index a90a692b2..a65295735 100644 --- a/doc/devel/meeting_summary/index.txt +++ b/doc/devel/meeting_summary/index.txt @@ -14,38 +14,34 @@ archives and other documentation. +11 May 2011 +----------- + +Topics +~~~~~~ + * detailed technical discussion about the Render Engine Interface + +Summary +^^^^^^^ + * link:2011-05-11.html[IRC Transcript (prepared by Ichthyo)] + + + + 13 Apr 2011 ----------- Topics ~~~~~~ * New Website Page Layout - - liquid layout? - - vertical scrolling? - - colour scheme? - - Webserver upgrade? ++ +liquid layout -- scrolling -- colours + * Website licensing and legal questions * Trac spam * Go through Ideas and Drafts in design process - - Application Install - - Delectus Shot Evaluator - - Clip Cataloging System - - Design Param Automation - - Lumiera Forward Iterator -> *accepted* - - Design the Render Nodes interface - - Developer Documentation Structure - - Engine Interface Overview - - Feature Bundle - - Marble Mode - - Normalized Device Coordinates - - Proc High Level Model -> *accepted* - - Placement concept -> *accepted* - - Render Optimizer, Resource Management and Profiling - - Stream Type System - - Threads Signal handling - - Session structure -- Timelines, Sequences, Output -> *accepted* - - Use Cases - - Version Number Scheme -> *accepted* ++ +accepted: ForwardIterator, HighLevelModel, Placement concept, Session structure Summary ^^^^^^^ diff --git a/doc/devel/rfc_pending/EngineInterfaceSpec.txt b/doc/devel/rfc_pending/EngineInterfaceSpec.txt index f4278929a..2b4be964e 100644 --- a/doc/devel/rfc_pending/EngineInterfaceSpec.txt +++ b/doc/devel/rfc_pending/EngineInterfaceSpec.txt @@ -121,6 +121,10 @@ and partially passed on as parameter to the individual node invocations. For example, the engine might decide to switch down to proxy media, while actually the node network will perform the actual switch and reconfiguration. +The quality of service could be implemented as a strategy, to be consulted at +various decision points. The above cases would then be just some preconfigured +default strategies. + Reconfiguration and abort ^^^^^^^^^^^^^^^^^^^^^^^^^ There needs to be a separate ``control channel'' to cause various reconfigurations @@ -212,6 +216,17 @@ Comments -------- //comments: append below +Discussed in the May developers meeting. Seems to be basically acceptable. +_Cehteh_ proposed some small adjustments: + +- making the _QualityOfService_ rather a strategy to be queried +- treating the rescheduling a bit separate from the other changes, because + that is very common and needs to be performant. +- introducing a separate scheduler/queue for time scheduled tasks, like + with rater soft realtime requirements + + So 15 Mai 2011 00:55:24 CEST Ichthyostega <prg@ichthyostega.de> + //endof_comments: diff --git a/doc/devel/template/YYYY-MM-DD-developerMeetingSummary.txt b/doc/devel/template/YYYY-MM-DD-developerMeetingSummary.txt index 28c0fafad..0b4b4a8ee 100644 --- a/doc/devel/template/YYYY-MM-DD-developerMeetingSummary.txt +++ b/doc/devel/template/YYYY-MM-DD-developerMeetingSummary.txt @@ -11,7 +11,7 @@ __Participants__ * ichthyo * ... -_Protocol written by XXXXX_ +_Summary written by XXXXX_ From 5ace6db89555aabbd4c3879baa487bf220dc031f Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 15 May 2011 03:08:37 +0200 Subject: [PATCH 040/496] shortened the transcript --- doc/devel/meeting_summary/2011-05-11.txt | 536 +++-------------------- 1 file changed, 59 insertions(+), 477 deletions(-) diff --git a/doc/devel/meeting_summary/2011-05-11.txt b/doc/devel/meeting_summary/2011-05-11.txt index b3aeec242..da235b9a0 100644 --- a/doc/devel/meeting_summary/2011-05-11.txt +++ b/doc/devel/meeting_summary/2011-05-11.txt @@ -9,6 +9,7 @@ __Participants__ * cehteh * ichthyo + * daylife _Transcript prepared by Ichthyo_ @@ -46,102 +47,11 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC .-- Discussion of details -- [caption="☉Transcript☉ "] ---------------------------- -[2011-05-11 21:08:43] [INFO] Now logging to <file:///Lager/heim/profi/fox/chatzilla/logs/kornbluth.freenode.net/channels/%23lumiera.2011-05-11.log>. -[2011-05-11 21:08:43] [INFO] Channel view for ``#lumiera'' opened. -[2011-05-11 21:08:43] === 7 unknown connection(s) -[2011-05-11 21:08:43] =-= User mode for ichthyo is now +i -[2011-05-11 21:08:44] *NickServ* This nickname is registered. Please choose a different nickname, or identify via /msg NickServ identify <password>. -[2011-05-11 21:08:47] *NickServ* You are now identified for ichthyo. -[2011-05-11 21:08:50] -->| YOU (ichthyo) have joined #lumiera -[2011-05-11 21:08:50] =-= Topic for #lumiera is ``Lumiera, the new emerging NLE for GNU/Linux | http://www.lumiera.org | Become a develper NOW'' -[2011-05-11 21:08:50] =-= Topic for #lumiera was set by hermanr on Sa 12 Sep 2009 21:08:01 CEST -[2011-05-11 21:09:01] <ichthyo> Hello folks -[2011-05-11 21:09:24] <ichthyo> dev meeting in 1 hour? -[2011-05-11 21:11:26] <ichthyo> anyway... finished my RfC -[2011-05-11 21:11:42] <ichthyo> http://lumiera.org/documentation/devel/rfc_pending/EngineInterfaceSpec.html -[2011-05-11 21:12:01] <ichthyo> I'll be away shortly, but back in time for 20:00 UTC -[2011-05-11 21:12:30] [INFO] You are now marked as away (evening stroll). Click the nickname button or use the |/back| command to return from being away. -[2011-05-11 21:32:11] -->| hermanr (~herman@cinelerra.org) has joined #lumiera -[2011-05-11 21:46:18] <hermanr> Meeting in 15? -[2011-05-11 21:46:24] <Velmont> 14 -[2011-05-11 21:46:30] <hermanr> :-) -[2011-05-11 21:47:36] <SimAV> 12! -[2011-05-11 21:49:37] <SvenL-> Hi hermanr, ichthyo, Velmont and SimAV ! -[2011-05-11 21:49:43] <Velmont> hey SvenL-! -[2011-05-11 21:49:59] <hermanr> Good evening, SvenL-! -[2011-05-11 21:50:11] <cehteh> 10 -[2011-05-11 21:50:31] <SimAV> 9 -[2011-05-11 21:50:58] * hermanr is just lurking, out of curiosity -[2011-05-11 21:51:17] * SimAV is busy, as always -[2011-05-11 21:52:35] * SvenL- is hungry -[2011-05-11 21:55:24] <Velmont> http://google.com -- and the sprite: http://www.google.no/logos/2011/graham11-hp-sprite.png -- nice work! -[2011-05-11 21:57:54] <SvenL-> It's not a gif, png don't move, not Flash, what is it ? I wondered about that when I first saw it... -[2011-05-11 21:58:00] <SvenL-> HTML5 ? -[2011-05-11 21:59:05] <hermanr> SvenL-: There are animated PNGs, yes. It's a more capable standard than animated GIFs, but not as widely supported. -[2011-05-11 21:59:19] <cehteh> javascript :P -[2011-05-11 21:59:22] <SvenL-> Oh ?! Okay -[2011-05-11 21:59:24] * ichthyo back -[2011-05-11 21:59:28] [INFO] You are no longer marked as away. -[2011-05-11 21:59:32] <ichthyo> Hi all -[2011-05-11 21:59:34] <hermanr> Yeah, prolly JavaScript. -[2011-05-11 21:59:45] <hermanr> 'evening, ichthyo -[2011-05-11 21:59:50] <hermanr> -1 minute -[2011-05-11 21:59:51] <ichthyo> 'evening hermanr -[2011-05-11 21:59:55] <SvenL-> Forgot to mention it -[2011-05-11 22:00:03] <ichthyo> how embarrasing -[2011-05-11 22:00:08] <ichthyo> dong dong -[2011-05-11 22:00:10] <SvenL-> Reminds me of Pina, the new Wenders' movie. Anybody saw it ? -[2011-05-11 22:00:34] <cehteh> wb ichthyo -[2011-05-11 22:00:36] <Velmont> SvenL-: I gave you the answer, the sprite. :-) -[2011-05-11 22:01:05] <Velmont> It is javascript who does the moving etc, yes. So CSS+Javascript mostly. :-) -[2011-05-11 22:01:32] <SvenL-> Of course, JavaScript, okay -[2011-05-11 22:01:48] <Velmont> But very cool, very fast. -[2011-05-11 22:02:01] <Velmont> Worked on my lowfi computer quick as a fox :] -[2011-05-11 22:02:15] <cehteh> well its not big -[2011-05-11 22:02:24] <cehteh> try that with a fullscreen movie :P -[2011-05-11 22:02:43] <cehteh> and i bet it break on almost any non-mainstream browser -[2011-05-11 22:02:48] <Velmont> Yesyes, -- but I mean, if they'd done it using a flash embed, it wouldn't have been as nice. -[2011-05-11 22:03:13] <cehteh> well .. html5 would be the best way :) -[2011-05-11 22:03:28] <Velmont> Nah, html5 video would also be slower. -[2011-05-11 22:03:31] <Velmont> Not so graceful ;] -[2011-05-11 22:03:41] <cehteh> and fallback to anim gif -[2011-05-11 22:03:58] <Velmont> This start in an instant. --A video would be much more heavy -[2011-05-11 22:04:08] <cehteh> didnt worked for me at a first try because i have js turned off for google -[2011-05-11 22:04:57] <cehteh> ok ... lets start the devel meeting -[2011-05-11 22:05:28] <cehteh> ichthyo: anything else than your engine interface proposal? -[2011-05-11 22:05:57] <ichthyo> maybe fsiddi shows up? -[2011-05-11 22:06:04] -->| daylife (~daylife@5350C42D.cm-6-1d.dynamic.ziggo.nl) has joined #lumiera -[2011-05-11 22:06:15] <cehteh> dunno, if yes then we can talk about something there -[2011-05-11 22:06:18] <cehteh> hey daylife -[2011-05-11 22:06:26] <ichthyo> hi daylife -[2011-05-11 22:06:43] <cehteh> btw www.pipapo.org is back, using the lumiera web infrastructure as well :) -[2011-05-11 22:06:45] <SvenL-> Didn't have the time to read your new pages, ichthyo -[2011-05-11 22:06:46] <ichthyo> well... he wrote some days ago that he is busy, but will continue with the new layout -[2011-05-11 22:06:52] <daylife> hello guys -[2011-05-11 22:07:00] * ichthyo also promised to do something for the template -[2011-05-11 22:07:03] <cehteh> i talked with him a few words past days ... -[2011-05-11 22:07:10] <ichthyo> and didn't get around to that yet -[2011-05-11 22:07:12] <ichthyo> ah -[2011-05-11 22:07:31] <cehteh> also xsinick wants to help the project, dunno when he will be back here (non programmer) -[2011-05-11 22:07:55] <cehteh> http://radar.oreilly.com/2011/05/process-kills-developer-passion.html << grin -[2011-05-11 22:08:43] <ichthyo> well... we could discuss about process, if you prefer that -[2011-05-11 22:08:46] <ichthyo> :-P [2011-05-11 22:08:54] <cehteh> ichthyo: how do we want to proceed with the engine interface, discuss it point by point or only a overview and edit comments in the rfc? -[2011-05-11 22:09:08] <ichthyo> well I thought rather the latter -[2011-05-11 22:09:12] <ichthyo> its a rough draft -[2011-05-11 22:10:12] <cehteh> nah i think we are fine with process .. including your coding standards pushing, my reluctance in some detail, .. my formalism and your pushbacks there :) -[2011-05-11 22:10:13] <SvenL-> Reading the first lines is enough to remind me of the basic concepts down here in the low-level model haha +[2011-05-11 22:09:08] <ichthyo> well I thought rather the latter...its a rough draft [2011-05-11 22:10:52] <cehteh> well generally i agree with your proposal (i'm just reading it) with some refinements/ideas i want to add -[2011-05-11 22:10:53] <ichthyo> :) -[2011-05-11 22:10:58] <ichthyo> ok -[2011-05-11 22:11:08] <ichthyo> and while we at it -[2011-05-11 22:11:27] <ichthyo> last time we went through the RfCs, and there is another Rfc, which is closely related -[2011-05-11 22:11:31] <ichthyo> but more high-level -[2011-05-11 22:11:38] <ichthyo> http://lumiera.org/documentation/devel/rfc_pending/EngineInterfaceOverview.html -[2011-05-11 22:11:59] <cehteh> yes but you are right that we need to go into details soon [2011-05-11 22:12:48] <ichthyo> well, probably the most important new view angle is that I looked on all those 'play mode changes' [2011-05-11 22:12:51] <cehteh> QoS: do we want constants there or could you think about a 'functor' which reacts more dynamic? -[2011-05-11 22:13:02] <ichthyo> of course [2011-05-11 22:13:20] <cehteh> well your proposal sounded like constants (to be defined) [2011-05-11 22:13:35] <ichthyo> yes, its constants, but it seems natural to extend that [2011-05-11 22:13:47] <cehteh> i think about a functor or even a trait-class where you can query things @@ -150,81 +60,47 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 22:14:31] <cehteh> yes [2011-05-11 22:16:02] <cehteh> then we can ask precisely "shall we render this frame now?" .. instead generally "do we render frames under timing pressure?" [2011-05-11 22:16:47] <cehteh> and of course also other questions, accuracy, syncronization and so on + [2011-05-11 22:18:17] <cehteh> the other thing is about sequences ... the backend delivers only single frames, you set up relations (this frame comes after that frame) [2011-05-11 22:18:44] <cehteh> i would like if we can put prefetching and driving sequences into the player/scheduler and not in the backend at all [2011-05-11 22:18:56] <ichthyo> yeah... I figured that there is some discrepancy [2011-05-11 22:19:33] <cehteh> well for me there is no much difference, i am already optimizing for sequences of course but there is no need to pass that explicitly -[2011-05-11 22:19:33] <ichthyo> so somehow we'd fine a way to come from such sequences down to the individual frames which can be deliverd -[2011-05-11 22:19:46] <ichthyo> find* +[2011-05-11 22:19:33] <ichthyo> so somehow we'd find a way to come from such sequences down to the individual frames which can be deliverd [2011-05-11 22:20:22] <ichthyo> yet there is one nasty problem -[2011-05-11 22:20:40] <cehteh> my thinking is that every frame gets a 'job' ... all managed by player/scheduler and this jobs become canceled if not actual anymore [2011-05-11 22:20:42] <ichthyo> all those changes I'm writing about here are more or less connected to such a sequence a s awhole -[2011-05-11 22:20:54] <ichthyo> yes, that was the general idea -[2011-05-11 22:20:59] <ichthyo> each frame is a job -[2011-05-11 22:21:08] <ichthyo> maybe even parts of a frame -[2011-05-11 22:21:14] <ichthyo> e.g. fetching data [2011-05-11 22:22:02] <cehteh> a 'job' .. or more precicely a 'frame-render-job' can have some state, like references to related (following) jobs or so on -[2011-05-11 22:22:12] <ichthyo> ah ok [2011-05-11 22:22:15] <cehteh> i am not sure yet if we want to put that there or in the player -[2011-05-11 22:22:22] <ichthyo> so we could traverse that like a data structure -[2011-05-11 22:22:27] <cehteh> yes -[2011-05-11 22:22:30] <ichthyo> yes both would be possible -[2011-05-11 22:22:49] <ichthyo> e.g think about the following scenario: [2011-05-11 22:23:04] <cehteh> maybe even the player is better but that makes the player a little more complex as it doesnt just need to shoot and forget but also care for a lot (potentially aborted) jobs + +[2011-05-11 22:22:49] <ichthyo> e.g think about the following scenario: [2011-05-11 22:23:06] <ichthyo> we're in the middle of playing, and now we want to switch to double speed playback [2011-05-11 22:23:39] <cehteh> yes then the player cancels every other frame job and changes its timebase [2011-05-11 22:23:56] <cehteh> or instead canceling it could put them on a lower priority [2011-05-11 22:24:01] <ichthyo> that means, the player/scheduler/whatever needs to find out a point in time, *when* this switch can be performed -[2011-05-11 22:24:07] <cehteh> (bacground/caching) -[2011-05-11 22:24:13] <ichthyo> and then cancle all existing jobs after that point and reschedule new jobs -[2011-05-11 22:24:14] |<-- SimAV has left kornbluth.freenode.net (Quit: signal_entry: got SIGSEGV) -[2011-05-11 22:24:20] <cehteh> yes +[2011-05-11 22:24:13] <ichthyo> and then cancel all existing jobs after that point and reschedule new jobs [2011-05-11 22:24:40] <cehteh> so that means the player might get considerably more heavy with all this bookkeeping [2011-05-11 22:24:40] <ichthyo> note I thought it helps if we don't guarantee immediate reaction [2011-05-11 22:25:00] <ichthyo> for the user it doesn't count if the speedup happens 100ms later [2011-05-11 22:25:13] <cehteh> 100ms yes ... thats noticeable [2011-05-11 22:25:22] <ichthyo> ok, 50ms or so [2011-05-11 22:25:45] <daylife> 30 ms is noticable -[2011-05-11 22:25:48] <cehteh> well it can almost instantly switch .. this jobs are 'free wheeling' as you saied -[2011-05-11 22:25:56] <ichthyo> anyway, such a change of playback might require some preparation, i.e. fetch other raw data -[2011-05-11 22:26:15] <cehteh> yes -[2011-05-11 22:26:40] <ichthyo> so we might trade a (tiny) delay against a smooth continuation without jerk [2011-05-11 22:26:41] <cehteh> there should be a fast-path for time and priority rescheduling [2011-05-11 22:27:33] <cehteh> fast path == explcit interface for only that purpose [2011-05-11 22:27:36] <daylife> I'm not sure what you're discussing exactly, but one of the things that bothers me most about the Lightworks beta is the noticable lag when navigating the timeline, playing, etc, in contrast to Avid, which immediately, no delay whatsoever, responds to any user action -[2011-05-11 22:27:57] <cehteh> daylife: haha :P .. -[2011-05-11 22:28:16] <cehteh> dinosaurs have a lot inertia :) -[2011-05-11 22:28:31] <ichthyo> :-D -[2011-05-11 22:28:33] <cehteh> well yes thats related to what we discuss ... [2011-05-11 22:29:01] <cehteh> user actions should give *instant* feedback .. even if this feedback is only fake feedback in corner cases [2011-05-11 22:29:21] * ichthyo nods [2011-05-11 22:29:22] <daylife> editing is on that part a bit like playing piano. if there were any delay, no matter how short, between pressing a key and hearing a note, it would be very irritating [2011-05-11 22:29:32] <cehteh> exacty [2011-05-11 22:29:39] <hermanr> Indeed -[2011-05-11 22:29:53] <cehteh> well .. except you play pipe organ :P -[2011-05-11 22:30:01] <daylife> :D -[2011-05-11 22:30:01] <ichthyo> hah -[2011-05-11 22:30:32] <ichthyo> thats the reason why today the organ players largely prefer mechanical action -[2011-05-11 22:30:46] <cehteh> anyways .. you press a key and you hear that note (which might be syntezized, instead the real pipe if not deliverable) -[2011-05-11 22:30:51] <ichthyo> because then you can at least feel when the valve in the air channel opens -[2011-05-11 22:31:24] <cehteh> and i bet even avid sometimes can not deliver -[2011-05-11 22:31:26] <ichthyo> thats an example for such a 'fake' feedback -[2011-05-11 22:31:32] <ichthyo> anyway -[2011-05-11 22:31:32] <cehteh> yes -[2011-05-11 22:31:44] <ichthyo> so we might consider what is possible for us in that area -[2011-05-11 22:31:54] <ichthyo> reduced latency, that is -[2011-05-11 22:32:37] <cehteh> ichthyo: so an explicit interface for rescheduling jobs (without changing other parameters) is possiblee and required + [2011-05-11 22:33:02] <cehteh> but caveat .. maybe we need to reschedule dependent jobs too [2011-05-11 22:33:24] <ichthyo> yeah... likely [2011-05-11 22:33:38] <cehteh> so far you wanted to provide jobs in a conflictless nondeadlocking way -[2011-05-11 22:33:56] <ichthyo> at least on some level, yes [2011-05-11 22:34:08] <cehteh> but for rescheduling that means likely that dependency information needs to be passed and maintained in the scheduler as well [2011-05-11 22:34:12] <ichthyo> that doesn't need to be immediately the level within the scheduler [2011-05-11 22:34:29] <cehteh> well it would be much easier if it is [2011-05-11 22:35:09] <ichthyo> well... thus the idea to allow for a (small) tolerance interval [2011-05-11 22:35:14] <cehteh> then rescheduling automatically walks all dependencies, reschedules them too and by that it can already see if we can met timing constraints -[2011-05-11 22:35:27] <ichthyo> i.e. the scheduler can take his time to find out *when* he is able to perform the change -[2011-05-11 22:35:58] <cehteh> i think thats not explicitly needed [2011-05-11 22:36:37] <cehteh> the 'reschedule' can answer if rescheduling is possible [2011-05-11 22:37:07] <cehteh> so you just iterate over all frame-job to be rescheduled from shortest to longest time [2011-05-11 22:37:36] <cehteh> the most urgend reschedules may return "hey this job is already running, cant reschedule" @@ -235,29 +111,17 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 22:39:39] <ichthyo> ok, but now, just by doing that walk, we've found out a time point when the change will happen [2011-05-11 22:40:33] <ichthyo> and that would be the information interesting for the layers above [2011-05-11 22:40:56] <ichthyo> e.g. to change the animation speed of the cursor, or whatever -[2011-05-11 22:41:22] <ichthyo> or more concrete -[2011-05-11 22:41:23] <cehteh> so .. rescheduling is fast and synchronous [2011-05-11 22:41:40] <cehteh> if you want async rescheduling you make that a job by itself +[2011-05-11 22:42:41] <cehteh> so for the player it might reschedule the next 500ms worth of frames synchronously and any later frames (background rendering) asynchronously [2011-05-11 22:42:04] <ichthyo> or more concrete... when we do an 'abort' [2011-05-11 22:42:12] <ichthyo> e.g. because the user changed the model [2011-05-11 22:42:24] <ichthyo> then I'm sure I need that asynchroneous feedback -[2011-05-11 22:42:41] <cehteh> so for the player it might reschedule the next 500ms worth of frames synchronously and any later frames (background rendering) asynchronously [2011-05-11 22:43:14] <ichthyo> ...i.e. that other part doesn't have a timer or scheduler on its own, but rather relies on that callback [2011-05-11 22:43:19] <cehteh> we never abort 'running' jobs .. only waiting or scheduled jobs -[2011-05-11 22:43:28] <ichthyo> of course [2011-05-11 22:43:48] <ichthyo> but we can (and need) to 'abort' whole calculation streams -[2011-05-11 22:43:49] <cehteh> this aborts are not the fast-path rescheduling, prolly fast to but another interface -[2011-05-11 22:43:57] <ichthyo> yeah -[2011-05-11 22:44:09] <cehteh> thats a method on the job in OO speak -[2011-05-11 22:44:18] <cehteh> aborting is really fast -[2011-05-11 22:44:28] <cehteh> because it only sets a flag in the job -[2011-05-11 22:44:29] <ichthyo> and, as said, in that case I'd need such a callback to happen *when* the abort is done -[2011-05-11 22:45:01] <ichthyo> because only then can I release the nodes and other wiring tied to the old state prior to the change -[2011-05-11 22:45:01] <cehteh> the job stays in the scheduler (or wait list) and if it becomes disposed when it would be schedules -[2011-05-11 22:45:06] <ichthyo> thats just an example + [2011-05-11 22:45:27] <cehteh> do you really need that feedback? [2011-05-11 22:45:47] <cehteh> how about 'abort' gives instant feedback .. -[2011-05-11 22:45:49] <ichthyo> not 100% sure, but I rather think yes [2011-05-11 22:45:53] <cehteh> or even 'never fails' [2011-05-11 22:46:09] <cehteh> just abort a job and consider it dead from your point of view [2011-05-11 22:46:14] <ichthyo> note: the point is: in that other part triggering the 'abort' @@ -265,34 +129,19 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 22:46:44] <ichthyo> so it doesn't help to know *when* the abort is executed [2011-05-11 22:47:03] <ichthyo> but I need to be notified *after* it has happened [2011-05-11 22:47:09] <cehteh> jobs are self containing, any resources they need are attached to the job, they should not reference any volatile data up in proc -[2011-05-11 22:47:39] <ichthyo> directly, yes of course -[2011-05-11 22:47:47] <ichthyo> but indirectly thats impossible -[2011-05-11 22:47:58] <cehteh> yes so thats the question .. you just call 'job_abort()' which returns immediately and the job can be considered aborted from your pow -[2011-05-11 22:48:36] <ichthyo> well.. I'd prefer to get an async callback, *after* the abort has really happened -[2011-05-11 22:48:44] <cehteh> well can do that -[2011-05-11 22:48:52] <ichthyo> i.e. schedule a callback job? -[2011-05-11 22:48:56] <cehteh> (but why?) -[2011-05-11 22:49:24] <cehteh> each job has a 'job-function' callback .. that can be anything -[2011-05-11 22:49:46] <cehteh> when you abort a job you can dispose the intended function by a abort callback +[2011-05-11 22:47:39] <ichthyo> directly, yes of course -- but indirectly thats impossible [2011-05-11 22:49:49] <ichthyo> in that concrete example, quite simply, because the higher layers can do some stuff only after the jobs are gone [2011-05-11 22:49:57] <ichthyo> e.g. close the session [2011-05-11 22:50:03] <cehteh> really? [2011-05-11 22:50:25] <ichthyo> well... I can't just blow away a session which is still used by ongoing jobs -[2011-05-11 22:50:30] <cehteh> i was thinking about this beeing very separate -[2011-05-11 22:50:31] <ichthyo> indirectly of course -[2011-05-11 22:50:42] <cehteh> yes i thought so [2011-05-11 22:51:04] <cehteh> jobs have no backpointers to the session [2011-05-11 22:51:05] <ichthyo> of course I don't mutate the data which is referred by the jobs -[2011-05-11 22:51:38] <cehteh> then you can just close the session .. if that syncronizes on some resources still in use by jobs (but managed elsewhere) the close will block -[2011-05-11 22:51:42] <ichthyo> ah good point [2011-05-11 22:52:09] <cehteh> nah not only reference data i am really thinking that jobs have absolutely no references into the session [2011-05-11 22:52:18] <cehteh> they have references into the backend data [2011-05-11 22:52:23] <cehteh> but these are managed there [2011-05-11 22:52:37] <cehteh> while closing a session doesnt require to shutdown the backend [2011-05-11 22:53:01] <ichthyo> well, so to clarify that: there *is* some kind of back reference for sure. Just it isn't direct -[2011-05-11 22:53:11] <cehteh> which one? -[2011-05-11 22:53:12] <ichthyo> see: a job references an exit node -[2011-05-11 22:53:33] <cehteh> mhm +[2011-05-11 22:53:12] <ichthyo> see for example: a job references an exit node [2011-05-11 22:53:35] <ichthyo> and it references an output slot [2011-05-11 22:53:38] <cehteh> really? [2011-05-11 22:53:47] <cehteh> how about a functor there @@ -304,62 +153,26 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 22:55:30] <ichthyo> well... as said: jobs can't be aborted [2011-05-11 22:55:38] <ichthyo> but whole calculation streams can [2011-05-11 22:55:42] <cehteh> if you place a job to be executed in 1hour and then abort this job, this abort callback might be called not earlier than in one hour (or if your really want we can add a time-limit there for rescheduling) -[2011-05-11 22:56:04] <cehteh> from the scheduler pow every single job can be aborted -[2011-05-11 22:56:23] <ichthyo> yes, but only if it hasn't started running -[2011-05-11 22:56:31] <cehteh> makes no sense to implement this otherwise and there will be other maintenance jobs as well -[2011-05-11 22:56:38] <cehteh> yes -[2011-05-11 22:57:34] <ichthyo> so this callback job would not be scheduled to that time of the original job (1 hour in the future) but to that point when the scheduler can guarantee that non of the old jobs will become active anymore -[2011-05-11 22:57:45] <cehteh> well even a running job can be aborted from the interface view, it still continues running but the results will be discarded and it will call the callback +[2011-05-11 22:57:34] <ichthyo> no! this callback job would not be scheduled to that time of the original job (1 hour in the future) but to that point when the scheduler can guarantee that non of the old jobs will become active anymore [2011-05-11 22:57:54] <ichthyo> because then I can dispose the data, change connections, deregister output slots and so on -[2011-05-11 22:58:57] <cehteh> rescheduling has some cost, not much but its usualy more efficent not to reschedule something you want to remove from a priority queue -[2011-05-11 22:59:09] <ichthyo> yeah, of course -[2011-05-11 22:59:13] <cehteh> eventually it will become the head of the queue and then you drop it -[2011-05-11 22:59:20] <ichthyo> exactly -[2011-05-11 22:59:32] <cehteh> thats the way i want to implement it -[2011-05-11 22:59:55] <ichthyo> all I want for this respect is a guarantee that the working function of that job can't get active anymore -[2011-05-11 22:59:55] <cehteh> if you want to reschedule a aborted job i can add a flag or a time parameter to that interface -[2011-05-11 23:00:10] <cehteh> get? executed? -[2011-05-11 23:00:41] <cehteh> job_abort() can return "job already running" or "aborted" .. .both in instant +[2011-05-11 22:59:55] <ichthyo> thus all I want for this respect is a guarantee that the working function of that job can't get active anymore [2011-05-11 23:00:41] <ichthyo> do you see my point? [2011-05-11 23:00:46] <cehteh> yes i see your point [2011-05-11 23:00:55] <cehteh> adding that callback functionality -[2011-05-11 23:01:16] <cehteh> well the only case is "job already running" .. [2011-05-11 23:01:33] <ichthyo> it doesn't help me to know *now* that 3 frames into the future will still be calculated and delivered, but starting with the 4.th frame, the abort will be in effect [2011-05-11 23:01:44] <ichthyo> this information doesn't help me *now* -[2011-05-11 23:01:48] <cehteh> yes [2011-05-11 23:02:09] <ichthyo> because, until these 3 frames are delivered, I can't deregister the "output slot" [2011-05-11 23:02:17] <ichthyo> e.g. disconect an audio client from Jack server [2011-05-11 23:02:57] <cehteh> RAII :P .. garbage collecton would be better in this case [2011-05-11 23:03:07] <ichthyo> not the slightest -[2011-05-11 23:03:25] <cehteh> well yes but in a different way -[2011-05-11 23:04:00] <cehteh> anyways ok for me i add the callback -[2011-05-11 23:04:10] <ichthyo> and deregistering an external connection has nothing to do with how the resources are tracked. +[2011-05-11 23:04:10] <ichthyo> deregistering an external connection has nothing to do with how the resources are tracked. [2011-05-11 23:04:22] <ichthyo> rather I need to know *when* its safe to do so -[2011-05-11 23:04:26] -->| fsiddi (~fsiddi@host38-107-dynamic.21-79-r.retail.telecomitalia.it) has joined #lumiera -[2011-05-11 23:04:27] <cehteh> question is: do you want to reschedule aborts explicitly or shall that part of the abort interface? -[2011-05-11 23:04:46] <ichthyo> hello fsiddi ! -[2011-05-11 23:04:49] <fsiddi> hello everyone -[2011-05-11 23:04:52] <fsiddi> sorry for being late -[2011-05-11 23:04:56] <cehteh> job_abort(job); job_reschedule(job, NOW); -[2011-05-11 23:04:58] <ichthyo> no prob -[2011-05-11 23:05:27] <cehteh> looks cleaner to me than making the abort interface heavier -[2011-05-11 23:05:44] <ichthyo> fsiddi: my apologies... I din't find time yet to look into that CSS stuff I proposed -[2011-05-11 23:05:50] <cehteh> hi fsiddi -[2011-05-11 23:05:53] <ichthyo> and promised to do -[2011-05-11 23:06:04] <fsiddi> ichthyo: np. Live's being a mess for me in the last wweks -[2011-05-11 23:06:10] <ichthyo> :-/ -[2011-05-11 23:06:24] <ichthyo> hopefully not too bad... -[2011-05-11 23:07:08] <fsiddi> nono not bad -[2011-05-11 23:07:14] <fsiddi> hei cehteh -[2011-05-11 23:07:44] <fsiddi> I also did not manage to carry on the work. But I'm starting to get past some other jobs i had to do -[2011-05-11 23:07:52] <fsiddi> I really would like to finish it quickly + [2011-05-11 23:07:56] <ichthyo> cehteh: actually I'd put an adaptation layer in between. When viewed from the player (or the session for that), I only want an high-level 'abort' operation. Like I proposed in my RfC. I didn't expect to talk directly to the scheduler or any part of the engine -[2011-05-11 23:08:18] <cehteh> well i am thinking in low level terms (as usual) [2011-05-11 23:08:31] <ichthyo> so at the interface of the scheduler, that can be implemented so that it fits in best there +[2011-05-11 23:08:18] <cehteh> well i am thinking in low level terms (as usual) [2011-05-11 23:09:09] <cehteh> i write some low-level notes for the interface (nothing concrete) -[2011-05-11 23:09:22] <ichthyo> this approach with the async callback was intended for that high-level interface; just out of practical considerations -[2011-05-11 23:09:43] <ichthyo> I don't think it's a good idea when the clients using that high-level engine interface have to run a timer themselves -[2011-05-11 23:09:50] <ichthyo> just to find out when it is safe to proceed + [2011-05-11 23:09:58] <ichthyo> Just as a striking example [2011-05-11 23:10:13] <ichthyo> think about the occasional crashes with the dummy player currently in the GUI [2011-05-11 23:10:24] <ichthyo> that is exactly due to a lack of such a callback @@ -374,7 +187,6 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 23:13:34] <cehteh> but if this buffer is provided by the backend and the gui just closes down, not displaying it anymore then nothing happens, the backend knows how to manage resources [2011-05-11 23:14:29] <ichthyo> well... true, but a general mismatch [2011-05-11 23:14:34] <ichthyo> usually it works the other way round -[2011-05-11 23:14:43] <cehteh> well thinking futher, referencing an exit node is kindof violating the idea that proc is stateless :P [2011-05-11 23:14:49] <ichthyo> I don't know how it's for ALSA, but also for jack it works the other way round [2011-05-11 23:15:01] <ichthyo> all those systems assume that you have a thread running just to serve them [2011-05-11 23:15:06] <cehteh> yes @@ -383,57 +195,37 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 23:15:32] <cehteh> yes i see now [2011-05-11 23:15:54] <ichthyo> so thats an important general mismatch we should come up with a nice solution maybe [2011-05-11 23:16:13] <cehteh> but well i still wonder if we may put such a callback even more into the backend "resource released" ... -[2011-05-11 23:16:16] <ichthyo> of course it would be great, if the backend just delivers in time, right into the buffer for output [2011-05-11 23:16:18] <cehteh> mhm maybe not -[2011-05-11 23:16:39] <ichthyo> so that we don't need another buffering and copy over step on the output side [2011-05-11 23:16:48] <cehteh> has pros and cons .. not all resources have jobs associated -[2011-05-11 23:16:52] <ichthyo> ok, but maybe a short intermission +[2011-05-11 23:16:16] <ichthyo> of course it would be great, if the backend just delivers in time, right into the buffer for output +[2011-05-11 23:16:39] <ichthyo> so that we don't need another buffering and copy over step on the output side [2011-05-11 23:17:06] <cehteh> but still in real the jack buffers will come from the backend -[2011-05-11 23:17:10] <ichthyo> fsiddi: you see, we started to engage into a low level discussion [2011-05-11 23:17:13] <cehteh> so the backend can send a notify -[2011-05-11 23:17:20] <fsiddi> :) -[2011-05-11 23:17:24] <ichthyo> fsiddi: is there anything we might discuss regarding the gui right now -[2011-05-11 23:17:33] <ichthyo> or need to discuss? -[2011-05-11 23:17:35] <fsiddi> no not really -[2011-05-11 23:17:39] <fsiddi> you can go on -[2011-05-11 23:17:43] <ichthyo> same for me -[2011-05-11 23:17:44] <fsiddi> i just wanted to be present -[2011-05-11 23:18:20] <ichthyo> I just promised to look into that CSS stuff too, because of that question how to make a flexible layout with limitations -[2011-05-11 23:18:25] <ichthyo> as we discussed last time -[2011-05-11 23:18:37] <ichthyo> I still have that on my TODO -[2011-05-11 23:21:03] <ichthyo> ok, so we'll just continue with our thrilling discussion... [2011-05-11 23:21:23] <ichthyo> cehteh: AFAIK, the jack buffers come from the jack server -[2011-05-11 23:21:25] <fsiddi> yes yes go on with it! -[2011-05-11 23:21:31] <cehteh> whats a good name for a enum value when the constraints are not met? 'invalidated' +[2011-05-11 23:22:02] <ichthyo> same for any gui widgets to display video [2011-05-11 23:21:44] <ichthyo> and not from the backend [2011-05-11 23:21:49] <cehteh> ichthyo: the backend can still manage that -[2011-05-11 23:22:02] <ichthyo> same for any gui widgets to display video -[2011-05-11 23:22:06] <cehteh> yes [2011-05-11 23:22:44] <ichthyo> so why should the backend try to manage buffers, which are actually managed by an other (external) system? [2011-05-11 23:22:47] <cehteh> generally i want it the other way around that the backend allocates/mmaps buffers, but i know that there are a lot things which do it the other way around -[2011-05-11 23:22:57] <ichthyo> yes [2011-05-11 23:23:02] <cehteh> that means we can do 2 things: copying into the destination buffer [2011-05-11 23:23:11] <cehteh> or manage this destination buffers as well [2011-05-11 23:24:08] <ichthyo> or (3rd option): let the last calculation step work into those external destination buffers [2011-05-11 23:24:35] <cehteh> thats case 2 .. when we manage them, then we calculate there with our normal toolchain [2011-05-11 23:25:03] <ichthyo> not quite; we use them, but we don't manage them [2011-05-11 23:25:27] <cehteh> proc just asks for a buffer and doesnt need to care from where it comes (RAM, Graphics card, audio server ...) -[2011-05-11 23:26:08] <ichthyo> and thats not all [2011-05-11 23:26:11] <cehteh> if we dont manage them but only 'use' then then we get some more code paths for every different kind of buffer +[2011-05-11 23:26:08] <ichthyo> and thats not all [2011-05-11 23:26:14] <ichthyo> there are similar problems [2011-05-11 23:26:26] <ichthyo> e.g. jack doesn't run in our threading sytem -[2011-05-11 23:26:30] <cehteh> yes -[2011-05-11 23:26:34] <cehteh> shared memory +[2011-05-11 23:26:30] <cehteh> yes, rather shared memory [2011-05-11 23:27:04] <ichthyo> so the question is: can we even be so precise? we don't have low-latency permissions, as the jack server has [2011-05-11 23:27:27] <ichthyo> so that means some additional buffering or copying anyway [2011-05-11 23:27:31] <cehteh> we manage buffers provided by jack [2011-05-11 23:27:49] <cehteh> if we cant render in time, we cant copy in time for sure [2011-05-11 23:27:55] <ichthyo> ah... if you call that 'managing' then yes -[2011-05-11 23:28:00] <cehteh> err other way around -[2011-05-11 23:28:04] <cehteh> yes [2011-05-11 23:28:33] <cehteh> so we have different memory backend, first our own mmaped persistent file storage -[2011-05-11 23:28:37] <ichthyo> nah: 'managing' means owning, allocating, deallocating [2011-05-11 23:28:46] <cehteh> then later maybe memory on the gpu +[2011-05-11 23:28:37] <ichthyo> nah: 'managing' means owning, allocating, deallocating [2011-05-11 23:29:12] <cehteh> well we ask jack for memory and we tell jack when we dont need it anymore [2011-05-11 23:29:33] <cehteh> and i think i can do that better in the backend just like any other memory resource [2011-05-11 23:29:40] <ichthyo> but actually I don't see why we should @@ -451,15 +243,10 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 23:32:55] <cehteh> but since the backend always knows what memory is in use it can do a clean shutdown [2011-05-11 23:33:18] <cehteh> without all this callbacks and asyncronous messages [2011-05-11 23:33:34] <ichthyo> not really -[2011-05-11 23:33:49] <ichthyo> if we use external services, we're bout to the terms of these +[2011-05-11 23:33:49] <ichthyo> if we use external services, we're bound to the terms of these [2011-05-11 23:33:56] <ichthyo> same for external libraries [2011-05-11 23:34:14] <cehteh> yes -[2011-05-11 23:34:14] <ichthyo> I think its the fundamental wrong approach to try to build up such a huge centralism -[2011-05-11 23:34:30] <cehteh> it isnt huge in any way -[2011-05-11 23:34:33] <ichthyo> and trying to pseudo-manage things which we don't own -[2011-05-11 23:34:43] <ichthyo> instead of just cooperating, as eveyone else does [2011-05-11 23:35:04] <ichthyo> if we have a nice and blastingly fast mmaped file read and buffer handling for it than fine -[2011-05-11 23:35:06] <cehteh> imo this is cooperating .. but well we are not there yet [2011-05-11 23:35:41] <ichthyo> then I'd use that exactly for those sevices, which *we* write, provide and manage [2011-05-11 23:36:08] <cehteh> possibly we just do it as you saying, and we may implement it in the way as i think later (and then try out and compare) .. or stay with your way [2011-05-11 23:37:29] <ichthyo> maybe yes... probably I'd concentrate first on that core part which we provide, becasue I'd guess it is the most important one @@ -469,27 +256,11 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 23:38:48] <cehteh> but this is future and i think it is not a fundamental design decision can can be incrementally added when it makes sense and turns out to work well [2011-05-11 23:39:07] <cehteh> first i am going for the mmaped file access and nothing else [2011-05-11 23:39:07] <ichthyo> yes, probably better approaching it that way -[2011-05-11 23:39:21] <ichthyo> indeed, I think that is the most important thing -[2011-05-11 23:39:35] <cehteh> but this doesnt mean we shall not try it :) -[2011-05-11 23:40:09] <cehteh> so whats a good enum name for a job which we cant do in time? -[2011-05-11 23:40:20] <cehteh> invalidated, expired? .. (it isnt expired yet) -[2011-05-11 23:40:41] <cehteh> rejected? -[2011-05-11 23:41:07] <ichthyo> missed? -[2011-05-11 23:41:15] <ichthyo> floundered? -[2011-05-11 23:41:43] <ichthyo> lapsed? -[2011-05-11 23:42:42] <cehteh> http://pastebin.com/w7Rk8UXx -[2011-05-11 23:42:44] <cehteh> my notes :P -[2011-05-11 23:43:32] <cehteh> ah 'scheduled' state of course -[2011-05-11 23:43:55] <cehteh> well this isnt final .. there are some overlaps its more just to write down what we need -[2011-05-11 23:44:32] <ichthyo> sounds good -[2011-05-11 23:44:34] <ichthyo> question -[2011-05-11 23:44:48] <cehteh> so about QoS.. i am thinking about not caring at all for it + +[2011-05-11 23:44:34] <ichthyo> another question. [2011-05-11 23:44:51] <ichthyo> do our jobs have some mechanism to 'yield' or wait or similar? [2011-05-11 23:45:10] <ichthyo> i.e. go in a wait state (and make room for other jobs to be scheduled meanwhile)? -[2011-05-11 23:45:14] <cehteh> then you put that into the job function, when it becomes running (and can do an early abort there) -[2011-05-11 23:45:25] <cehteh> yes [2011-05-11 23:45:34] <cehteh> waiting on other jobs -[2011-05-11 23:45:43] <ichthyo> ah ok [2011-05-11 23:45:49] <cehteh> i am thinking if i make a generic waiting or only on jobs [2011-05-11 23:45:54] <ichthyo> so you call into a funciton on the lumiera scheduler [2011-05-11 23:45:55] <cehteh> i think only on jobs is good @@ -503,28 +274,15 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 23:47:27] <cehteh> yes thats possible [2011-05-11 23:48:00] <cehteh> but when a job runs it runs until its done (which might be a errorneous/abort on its own) [2011-05-11 23:48:20] <cehteh> after running a job wont become rescheduled -[2011-05-11 23:48:39] <ichthyo> yes [2011-05-11 23:48:49] <cehteh> if it turns out we need a yield i could implement it .. but not prematurely -[2011-05-11 23:48:53] <ichthyo> you know the model Erlang uses for this? -[2011-05-11 23:48:59] <cehteh> yes -[2011-05-11 23:49:06] <ichthyo> thats kind of a middle ground -[2011-05-11 23:49:14] <ichthyo> doesn't allow arbitrary blocking -[2011-05-11 23:49:24] <ichthyo> but can block waiting on the next message [2011-05-11 23:49:44] <ichthyo> but likely for our purposes we don't even need that [2011-05-11 23:50:16] <cehteh> yes and thats not domain of the scheduler [2011-05-11 23:50:32] <cehteh> jobs can block anyways [2011-05-11 23:50:40] <cehteh> in running state -[2011-05-11 23:50:53] <cehteh> no need to put them on a wit queue or whatever -[2011-05-11 23:51:18] <ichthyo> ok +[2011-05-11 23:50:53] <cehteh> no need to put them on a wait queue or whatever [2011-05-11 23:51:31] <ichthyo> but of course the goal is to write them such that they don't block -[2011-05-11 23:51:39] <cehteh> yes [2011-05-11 23:52:11] <cehteh> and the only thing worthwile to wait for in a special wait state is for some other job -[2011-05-11 23:52:26] <ichthyo> btw... how does the scheduler detect that? am I right that this rather works indirectly? -[2011-05-11 23:52:28] <ichthyo> i.e. [2011-05-11 23:52:38] <cehteh> the next question is .. do we release this waiting jobs by some signal or by a job termination? -[2011-05-11 23:52:41] <ichthyo> through the thread pooö -[2011-05-11 23:52:54] <ichthyo> pool -[2011-05-11 23:52:59] <ichthyo> good question [2011-05-11 23:53:14] <cehteh> first can be more efficent and will be more complex [2011-05-11 23:53:30] <cehteh> so for first i opt for the later, there is prolly anyways something to do [2011-05-11 23:54:02] <ichthyo> how does this 'waiting' actually work? @@ -534,61 +292,21 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 23:55:02] <cehteh> the sheduler wraps the jobfunction [2011-05-11 23:55:10] <ichthyo> ok [2011-05-11 23:55:29] <ichthyo> so that means, he gets control only when the jobfunction exits -[2011-05-11 23:55:32] <cehteh> later, when needed we can add explicit calls .. and signaling over the threads condvar -[2011-05-11 23:55:34] <ichthyo> is that correct= -[2011-05-11 23:55:37] <ichthyo> ? [2011-05-11 23:55:51] <cehteh> for simplicity and a first implementation yes i think that suffices [2011-05-11 23:55:59] * ichthyo thinks the same -[2011-05-11 23:56:18] <cehteh> btw you certainly will have some fun when a job throws -[2011-05-11 23:56:25] <ichthyo> haha -[2011-05-11 23:57:00] <ichthyo> well.. I think that pretty much figures for anyone used to such systems -[2011-05-11 23:57:02] <cehteh> make a c++ wraper .. catch the exception .. store it in the job structure, job done with error -[2011-05-11 23:57:06] <ichthyo> exactly -[2011-05-11 23:57:42] <ichthyo> thats valid even for 'regular' theads -[2011-05-11 23:57:46] <cehteh> yes -[2011-05-11 23:58:08] <ichthyo> its rarely a good idea letting an exception leak out of any such parallel runing system in an uncontrolled manner -[2011-05-11 23:58:12] <cehteh> jobs are thread .. just without that overhead in creation and finer timing and priority control -[2011-05-11 23:59:32] <cehteh> can you think about any case where a job must wait on something else than another job? -[2011-05-12 00:00:00] <cehteh> or simplified .. can you think about any resource which can not be turned into a job -[2011-05-12 00:00:47] <ichthyo> I'd say its the other way round. We define our 'jobs' to have that properties -[2011-05-12 00:00:59] <cehteh> ok -[2011-05-12 00:01:01] <cehteh> same here -[2011-05-12 00:01:02] <ichthyo> so that defines what can be done in a job an what can't -[2011-05-12 00:01:27] <ichthyo> but, answering to your question, I think we can cover all the cases usually encountered when rendering + [2011-05-12 00:01:37] <cehteh> so now we talked about the scheduler interface .. which isnt really the engine interface .. BUT: the job structure needs to carry all resources involved around [2011-05-12 00:02:34] <cehteh> think abut the W there are more interfaces involved [2011-05-12 00:02:43] <ichthyo> ok... that brings up another closely related question [2011-05-12 00:02:59] <ichthyo> the engine interface needs something like these calculation streams -[2011-05-12 00:02:59] <cehteh> first, asking the backend for a frame [2011-05-12 00:03:19] <ichthyo> that means, that the individual job needs to cary a marker, so it can be associated to a specific stream +[2011-05-12 00:02:59] <cehteh> first, asking the backend for a frame [2011-05-12 00:03:58] <cehteh> i add at least one generic void* to the job where you can put any data along [2011-05-12 00:04:12] <cehteh> type safety on the level above please :P [2011-05-12 00:05:20] <ichthyo> I'm fine with that [2011-05-12 00:05:27] <cehteh> (caveat about the lifetime and ownership of this void*) [2011-05-12 00:05:52] <cehteh> if the job owns it then we need also a cleanup hook there -[2011-05-12 00:06:17] <ichthyo> of course that means that this void* e.g. points back to, lets say an CalculationStream object -[2011-05-12 00:06:26] <cehteh> yes -[2011-05-12 00:06:33] <ichthyo> and then yes, we get again at the same situation discussed above -[2011-05-12 00:06:52] <ichthyo> regarding a cleanup hook -[2011-05-12 00:06:57] <cehteh> well i add the cleanup hook but make it optional (helps me and you just leave it NULL) -[2011-05-12 00:07:02] <ichthyo> thats fine -[2011-05-12 00:07:11] <ichthyo> regarding a cleanup hook ... I would be careful -[2011-05-12 00:07:13] <ichthyo> because -[2011-05-12 00:07:25] <ichthyo> we don't know in what thread a job is executed exactly, right? -[2011-05-12 00:07:26] <cehteh> yes i know :) -[2011-05-12 00:07:51] <cehteh> well prolly we can pin that down or make this thread-agnostic -[2011-05-12 00:08:01] <ichthyo> so I rather do not want to start with reference counts or similar (or worse) from within that cleanup hook -[2011-05-12 00:08:10] <cehteh> nah certainly not -[2011-05-12 00:08:18] <cehteh> this cleanup hook should be short and precise -[2011-05-12 00:09:01] <ichthyo> so again: I its possible to find out when the whole calculation stream is done -[2011-05-12 00:09:02] <cehteh> ah another point against yield() .. forgotten to say .. -[2011-05-12 00:09:10] <cehteh> jobs should not migrate between thread -[2011-05-12 00:09:15] <ichthyo> than we don't need to care for resource management on the level of individual jobs -[2011-05-12 00:09:27] <ichthyo> yes, good point -[2011-05-12 00:09:55] <cehteh> so KISS -[2011-05-12 00:10:05] <ichthyo> yes -[2011-05-12 00:10:11] <cehteh> a job is the smallest entity for scheduling -[2011-05-12 00:10:25] <cehteh> (the only entity) + [2011-05-12 00:10:44] <cehteh> next would be a interface how to setup a job... possibly in multiple stages [2011-05-12 00:11:59] <cehteh> job_new(); job_wait_for(another_job); job_data()=some_calculation_stream; [2011-05-12 00:12:06] <cehteh> job_arm() @@ -598,21 +316,21 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 00:13:15] <cehteh> you dont ask the backend directly for a frame you schedule a job delivering a frame [2011-05-12 00:13:29] <cehteh> the backend schedules a job rendering a frame if its not cached [2011-05-12 00:13:35] <ichthyo> yes, my thinking too + [2011-05-12 00:14:50] <cehteh> so 'job' is a baseclass .. but there can be highlevel subclasses cache_fetch_job and render_frame_job [2011-05-12 00:15:04] <ichthyo> really? [2011-05-12 00:15:11] <cehteh> i wonder .. makes sense or? -[2011-05-12 00:15:22] <cehteh> (even implemnting them in C :P) [2011-05-12 00:15:38] <ichthyo> *could* be done, but somehow goes against that simplicity [2011-05-12 00:15:53] <cehteh> i mean each of this class shares a lot, how abortion is handled and more [2011-05-12 00:16:43] <cehteh> ok on the low level you can always put a job together with all the little details needed and then arm it +[2011-05-12 00:17:35] <cehteh> i expect that we have maybe half a dozen classes of jobs [2011-05-12 00:17:03] <ichthyo> not only on the low level. That would also be the correct OO aproach [2011-05-12 00:17:14] <ichthyo> "prefer composition over inheritance" -[2011-05-12 00:17:35] <cehteh> i expect that we have maybe half a dozen classes of jobs -[2011-05-12 00:17:43] <cehteh> yes [2011-05-12 00:17:53] <ichthyo> because, if you have a care_fetch_job, etc etc, you end up switching on types [2011-05-12 00:18:13] <cehteh> ok .. leave it simple [2011-05-12 00:18:27] <ichthyo> better investigate data at the start, and then assemble a job which can just run as-is [2011-05-12 00:19:01] <cehteh> ok + [2011-05-12 00:20:00] <ichthyo> to expand on that [2011-05-12 00:20:11] <ichthyo> when we create a 'calculation stream' [2011-05-12 00:20:18] <ichthyo> we get the following informations: @@ -625,8 +343,19 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 00:21:43] <cehteh> except i would prolly take some hints for optimization [2011-05-12 00:22:17] <cehteh> so you can add at least add flags like ONESHOT, FORWARD, BACKWARD, RANDOM [2011-05-12 00:22:19] <ichthyo> taking that approach would mean that I would also have to set up the times for the jobs, right? +[2011-05-12 00:27:39] <ichthyo> where starts the part which you care for? +[2011-05-12 00:27:59] <ichthyo> did I understand you correct, that you rather don't want to care for the calculation streams +[2011-05-12 00:28:07] <ichthyo> the callbacks and this stuff? +[2011-05-12 00:28:31] <cehteh> yes i think you can abstract them better, you have knowledge about the session and user interaction +[2011-05-12 00:28:37] <ichthyo> (I have no problem taking that responsibility, and implement it on top of the bare jobs) +[2011-05-12 00:29:38] <ichthyo> so that means, that I will also care for setting up the render nodes/invocation, before packaging that into a job function, right? +[2011-05-12 00:29:55] <cehteh> (and scheduling times, possibly a job can be in both queues!) +[2011-05-12 00:29:57] <ichthyo> which would make sense, because I also implemented the render nodes themselves +[2011-05-12 00:30:02] <cehteh> yes +[2011-05-12 00:28:50] <ichthyo> ok + + [2011-05-12 00:22:48] <cehteh> we once talked about that we want a timer facility (outside of player) -[2011-05-12 00:23:04] <ichthyo> yes, thats the next sub-topic I wanted to discuss [2011-05-12 00:23:16] <cehteh> and a 3rd prio queue in the scheduler which is short and hard(er) realtime [2011-05-12 00:23:21] <ichthyo> because e.g. we need such for animating the GUI [2011-05-12 00:23:28] <ichthyo> i.e. show the playback position @@ -639,114 +368,43 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 00:26:10] <ichthyo> what do you think to add to the normal jobs, beyond what we just discussed? [2011-05-12 00:26:15] <ichthyo> ah maybe the dependencies [2011-05-12 00:26:23] <ichthyo> but besides that? -[2011-05-12 00:26:30] <cehteh> just deliver function calls as precice as possible with hard constraints -[2011-05-12 00:26:55] <cehteh> dunno .. lets see could use the same job structure, or a special one -[2011-05-12 00:27:00] <ichthyo> ok -[2011-05-12 00:27:02] <cehteh> no aborting there too -[2011-05-12 00:27:23] <ichthyo> but maybe lets first finish the question above -[2011-05-12 00:27:39] <ichthyo> where starts the part which you care for? -[2011-05-12 00:27:52] <cehteh> struct tinyjob { time when; void (*function)(void* data); void* data;} -[2011-05-12 00:27:59] <ichthyo> did I understand you correct, that you rather don't want to care for the calculation streams -[2011-05-12 00:28:07] <ichthyo> the callbacks and this stuff? -[2011-05-12 00:28:31] <cehteh> yes i think you can abstract them better, you have knowledge about the session and user interaction -[2011-05-12 00:28:37] <ichthyo> (I have no problem taking that responsibility, and implement it on top of the bare jobs) -[2011-05-12 00:28:50] <ichthyo> ok [2011-05-12 00:29:03] <cehteh> so there will be the full blown jobs [2011-05-12 00:29:27] <cehteh> which can record dependencies on other jobs, have their state, a jobfunction -[2011-05-12 00:29:38] <ichthyo> so that means, that I will also care for setting up the render nodes/invocation, before packaging that into a job function, right? -[2011-05-12 00:29:55] <cehteh> (and scheduling times, possibly a job can be in both queues!) -[2011-05-12 00:29:57] <ichthyo> which would make sense, because I also implemented the render nodes themselves -[2011-05-12 00:30:02] <cehteh> yes -[2011-05-12 00:30:26] <cehteh> possibly this setting up is a job on its own -[2011-05-12 00:30:26] <daylife> i'm going to sleep. g'night everybody -[2011-05-12 00:30:31] <cehteh> n8 daylife :) -[2011-05-12 00:30:32] <ichthyo> and we've discussed that topic already several times. This invocation will have kind-of two stages -[2011-05-12 00:30:38] <ichthyo> good night daylife ! -[2011-05-12 00:30:40] <cehteh> or even more -[2011-05-12 00:30:43] |<-- daylife has left kornbluth.freenode.net (Quit: gotta go) -[2011-05-12 00:30:43] <ichthyo> yes -[2011-05-12 00:31:08] <ichthyo> I mean, the first stage is just finding out the dependencies, other constraints etc and it ends with having created a suitable job -[2011-05-12 00:31:19] <cehteh> yes -[2011-05-12 00:31:22] <ichthyo> ok -[2011-05-12 00:31:42] <cehteh> you can do things syncronously if you know that they are very fast in all cases -[2011-05-12 00:31:54] <cehteh> or async with jobs if thats not known -[2011-05-12 00:32:33] <ichthyo> and, besides, there is also the possibility to use a job as a "continuation" (or functor) -[2011-05-12 00:32:43] <cehteh> yes absolutely -[2011-05-12 00:32:55] <skangas> Hello! -[2011-05-12 00:33:02] <cehteh> prefetching would be also a job btw -[2011-05-12 00:33:08] <ichthyo> Hello skangas ! -[2011-05-12 00:33:11] <cehteh> hi skangas -[2011-05-12 00:33:16] <ichthyo> is that good morning or good night? -[2011-05-12 00:33:30] <ichthyo> ;-) -[2011-05-12 00:33:33] <skangas> Good night :-) -[2011-05-12 00:33:41] <skangas> But now I am here finally at least. + [2011-05-12 00:34:00] <cehteh> the exact setup for scheduling will likely become little more complicated [2011-05-12 00:34:46] <cehteh> you dont just set a time when to render but timespans and priorities, possibly twice for each queue [2011-05-12 00:35:33] <cehteh> at least my plan is that a job can be in the background render queue and in the high prio queue (we should give this a better name) at once [2011-05-12 00:36:52] <cehteh> this interface needs to be discussed ... but for now it suffices that we know its there [2011-05-12 00:37:08] <cehteh> (also a reason to make the tinyjobs which are much simpler) -[2011-05-12 00:38:44] <ichthyo> skangas: how's life? everything ok-ish for you? + [2011-05-12 00:41:34] <ichthyo> cehteh: how would you deal with that 'animating' of the gui during playback? [2011-05-12 00:41:49] <ichthyo> that is, updating the time, moving the playhead cursor etc. [2011-05-12 00:42:02] <ichthyo> could that be a job too? -[2011-05-12 00:42:21] <cehteh> for blinking cursors? :) -[2011-05-12 00:42:31] <ichthyo> ;-) [2011-05-12 00:42:38] <cehteh> well summarize: [2011-05-12 00:43:08] <cehteh> we use the 'normal' queues for rendering .. there are job dependencies which eventually yield in a frame rendered [2011-05-12 00:44:08] <cehteh> the actualy 'playout' whatever that means is done by the realtime tinyjobs from a special time-syncronized queue [2011-05-12 00:44:32] <cehteh> this is continutation based -[2011-05-12 00:45:09] <cehteh> the last thing in the normal queues is setup this playout (if still in time) +[2011-05-12 00:45:09] <cehteh> the last thing in the normal queues is setup this playout (if still in time) [2011-05-12 00:45:43] <cehteh> blinking the gui doesnt need to be hard realtime it could be set up there too .. or can you inject gui messages from other threads? -[2011-05-12 00:46:05] <cehteh> we wanted a short queue between gui and proc right? -[2011-05-12 00:46:18] <ichthyo> thats something different IMHO -[2011-05-12 00:46:22] <ichthyo> it is GUI -> proc -[2011-05-12 00:46:28] <cehteh> ah not bidir? -[2011-05-12 00:46:32] <cehteh> (2 queues) -[2011-05-12 00:46:45] <ichthyo> well, didn't think about that yet -[2011-05-12 00:46:49] <ichthyo> see [2011-05-12 00:46:56] <ichthyo> in GTK, you invoke so called "signals" [2011-05-12 00:47:22] <ichthyo> which are functors. And they are wired such that the actual function is then dispatched from the event thread -[2011-05-12 00:47:32] <cehteh> yes but you can also integrate some custom queue which generates signals i guess -[2011-05-12 00:47:57] <cehteh> in some or another way [2011-05-12 00:48:02] <ichthyo> you can create that signals from any thread [2011-05-12 00:48:18] <ichthyo> GTK cares to execute them through his event mechanism [2011-05-12 00:48:44] <cehteh> this signals can carry some payload (rendered thumbnail?) [2011-05-12 00:48:56] <ichthyo> they are function objects [2011-05-12 00:49:02] <ichthyo> so they can do anything they want [2011-05-12 00:49:32] <ichthyo> typically you bind a method from some existing object -[2011-05-12 00:50:35] <ichthyo> thus [2011-05-12 00:50:50] <cehteh> so we may add a notification layer there to give it the right distance for abstraction .. and the gui can maybe subscribe to certain notification as well as things which are queried by the gui are send back there -[2011-05-12 00:51:02] <ichthyo> for our problem that means that there will be some signal(s) to update the timecode [2011-05-12 00:51:07] <ichthyo> yes, my thinking too +[2011-05-12 00:51:02] <ichthyo> for our problem that means that there will be some signal(s) to update the timecode [2011-05-12 00:51:23] <ichthyo> there is already even an interface called "GuiNotificationFacade" [2011-05-12 00:51:30] <ichthyo> its defined through the interface system [2011-05-12 00:51:41] <ichthyo> just Joel didn't get around filling it with life -[2011-05-12 00:51:44] |<-- andrewjames has left kornbluth.freenode.net (Ping timeout: 258 seconds) -[2011-05-12 00:51:49] <cehteh> so you just hook that into the jobs you (the player) creates [2011-05-12 00:51:51] <ichthyo> that was shortly before he left -[2011-05-12 00:51:54] <ichthyo> yes -[2011-05-12 00:52:02] <ichthyo> point is: it won't block -[2011-05-12 00:52:11] <ichthyo> but it /has/ some overhead for sure -[2011-05-12 00:52:31] <ichthyo> (so nothing for a realy realtime constrained dispatch) -[2011-05-12 00:53:05] <ichthyo> e.g. ther will be heap allocations involed, somewhere within the framework, and so on -[2011-05-12 00:53:11] <cehteh> well if we miss a deadline we miss it, there is no time machine or other magic build in -[2011-05-12 00:53:37] <ichthyo> well ok -[2011-05-12 00:53:41] <cehteh> and the gui doesnt run in realtime -[2011-05-12 00:53:45] <cehteh> the sound does -[2011-05-12 00:53:58] <ichthyo> I was just concerned if that time queue has tight constraints -[2011-05-12 00:54:01] <cehteh> and maybe the video player (just blitting rendered frames) does -[2011-05-12 00:54:28] <ichthyo> ...then I rather would put these GUI update actions into a normal foreground job queue -[2011-05-12 00:54:32] <cehteh> there is nothing we can do than reducing the load -[2011-05-12 00:54:38] <cehteh> yes -[2011-05-12 00:54:49] <cehteh> i wont put these into the time queue -[2011-05-12 00:55:17] <cehteh> by definition you can never bink the cursor too early :) + [2011-05-12 00:56:13] <cehteh> and anything not sound and video playback, just gui update related can be lets say 50ms off time -[2011-05-12 00:56:24] <ichthyo> yeah [2011-05-12 00:56:41] <cehteh> for sure it would be a bit pita when automation curves lag noticeable [2011-05-12 00:56:55] <ichthyo> so it could even be enqueued with a lower priority than frame calculations scheduled for the same time -[2011-05-12 00:57:05] <cehteh> but if we cant render them in time then we cant do that, nothing we can fix there than reducing the load somehow -[2011-05-12 00:57:49] <cehteh> (besides automation curves might be rendered by the gui alone, no need for backend and proc) -[2011-05-12 00:58:09] <ichthyo> yeah [2011-05-12 00:58:28] <ichthyo> well... cinelerra e.g. scrolls the whole timeline pane keeping the cursor in the middle [2011-05-12 00:58:36] <cehteh> there are no much things which are time constraind in the gui but not video or sound [2011-05-12 00:58:37] <ichthyo> this is nice for the handling @@ -761,20 +419,12 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 01:00:56] <ichthyo> (e.g. ardour has this behaviour) [2011-05-12 01:00:59] <cehteh> neither is good [2011-05-12 01:02:03] <ichthyo> probably the best solution (also the rather expensive to implement) is to make that GUI update in several stages, and each with a different priority -[2011-05-12 01:02:18] <cehteh> i dont think so [2011-05-12 01:02:22] <ichthyo> e.g. a wireframe has high priority, thumbnails and waveforms have low priority -[2011-05-12 01:02:36] <cehteh> because if you cant do it in time, you cant do it in time :P [2011-05-12 01:03:29] <cehteh> well the timeline can be rendere into an extra canvas .. then displaying it with scrolling is certainly more efficent than redraw it completely 60 times a second [2011-05-12 01:03:37] <ichthyo> well... you could skip the thumbnails, yet still have an animated cursor and running timecode [2011-05-12 01:04:15] <cehteh> yes but you dont need to care in the gui .. you just send the jobs with configured piority and deadline and render when the data arrives [2011-05-12 01:04:29] <ichthyo> yeah, thats what I mean -[2011-05-12 01:04:30] <cehteh> if it couldnt be rendered then there are no thumbails -[2011-05-12 01:04:47] <cehteh> ah ok i took that for granted -[2011-05-12 01:05:01] <ichthyo> I too take that for granted, but actually -[2011-05-12 01:05:10] <ichthyo> it doesn't seem to be obvious for many people -[2011-05-12 01:05:18] <ichthyo> e.g. Joel resisted to that idea -[2011-05-12 01:05:33] <ichthyo> and if you look into the ardour sourcecode, they didn't get it right either -[2011-05-12 01:06:00] <cehteh> heh + [2011-05-12 01:06:06] <cehteh> i am not gui programmer [2011-05-12 01:06:53] <cehteh> but if, i would make a timeline in a canvas (or even multiple layers, automation on top) and render that in background even with some safe areas around wich are not visible [2011-05-12 01:07:09] <cehteh> then scolling in is just a fast hardware operation @@ -791,19 +441,13 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 01:11:02] <ichthyo> yes, indeed [2011-05-12 01:11:20] <ichthyo> that is just an attempt to make that old model running faster, by offloading to the hardware [2011-05-12 01:12:03] <cehteh> i would like a compositor on the toolkit level ... not as window server -[2011-05-12 01:13:01] <ichthyo> anyway -[2011-05-12 01:13:02] <cehteh> every little widget state is some canvas somewhere which i can update async (playing video on a button?) and then the whole gui is composited in a final step with all building blocks -[2011-05-12 01:13:14] <cehteh> instead re-rendered as whole [2011-05-12 01:13:14] <ichthyo> yes [2011-05-12 01:13:19] <ichthyo> and even more [2011-05-12 01:13:28] <ichthyo> the parts should be composed in a functional way [2011-05-12 01:13:47] <ichthyo> and the toolkit would then invoke that functors in the right order and async [2011-05-12 01:13:53] <cehteh> yep -[2011-05-12 01:14:07] <cehteh> ok .. anyways something else for today? -[2011-05-12 01:14:21] <ichthyo> seems we're through with that discussion -[2011-05-12 01:14:39] <ichthyo> let's call it a day (or a night) + [2011-05-12 01:14:46] <cehteh> the 'actual' interfaces how the backend queries a frame from proc and how proc asks the backend for a frame .. do we need to work this out? -[2011-05-12 01:14:51] <cehteh> not today [2011-05-12 01:15:26] <cehteh> but i think about not working it offically out at all in a big way, this interfaces prolly just happen and can be improved in small details [2011-05-12 01:16:10] <ichthyo> in the (half finished) code in the ProcNode, I used placeholder / facade interfaces [2011-05-12 01:16:21] <ichthyo> that is enough to set up the backbone @@ -811,108 +455,55 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 01:17:12] <cehteh> ah ok [2011-05-12 01:17:18] <ichthyo> the other one will turn out, when I care for wiring up such a job, as we discussed today [2011-05-12 01:17:31] <ichthyo> that is engine/player -> proc -[2011-05-12 01:17:43] <cehteh> well i wonder if we have to pass timing information there ? -[2011-05-12 01:17:48] <cehteh> well see that next [2011-05-12 01:17:50] <ichthyo> "node invocation interface" [2011-05-12 01:18:22] <ichthyo> well, personally I think I'll first concentrate on the player, to get a rough outline there [2011-05-12 01:18:36] <cehteh> yes that will show us the requirements -[2011-05-12 01:18:43] <ichthyo> and from there to this engine interface we discussed today -[2011-05-12 01:19:01] <cehteh> which is more a concept than a interface :P [2011-05-12 01:19:15] <ichthyo> and if I recall right, you were rather engaged into the framecache / frame database [2011-05-12 01:20:10] <cehteh> the general interface is simple (in my head) .. but there are some dirty corners to worked out -[2011-05-12 01:20:33] <ichthyo> for creating jobs, and the scheduler? -[2011-05-12 01:20:42] <ichthyo> or do you mean the framecache? + [2011-05-12 01:20:48] <cehteh> if proc asks the backend for a frame, the backend may ask proc for rendering it .. and this going pingpoing with all dependenices [2011-05-12 01:21:32] <cehteh> but the inital query must provide enough information to construct all of the rest which involves setting up jobs which need deadlines and priorities -[2011-05-12 01:22:05] <cehteh> or we may rething this somehow +[2011-05-12 01:22:05] <cehteh> or we may rethink this somehow [2011-05-12 01:22:17] <cehteh> doing the ping-pong more in the higher levels [2011-05-12 01:22:26] <cehteh> (no W then) [2011-05-12 01:22:56] <ichthyo> well... still a W, but between two function levels a bit higher then [2011-05-12 01:22:59] <cehteh> asking the cache if it has a frame, if not then the initiator is responsible for setting up the rendering job [2011-05-12 01:23:08] <cehteh> might become a M then :) [2011-05-12 01:23:15] <ichthyo> yes, kindof that -[2011-05-12 01:23:56] <cehteh> well this needs some careful thinkin thats an important design decision we can not easily revert +[2011-05-12 01:23:56] <cehteh> well this needs some careful thinking -- thats an important design decision we can not easily revert [2011-05-12 01:26:00] <ichthyo> well... until now I've kept that decision rather flexible. That is, in the part I wrote in the nodes [2011-05-12 01:26:40] <cehteh> for now .. eventually this becomes nailed down -[2011-05-12 01:26:44] <ichthyo> yes [2011-05-12 01:27:35] <ichthyo> you recall, there was the controversy if we should split and schedule every calculation step, even if it doesn't include fetching some aditional data [2011-05-12 01:28:43] <cehteh> huh? ... well it wasnt about the fetching it was about finer granulared planning for rendering -[2011-05-12 01:28:55] <ichthyo> yes -[2011-05-12 01:29:00] <cehteh> note that when you render too early the caches cool down -[2011-05-12 01:29:15] <cehteh> (or fetch too early) +[2011-05-12 01:29:00] <cehteh> note that when you render too early the caches cool down (or fetch too early) [2011-05-12 01:29:59] <cehteh> so i was about discovery before fetching, and fetching in time, and then rendering [2011-05-12 01:30:12] <cehteh> and possibly add some more steps as need arises [2011-05-12 01:30:27] <ichthyo> yes... that was kind of the middle ground we agreed on [2011-05-12 01:31:10] <ichthyo> I retained the possibility to decide if and where a chain of calculations for a single frame should be split and scheduled as separate jobs [2011-05-12 01:31:24] <cehteh> ah -[2011-05-12 01:31:37] <cehteh> now i understand .. hehe processing chain is a single job :) -[2011-05-12 01:31:46] <ichthyo> yes -[2011-05-12 01:32:01] <cehteh> well dunno if that scales badly -[2011-05-12 01:32:03] <ichthyo> and of course this requires a discovery step beforehand -[2011-05-12 01:32:11] <cehteh> imagine you can run every other frame on another core -[2011-05-12 01:32:27] <ichthyo> so thats kind of the point where we both agree -[2011-05-12 01:32:46] <cehteh> and you dont want to move data between cores if avoidable -[2011-05-12 01:33:34] <cehteh> can you do that with your processing chain? split them up into jobs rendering every other frame -[2011-05-12 01:34:01] <cehteh> or well ... -[2011-05-12 01:34:11] <ichthyo> oops, no, I think we misunderstand [2011-05-12 01:34:15] <cehteh> run only the heavy lifting in single jobs -[2011-05-12 01:34:25] <cehteh> but the processing chain is one job -[2011-05-12 01:34:26] <ichthyo> I don't talk about 'calculation streams' [2011-05-12 01:34:38] <ichthyo> I allways assume that 1 frame is one job [2011-05-12 01:34:40] <ichthyo> at least -[2011-05-12 01:34:42] <cehteh> ah ok [2011-05-12 01:34:53] <cehteh> well refine 'rendering one frame is one job' [2011-05-12 01:35:02] <ichthyo> the controversy was to what extend a single frame should be split into multiple jobs [2011-05-12 01:35:10] <cehteh> i dont care if discovery over a sequence is one job for multiple frames -[2011-05-12 01:36:02] <cehteh> you mean its dependencies? -[2011-05-12 01:36:41] <cehteh> ah .. now i get it caclulation streams are vertical not horizontal :P [2011-05-12 01:36:54] <ichthyo> at that point (more than a year ago), you insisted that every calculation be done in a separate job, and I didn't agree to that [2011-05-12 01:37:00] <cehteh> yes [2011-05-12 01:37:08] <ichthyo> that is, even if its just the application of yet another effect to the same frame [2011-05-12 01:37:09] <cehteh> i think we can try out and see [2011-05-12 01:37:16] <ichthyo> yes, that was the conclusion -[2011-05-12 01:37:17] <cehteh> yes -[2011-05-12 01:37:37] <ichthyo> while it is pretty much clear that the fetching of data should be a separate job -[2011-05-12 01:37:45] <ichthyo> i.e. the IO wait -[2011-05-12 01:37:50] <cehteh> heh i assumed you meant horizonal rendering .. frames in sequenve with caclulation stream -[2011-05-12 01:38:08] <ichthyo> yes, thats what I mean -[2011-05-12 01:38:11] <cehteh> now i see you mean going down the render graoh -[2011-05-12 01:38:12] <ichthyo> but a calculation stream -[2011-05-12 01:38:26] <ichthyo> is then split into jobs, at least one job per frame -[2011-05-12 01:38:37] <cehteh> yes ok -[2011-05-12 01:38:49] <cehteh> lets see ... +[2011-05-12 01:37:37] <ichthyo> while it is pretty much clear that the fetching of data should be a separate job; i.e. the IO wait [2011-05-12 01:39:12] <cehteh> prolly scales well this way, but we may need refinements for branches in the graph -[2011-05-12 01:39:18] |<-- hermanr has left kornbluth.freenode.net (Quit: hermanr) -[2011-05-12 01:39:22] <ichthyo> yeah [2011-05-12 01:39:41] <ichthyo> especially for those cases, which combine several frames (e.g. time average, motion tracking) -[2011-05-12 01:39:58] <cehteh> with just one job per single render operation i wanted to avoid this special cases .. this then just becomes jobs waiting on other jobs -[2011-05-12 01:40:44] <ichthyo> well... the way I approach it for the ProcNode code also avoids special cases -[2011-05-12 01:40:54] <ichthyo> but requires the builder to set up things appropriately -[2011-05-12 01:41:12] <cehteh> of course if you make a vertical computation stream then you have less overhead than splitting this up into many small jobs -[2011-05-12 01:41:15] <ichthyo> hehe.... and the builder isn't written yet :-P -[2011-05-12 01:41:33] <cehteh> but i think compared to the heavy lifting in the effects this overhead will be neglectibe -[2011-05-12 01:42:30] <cehteh> and this caclulation streams shouldnt become too long as they bind resources -[2011-05-12 01:42:49] <ichthyo> you mean, "horizontally"? -[2011-05-12 01:42:57] <cehteh> nope vertically -[2011-05-12 01:43:21] <cehteh> btw i forgot something for the job_*() interface -[2011-05-12 01:43:25] <ichthyo> ok, than that would be rather chained nodes in the graph + [2011-05-12 01:43:35] <cehteh> resource transfer .. we talked about that -[2011-05-12 01:43:36] <ichthyo> calculation stream = sequence of frames [2011-05-12 01:43:46] <cehteh> waiting for resources not jobs [2011-05-12 01:44:26] <ichthyo> yes, of course .... I took that for granted [2011-05-12 01:44:44] <ichthyo> otherwise it wouldn't make sense to have a separate job for fetching input data [2011-05-12 01:45:09] <cehteh> well you remember the that writing has to be committed .. and then i wanted a short circuit to transfer a resource to another receiver than giving it back to the backend -[2011-05-12 01:45:47] <cehteh> i still dont get your calculation stream -[2011-05-12 01:46:10] <cehteh> when thats a sequence of frames and a single job then you cant calculate frames in parallel -[2011-05-12 01:46:21] <ichthyo> no, it isn't a single job -[2011-05-12 01:46:25] <cehteh> ah ok -[2011-05-12 01:46:37] <ichthyo> look at it from the viewangle of the player -[2011-05-12 01:46:52] <ichthyo> a calculation stream eventually produces a data stream (channel) -[2011-05-12 01:46:58] <ichthyo> that is the idea [2011-05-12 01:47:31] <cehteh> if you want to accumulate render jobs then i strongly opt for doing this vertically (if ever) [2011-05-12 01:47:40] <cehteh> never horizontally [2011-05-12 01:47:43] <ichthyo> yes -[2011-05-12 01:48:00] <cehteh> ok then we are fine .. no matter how you name it :P [2011-05-12 01:48:11] <ichthyo> with the exception you just mentioned: planning further jobs could be done in a sway for a segment of time [2011-05-12 01:48:44] <cehteh> yes but with a limit .. not a full sequence from the session [2011-05-12 01:48:49] <ichthyo> e.g. the 2* 25 jobs for the next second @@ -922,17 +513,8 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 01:49:40] <ichthyo> just as an example [2011-05-12 01:49:52] <cehteh> lets say dont run discovery for more than 20 frames .. and dont fetch more than 5 frames and render every single frame [2011-05-12 01:50:07] <cehteh> even if the span between cuts is longer -[2011-05-12 01:50:13] <ichthyo> yes [2011-05-12 01:50:22] <cehteh> this values of course may be configureable [2011-05-12 01:50:26] <ichthyo> and its clear that the actual numbers are for optimisation -[2011-05-12 01:50:28] <ichthyo> yes -[2011-05-12 01:50:39] <ichthyo> ok -[2011-05-12 01:50:44] <ichthyo> lets call it a night! -[2011-05-12 01:50:48] <cehteh> feierabend :P -[2011-05-12 01:50:50] <cehteh> n8 -[2011-05-12 01:50:54] <ichthyo> n8 -[2011-05-12 01:50:58] <cehteh> well i play abit around -[2011-05-12 01:51:13] [QUIT] Disconnected from irc://kornbluth.freenode.net/ (irc://kornbluth.freenode.net/). [[Reconnect][Reconnect to kornbluth.freenode.net][reconnect]] ---------------------------- From 82ca6ef7ea53970e682465d6278b7b4b394e0d44 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 15 May 2011 03:58:44 +0200 Subject: [PATCH 041/496] add subsections, further tidying of the transcript Signed-off-by: Ichthyostega <prg@ichthyostega.de> --- doc/devel/meeting_summary/2011-04-13.txt | 2 +- doc/devel/meeting_summary/2011-05-11.txt | 353 ++++++++++++++++------- 2 files changed, 256 insertions(+), 99 deletions(-) diff --git a/doc/devel/meeting_summary/2011-04-13.txt b/doc/devel/meeting_summary/2011-04-13.txt index 949b644c8..77b794746 100644 --- a/doc/devel/meeting_summary/2011-04-13.txt +++ b/doc/devel/meeting_summary/2011-04-13.txt @@ -66,7 +66,7 @@ the not-so large displays. using a »liquid layout«? ^^^^^^^^^^^^^^^^^^^^^^^^ -.-- liquid <--> not liquid -- +.-- liquid ⟺ not liquid -- [caption="☉Transcript☉ "] ---------------------------- [2011-04-13 23:05:14] <fsiddi> ok, so apart from that, the discussion is liquid vs not liquid diff --git a/doc/devel/meeting_summary/2011-05-11.txt b/doc/devel/meeting_summary/2011-05-11.txt index da235b9a0..8b3f1cc69 100644 --- a/doc/devel/meeting_summary/2011-05-11.txt +++ b/doc/devel/meeting_summary/2011-05-11.txt @@ -24,7 +24,21 @@ link:/documentation/design/architecture/playRender.html[Player subsystem] and the Renderengine/Scheduler. The following discussion turned into considering several detaled and technical topics regarding the Jobs to be scheduled, how to create and attach new jobs, -handle memory etc. +handle memory ... + +- Engine Interface ⟺ Scheduler Interface +- Latency of GUI reaction +- Aborting of Jobs, rescheduling, Job dependencies +- Ressource handling, Callbacks after Modifications +- one central service ⟺ integrating with external libs/services +- explicit wait state on Jobs? +- Interfaces and components working together +- Responsibilities: who cares for what part +- Dedicated Time facility +- animating the GUI during playback +- interaction between proc and backend +- handling of prerequisite ressources + Conclusion ~~~~~~~~~~ @@ -44,13 +58,16 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC '''' -.-- Discussion of details -- +.-- Engine Interface ⟺ Scheduler Interface -- [caption="☉Transcript☉ "] ---------------------------- -[2011-05-11 22:08:54] <cehteh> ichthyo: how do we want to proceed with the engine interface, discuss it point by point or only a overview and edit comments in the rfc? +[2011-05-11 22:08:54] <cehteh> ichthyo: how do we want to proceed with the engine interface, + discuss it point by point or only a overview and edit comments in the rfc? [2011-05-11 22:09:08] <ichthyo> well I thought rather the latter...its a rough draft -[2011-05-11 22:10:52] <cehteh> well generally i agree with your proposal (i'm just reading it) with some refinements/ideas i want to add -[2011-05-11 22:12:48] <ichthyo> well, probably the most important new view angle is that I looked on all those 'play mode changes' +[2011-05-11 22:10:52] <cehteh> well generally i agree with your proposal (i'm just reading it) + with some refinements/ideas i want to add +[2011-05-11 22:12:48] <ichthyo> well, probably the most important new view angle is that + I looked on all those 'play mode changes' [2011-05-11 22:12:51] <cehteh> QoS: do we want constants there or could you think about a 'functor' which reacts more dynamic? [2011-05-11 22:13:20] <cehteh> well your proposal sounded like constants (to be defined) [2011-05-11 22:13:35] <ichthyo> yes, its constants, but it seems natural to extend that @@ -58,25 +75,42 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 22:14:19] <cehteh> this class can then react more dynamic [2011-05-11 22:14:22] <ichthyo> ok, yes, so that you could e.g. combine several independent degrees of freedom [2011-05-11 22:14:31] <cehteh> yes -[2011-05-11 22:16:02] <cehteh> then we can ask precisely "shall we render this frame now?" .. instead generally "do we render frames under timing pressure?" +[2011-05-11 22:16:02] <cehteh> then we can ask precisely "shall we render this frame now?"... + instead generally "do we render frames under timing pressure?" [2011-05-11 22:16:47] <cehteh> and of course also other questions, accuracy, syncronization and so on -[2011-05-11 22:18:17] <cehteh> the other thing is about sequences ... the backend delivers only single frames, you set up relations (this frame comes after that frame) -[2011-05-11 22:18:44] <cehteh> i would like if we can put prefetching and driving sequences into the player/scheduler and not in the backend at all +(...) + +[2011-05-11 22:18:17] <cehteh> the other thing is about sequences -- + the backend delivers only single frames, you set up relations + (this frame comes after that frame) +[2011-05-11 22:18:44] <cehteh> i would like if we can put prefetching and driving sequences + into the player/scheduler and not in the backend at all [2011-05-11 22:18:56] <ichthyo> yeah... I figured that there is some discrepancy -[2011-05-11 22:19:33] <cehteh> well for me there is no much difference, i am already optimizing for sequences of course but there is no need to pass that explicitly -[2011-05-11 22:19:33] <ichthyo> so somehow we'd find a way to come from such sequences down to the individual frames which can be deliverd +[2011-05-11 22:19:33] <cehteh> well for me there is no much difference, + i am already optimizing for sequences of course but + there is no need to pass that explicitly +[2011-05-11 22:19:33] <ichthyo> so somehow we'd find a way to come from such sequences down to + the individual frames which can be deliverd [2011-05-11 22:20:22] <ichthyo> yet there is one nasty problem [2011-05-11 22:20:42] <ichthyo> all those changes I'm writing about here are more or less connected to such a sequence a s awhole -[2011-05-11 22:22:02] <cehteh> a 'job' .. or more precicely a 'frame-render-job' can have some state, like references to related (following) jobs or so on +[2011-05-11 22:22:02] <cehteh> a 'job' .. or more precicely a 'frame-render-job' can have some state, + like references to related (following) jobs or so on [2011-05-11 22:22:15] <cehteh> i am not sure yet if we want to put that there or in the player -[2011-05-11 22:23:04] <cehteh> maybe even the player is better but that makes the player a little more complex as it doesnt just need to shoot and forget but also care for a lot (potentially aborted) jobs +[2011-05-11 22:23:04] <cehteh> maybe even the player is better but that makes the player a little more complex + as it doesnt just need to shoot and forget but also care for a lot + (potentially aborted) jobs +---------------------------- +.-- Latency of GUI reaction -- +[caption="☉Transcript☉ "] +---------------------------- [2011-05-11 22:22:49] <ichthyo> e.g think about the following scenario: [2011-05-11 22:23:06] <ichthyo> we're in the middle of playing, and now we want to switch to double speed playback [2011-05-11 22:23:39] <cehteh> yes then the player cancels every other frame job and changes its timebase [2011-05-11 22:23:56] <cehteh> or instead canceling it could put them on a lower priority -[2011-05-11 22:24:01] <ichthyo> that means, the player/scheduler/whatever needs to find out a point in time, *when* this switch can be performed +[2011-05-11 22:24:01] <ichthyo> that means, the player/scheduler/whatever needs to find out a point in time, + *when* this switch can be performed [2011-05-11 22:24:13] <ichthyo> and then cancel all existing jobs after that point and reschedule new jobs [2011-05-11 22:24:40] <cehteh> so that means the player might get considerably more heavy with all this bookkeeping [2011-05-11 22:24:40] <ichthyo> note I thought it helps if we don't guarantee immediate reaction @@ -86,21 +120,32 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 22:25:45] <daylife> 30 ms is noticable [2011-05-11 22:26:41] <cehteh> there should be a fast-path for time and priority rescheduling [2011-05-11 22:27:33] <cehteh> fast path == explcit interface for only that purpose -[2011-05-11 22:27:36] <daylife> I'm not sure what you're discussing exactly, but one of the things that bothers me most about the Lightworks beta is the noticable lag when navigating the timeline, playing, etc, in contrast to Avid, which immediately, no delay whatsoever, responds to any user action -[2011-05-11 22:29:01] <cehteh> user actions should give *instant* feedback .. even if this feedback is only fake feedback in corner cases +[2011-05-11 22:27:36] <daylife> I'm not sure what you're discussing exactly, but one of the things that bothers me most + about the Lightworks beta is the noticable lag when navigating the timeline, + playing, etc, in contrast to Avid, which immediately, no delay whatsoever, + responds to any user action +[2011-05-11 22:29:01] <cehteh> user actions should give *instant* feedback -- + even if this feedback is only fake feedback in corner cases [2011-05-11 22:29:21] * ichthyo nods -[2011-05-11 22:29:22] <daylife> editing is on that part a bit like playing piano. if there were any delay, no matter how short, between pressing a key and hearing a note, it would be very irritating +[2011-05-11 22:29:22] <daylife> editing is on that part a bit like playing piano. if there were any delay, + no matter how short, between pressing a key and hearing a note, it would be very irritating [2011-05-11 22:29:32] <cehteh> exacty [2011-05-11 22:29:39] <hermanr> Indeed +---------------------------- +.-- Aborting of Jobs, rescheduling, Job dependencies -- +[caption="☉Transcript☉ "] +---------------------------- [2011-05-11 22:33:02] <cehteh> but caveat .. maybe we need to reschedule dependent jobs too [2011-05-11 22:33:24] <ichthyo> yeah... likely [2011-05-11 22:33:38] <cehteh> so far you wanted to provide jobs in a conflictless nondeadlocking way -[2011-05-11 22:34:08] <cehteh> but for rescheduling that means likely that dependency information needs to be passed and maintained in the scheduler as well +[2011-05-11 22:34:08] <cehteh> but for rescheduling that means likely that dependency information needs + to be passed and maintained in the scheduler as well [2011-05-11 22:34:12] <ichthyo> that doesn't need to be immediately the level within the scheduler [2011-05-11 22:34:29] <cehteh> well it would be much easier if it is [2011-05-11 22:35:09] <ichthyo> well... thus the idea to allow for a (small) tolerance interval -[2011-05-11 22:35:14] <cehteh> then rescheduling automatically walks all dependencies, reschedules them too and by that it can already see if we can met timing constraints +[2011-05-11 22:35:14] <cehteh> then rescheduling automatically walks all dependencies, reschedules them too + and by that it can already see if we can met timing constraints [2011-05-11 22:36:37] <cehteh> the 'reschedule' can answer if rescheduling is possible [2011-05-11 22:37:07] <cehteh> so you just iterate over all frame-job to be rescheduled from shortest to longest time [2011-05-11 22:37:36] <cehteh> the most urgend reschedules may return "hey this job is already running, cant reschedule" @@ -112,14 +157,20 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 22:40:33] <ichthyo> and that would be the information interesting for the layers above [2011-05-11 22:40:56] <ichthyo> e.g. to change the animation speed of the cursor, or whatever [2011-05-11 22:41:40] <cehteh> if you want async rescheduling you make that a job by itself -[2011-05-11 22:42:41] <cehteh> so for the player it might reschedule the next 500ms worth of frames synchronously and any later frames (background rendering) asynchronously +[2011-05-11 22:42:41] <cehteh> so for the player it might reschedule the next 500ms worth of frames synchronously + and any later frames (background rendering) asynchronously [2011-05-11 22:42:04] <ichthyo> or more concrete... when we do an 'abort' [2011-05-11 22:42:12] <ichthyo> e.g. because the user changed the model [2011-05-11 22:42:24] <ichthyo> then I'm sure I need that asynchroneous feedback -[2011-05-11 22:43:14] <ichthyo> ...i.e. that other part doesn't have a timer or scheduler on its own, but rather relies on that callback +[2011-05-11 22:43:14] <ichthyo> ...i.e. that other part doesn't have a timer or scheduler on its own, + but rather relies on that callback [2011-05-11 22:43:19] <cehteh> we never abort 'running' jobs .. only waiting or scheduled jobs [2011-05-11 22:43:48] <ichthyo> but we can (and need) to 'abort' whole calculation streams +---------------------------- +.-- Ressource handling, Callbacks after Modifications -- +[caption="☉Transcript☉ "] +---------------------------- [2011-05-11 22:45:27] <cehteh> do you really need that feedback? [2011-05-11 22:45:47] <cehteh> how about 'abort' gives instant feedback .. [2011-05-11 22:45:53] <cehteh> or even 'never fails' @@ -128,20 +179,23 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 22:46:23] <ichthyo> there is no timer or scheduler running [2011-05-11 22:46:44] <ichthyo> so it doesn't help to know *when* the abort is executed [2011-05-11 22:47:03] <ichthyo> but I need to be notified *after* it has happened -[2011-05-11 22:47:09] <cehteh> jobs are self containing, any resources they need are attached to the job, they should not reference any volatile data up in proc +[2011-05-11 22:47:09] <cehteh> jobs are self containing, any resources they need are attached to the job, + they should not reference any volatile data up in proc [2011-05-11 22:47:39] <ichthyo> directly, yes of course -- but indirectly thats impossible -[2011-05-11 22:49:49] <ichthyo> in that concrete example, quite simply, because the higher layers can do some stuff only after the jobs are gone +[2011-05-11 22:49:49] <ichthyo> in that concrete example, quite simply, because the higher layers can do some stuff + only after the jobs are gone [2011-05-11 22:49:57] <ichthyo> e.g. close the session [2011-05-11 22:50:03] <cehteh> really? [2011-05-11 22:50:25] <ichthyo> well... I can't just blow away a session which is still used by ongoing jobs [2011-05-11 22:51:04] <cehteh> jobs have no backpointers to the session [2011-05-11 22:51:05] <ichthyo> of course I don't mutate the data which is referred by the jobs -[2011-05-11 22:52:09] <cehteh> nah not only reference data i am really thinking that jobs have absolutely no references into the session +[2011-05-11 22:52:09] <cehteh> nah not only reference data i am really thinking that jobs have absolutely + no references into the session [2011-05-11 22:52:18] <cehteh> they have references into the backend data [2011-05-11 22:52:23] <cehteh> but these are managed there [2011-05-11 22:52:37] <cehteh> while closing a session doesnt require to shutdown the backend -[2011-05-11 22:53:01] <ichthyo> well, so to clarify that: there *is* some kind of back reference for sure. Just it isn't direct -[2011-05-11 22:53:12] <ichthyo> see for example: a job references an exit node +[2011-05-11 22:53:01] <ichthyo> well, so to clarify that: there *is* some kind of back reference for sure. + Just it isn't direct. See for example: a job references an exit node [2011-05-11 22:53:35] <ichthyo> and it references an output slot [2011-05-11 22:53:38] <cehteh> really? [2011-05-11 22:53:47] <cehteh> how about a functor there @@ -152,14 +206,19 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 22:54:48] <cehteh> but do not expect that i want to do rescheduling for aborting jobs [2011-05-11 22:55:30] <ichthyo> well... as said: jobs can't be aborted [2011-05-11 22:55:38] <ichthyo> but whole calculation streams can -[2011-05-11 22:55:42] <cehteh> if you place a job to be executed in 1hour and then abort this job, this abort callback might be called not earlier than in one hour (or if your really want we can add a time-limit there for rescheduling) -[2011-05-11 22:57:34] <ichthyo> no! this callback job would not be scheduled to that time of the original job (1 hour in the future) but to that point when the scheduler can guarantee that non of the old jobs will become active anymore -[2011-05-11 22:57:54] <ichthyo> because then I can dispose the data, change connections, deregister output slots and so on -[2011-05-11 22:59:55] <ichthyo> thus all I want for this respect is a guarantee that the working function of that job can't get active anymore +[2011-05-11 22:55:42] <cehteh> if you place a job to be executed in 1 hour and then abort this job, + this abort callback might be called not earlier than in one hour + (or if your really want we can add a time-limit there for rescheduling) +[2011-05-11 22:57:34] <ichthyo> no! this callback job would not be scheduled to that time of the original job + (1 hour in the future) but to that point when the scheduler can guarantee that + non of the old jobs will become active anymore because then I can dispose the data, + change connections, deregister output slots and so on. + Thus all I want for this respect is a guarantee that the working function of that job + can't get active anymore [2011-05-11 23:00:41] <ichthyo> do you see my point? [2011-05-11 23:00:46] <cehteh> yes i see your point -[2011-05-11 23:00:55] <cehteh> adding that callback functionality -[2011-05-11 23:01:33] <ichthyo> it doesn't help me to know *now* that 3 frames into the future will still be calculated and delivered, but starting with the 4.th frame, the abort will be in effect +[2011-05-11 23:01:33] <ichthyo> it doesn't help me to know *now* that 3 frames into the future will still be calculated + and delivered, but starting with the 4.th frame, the abort will be in effect [2011-05-11 23:01:44] <ichthyo> this information doesn't help me *now* [2011-05-11 23:02:09] <ichthyo> because, until these 3 frames are delivered, I can't deregister the "output slot" [2011-05-11 23:02:17] <ichthyo> e.g. disconect an audio client from Jack server @@ -168,23 +227,35 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 23:04:10] <ichthyo> deregistering an external connection has nothing to do with how the resources are tracked. [2011-05-11 23:04:22] <ichthyo> rather I need to know *when* its safe to do so -[2011-05-11 23:07:56] <ichthyo> cehteh: actually I'd put an adaptation layer in between. When viewed from the player (or the session for that), I only want an high-level 'abort' operation. Like I proposed in my RfC. I didn't expect to talk directly to the scheduler or any part of the engine -[2011-05-11 23:08:31] <ichthyo> so at the interface of the scheduler, that can be implemented so that it fits in best there +(...) + +[2011-05-11 23:07:56] <ichthyo> cehteh: actually I'd put an adaptation layer in between. + When viewed from the player (or the session for that), I only want an high-level 'abort' + operation. Like I proposed in my RfC. I didn't expect to talk directly to the scheduler + or any part of the engine. So at the interface of the scheduler, that can be implemented + so that it fits in best there [2011-05-11 23:08:18] <cehteh> well i am thinking in low level terms (as usual) [2011-05-11 23:09:09] <cehteh> i write some low-level notes for the interface (nothing concrete) +---------------------------- +.-- one central service ⟺ integrating with external libs/services -- +[caption="☉Transcript☉ "] +---------------------------- [2011-05-11 23:09:58] <ichthyo> Just as a striking example [2011-05-11 23:10:13] <ichthyo> think about the occasional crashes with the dummy player currently in the GUI [2011-05-11 23:10:24] <ichthyo> that is exactly due to a lack of such a callback [2011-05-11 23:10:26] <cehteh> yes while i was thinking we can decouple this completely, but maybe not [2011-05-11 23:10:41] <ichthyo> the GUI closes the window. The only thing I can do is to block that closing [2011-05-11 23:10:57] <ichthyo> but without external help, I don't know how long to block -[2011-05-11 23:11:33] <ichthyo> and I we don't block, then the last frame gets disposed into an output buffer which is already gone, deregistered from XV and whatsoever +[2011-05-11 23:11:33] <ichthyo> and I we don't block, then the last frame gets disposed into an output buffer + which is already gone, deregistered from XV and whatsoever [2011-05-11 23:11:45] <ichthyo> not ever, but sometimes -[2011-05-11 23:12:18] <cehteh> well i was thinking that the scheduler and backend only operate on their own data and you can just drop that on the floor from player and proc (and gui) +[2011-05-11 23:12:18] <cehteh> well i was thinking that the scheduler and backend only operate on their own data + and you can just drop that on the floor from player and proc (and gui) [2011-05-11 23:12:37] <cehteh> that means currently the player presents some buffers and the mockup player renders there [2011-05-11 23:13:02] <cehteh> this renders into the gui provied buffer which the gui may release too early right? -[2011-05-11 23:13:34] <cehteh> but if this buffer is provided by the backend and the gui just closes down, not displaying it anymore then nothing happens, the backend knows how to manage resources +[2011-05-11 23:13:34] <cehteh> but if this buffer is provided by the backend and the gui just closes down, not displaying it + anymore then nothing happens, the backend knows how to manage resources [2011-05-11 23:14:29] <ichthyo> well... true, but a general mismatch [2011-05-11 23:14:34] <ichthyo> usually it works the other way round [2011-05-11 23:14:49] <ichthyo> I don't know how it's for ALSA, but also for jack it works the other way round @@ -194,10 +265,12 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 23:15:24] <ichthyo> Jack is even more rigourous [2011-05-11 23:15:32] <cehteh> yes i see now [2011-05-11 23:15:54] <ichthyo> so thats an important general mismatch we should come up with a nice solution maybe -[2011-05-11 23:16:13] <cehteh> but well i still wonder if we may put such a callback even more into the backend "resource released" ... +[2011-05-11 23:16:13] <cehteh> but well i still wonder if we may put such a callback even more into the + backend "resource released" ... [2011-05-11 23:16:18] <cehteh> mhm maybe not [2011-05-11 23:16:48] <cehteh> has pros and cons .. not all resources have jobs associated -[2011-05-11 23:16:16] <ichthyo> of course it would be great, if the backend just delivers in time, right into the buffer for output +[2011-05-11 23:16:16] <ichthyo> of course it would be great, if the backend just delivers in time, + right into the buffer for output [2011-05-11 23:16:39] <ichthyo> so that we don't need another buffering and copy over step on the output side [2011-05-11 23:17:06] <cehteh> but still in real the jack buffers will come from the backend [2011-05-11 23:17:13] <cehteh> so the backend can send a notify @@ -205,21 +278,26 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 23:22:02] <ichthyo> same for any gui widgets to display video [2011-05-11 23:21:44] <ichthyo> and not from the backend [2011-05-11 23:21:49] <cehteh> ichthyo: the backend can still manage that -[2011-05-11 23:22:44] <ichthyo> so why should the backend try to manage buffers, which are actually managed by an other (external) system? -[2011-05-11 23:22:47] <cehteh> generally i want it the other way around that the backend allocates/mmaps buffers, but i know that there are a lot things which do it the other way around +[2011-05-11 23:22:44] <ichthyo> so why should the backend try to manage buffers, + which are actually managed by an other (external) system?? +[2011-05-11 23:22:47] <cehteh> generally i want it the other way around that the backend allocates/mmaps buffers, + but i know that there are a lot things which do it the other way around [2011-05-11 23:23:02] <cehteh> that means we can do 2 things: copying into the destination buffer [2011-05-11 23:23:11] <cehteh> or manage this destination buffers as well [2011-05-11 23:24:08] <ichthyo> or (3rd option): let the last calculation step work into those external destination buffers [2011-05-11 23:24:35] <cehteh> thats case 2 .. when we manage them, then we calculate there with our normal toolchain [2011-05-11 23:25:03] <ichthyo> not quite; we use them, but we don't manage them -[2011-05-11 23:25:27] <cehteh> proc just asks for a buffer and doesnt need to care from where it comes (RAM, Graphics card, audio server ...) -[2011-05-11 23:26:11] <cehteh> if we dont manage them but only 'use' then then we get some more code paths for every different kind of buffer +[2011-05-11 23:25:27] <cehteh> proc just asks for a buffer and doesnt need to care from where it comes + (RAM, Graphics card, audio server ...) +[2011-05-11 23:26:11] <cehteh> if we dont manage them but only 'use' then then we get some more code paths + for every different kind of buffer [2011-05-11 23:26:08] <ichthyo> and thats not all [2011-05-11 23:26:14] <ichthyo> there are similar problems [2011-05-11 23:26:26] <ichthyo> e.g. jack doesn't run in our threading sytem [2011-05-11 23:26:30] <cehteh> yes, rather shared memory -[2011-05-11 23:27:04] <ichthyo> so the question is: can we even be so precise? we don't have low-latency permissions, as the jack server has -[2011-05-11 23:27:27] <ichthyo> so that means some additional buffering or copying anyway +[2011-05-11 23:27:04] <ichthyo> so another question is: can we even be so precise? + we don't have low-latency permissions, as the jack server has + so that means some additional buffering or copying anyway [2011-05-11 23:27:31] <cehteh> we manage buffers provided by jack [2011-05-11 23:27:49] <cehteh> if we cant render in time, we cant copy in time for sure [2011-05-11 23:27:55] <ichthyo> ah... if you call that 'managing' then yes @@ -232,12 +310,17 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 23:29:49] <ichthyo> why can't we just cooperate with these services? [2011-05-11 23:29:56] <ichthyo> as everyone else does too? [2011-05-11 23:30:01] <cehteh> i call that cooperation? -[2011-05-11 23:30:41] <ichthyo> I don't see why the backend needs to pseudo-manage things which are just out of the scope, and actually owned and managed by someone else? -[2011-05-11 23:30:49] <cehteh> the question is only if every single thing does this ad-hoc .. aka the gui cares for video buffers, the audio player cares for sound buffers, proc cares for rendering buffers and so on -[2011-05-11 23:31:02] <cehteh> or we have one backend which cares about all buffers -[2011-05-11 23:31:17] <cehteh> the coding effort is almost the same -[2011-05-11 23:31:46] <cehteh> but we get one 'memory' service which can deliver different classes of memory (and later easily adaptable/extendable to new technologies) -[2011-05-11 23:32:01] <ichthyo> I'd rather doubt that. In all those cases, we're using already existing libraries and solutions. and we're rather integrating with them +[2011-05-11 23:30:41] <ichthyo> I don't see why the backend needs to pseudo-manage things which are just out of the scope, + and actually owned and managed by someone else? +[2011-05-11 23:30:49] <cehteh> the question is only if every single thing does this ad-hoc -- + aka the gui cares for video buffers, the audio player cares for sound buffers, + proc cares for rendering buffers and so on + or we have one backend which cares about all buffers + the coding effort is almost the same +[2011-05-11 23:31:46] <cehteh> but we get one 'memory' service which can deliver different classes of memory + (and later easily adaptable/extendable to new technologies) +[2011-05-11 23:32:01] <ichthyo> I'd rather doubt that. In all those cases, we're using already existing + libraries and solutions. and we're rather integrating with them [2011-05-11 23:32:23] <cehteh> yes so there is no much effort doing so .. [2011-05-11 23:32:54] <ichthyo> that extends even more to the media libs, we're intend to use [2011-05-11 23:32:55] <cehteh> but since the backend always knows what memory is in use it can do a clean shutdown @@ -248,15 +331,24 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 23:34:14] <cehteh> yes [2011-05-11 23:35:04] <ichthyo> if we have a nice and blastingly fast mmaped file read and buffer handling for it than fine [2011-05-11 23:35:41] <ichthyo> then I'd use that exactly for those sevices, which *we* write, provide and manage -[2011-05-11 23:36:08] <cehteh> possibly we just do it as you saying, and we may implement it in the way as i think later (and then try out and compare) .. or stay with your way -[2011-05-11 23:37:29] <ichthyo> maybe yes... probably I'd concentrate first on that core part which we provide, becasue I'd guess it is the most important one -[2011-05-11 23:37:44] <ichthyo> i.e. reading those source media files and the like -[2011-05-11 23:37:46] <cehteh> well i have some reasons to do it in my way .. for example if you connect the viewer somewhere in the graph, then it would be nice if it can transparently use memory on the graphics card instead copying over -[2011-05-11 23:38:10] <cehteh> or think about render nodes and network transparency later -[2011-05-11 23:38:48] <cehteh> but this is future and i think it is not a fundamental design decision can can be incrementally added when it makes sense and turns out to work well +[2011-05-11 23:36:08] <cehteh> possibly we just do it as you saying, and we may implement it in the way as i think later + (and then try out and compare) .. or stay with your way +[2011-05-11 23:37:29] <ichthyo> maybe yes... probably I'd concentrate first on that core part which we provide, + because I'd guess it is the most important one + i.e. reading those source media files and the like +[2011-05-11 23:37:46] <cehteh> well i have some reasons to do it in my way -- + for example if you connect the viewer somewhere in the graph, + then it would be nice if it can transparently use memory on the graphics card + instead copying over. Or think about render nodes and network transparency later +[2011-05-11 23:38:48] <cehteh> but this is future and i think it is not a fundamental design decision can can be + incrementally added when it makes sense and turns out to work well [2011-05-11 23:39:07] <cehteh> first i am going for the mmaped file access and nothing else [2011-05-11 23:39:07] <ichthyo> yes, probably better approaching it that way +---------------------------- +.-- explicit wait state on Jobs? -- +[caption="☉Transcript☉ "] +---------------------------- [2011-05-11 23:44:34] <ichthyo> another question. [2011-05-11 23:44:51] <ichthyo> do our jobs have some mechanism to 'yield' or wait or similar? [2011-05-11 23:45:10] <ichthyo> i.e. go in a wait state (and make room for other jobs to be scheduled meanwhile)? @@ -294,12 +386,19 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-11 23:55:29] <ichthyo> so that means, he gets control only when the jobfunction exits [2011-05-11 23:55:51] <cehteh> for simplicity and a first implementation yes i think that suffices [2011-05-11 23:55:59] * ichthyo thinks the same +---------------------------- -[2011-05-12 00:01:37] <cehteh> so now we talked about the scheduler interface .. which isnt really the engine interface .. BUT: the job structure needs to carry all resources involved around +.-- Interfaces and components working together -- +[caption="☉Transcript☉ "] +---------------------------- +[2011-05-12 00:01:37] <cehteh> so now we talked about the scheduler interface -- + which isnt really the engine interface -- + BUT: the job structure needs to carry all resources involved around [2011-05-12 00:02:34] <cehteh> think abut the W there are more interfaces involved [2011-05-12 00:02:43] <ichthyo> ok... that brings up another closely related question [2011-05-12 00:02:59] <ichthyo> the engine interface needs something like these calculation streams -[2011-05-12 00:03:19] <ichthyo> that means, that the individual job needs to cary a marker, so it can be associated to a specific stream +[2011-05-12 00:03:19] <ichthyo> that means, that the individual job needs to cary a marker, so it can be associated + to a specific stream [2011-05-12 00:02:59] <cehteh> first, asking the backend for a frame [2011-05-12 00:03:58] <cehteh> i add at least one generic void* to the job where you can put any data along [2011-05-12 00:04:12] <cehteh> type safety on the level above please :P @@ -307,36 +406,47 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 00:05:27] <cehteh> (caveat about the lifetime and ownership of this void*) [2011-05-12 00:05:52] <cehteh> if the job owns it then we need also a cleanup hook there +(...) + [2011-05-12 00:10:44] <cehteh> next would be a interface how to setup a job... possibly in multiple stages [2011-05-12 00:11:59] <cehteh> job_new(); job_wait_for(another_job); job_data()=some_calculation_stream; [2011-05-12 00:12:06] <cehteh> job_arm() [2011-05-12 00:12:14] <cehteh> something like that [2011-05-12 00:12:38] <ichthyo> sounds ok -[2011-05-12 00:12:53] <cehteh> well .. now i think it becomes clear that 'jobs' are our message passing interface between proc and backend +[2011-05-12 00:12:53] <cehteh> well .. now i think it becomes clear that 'jobs' are our + message passing interface between proc and backend [2011-05-12 00:13:15] <cehteh> you dont ask the backend directly for a frame you schedule a job delivering a frame [2011-05-12 00:13:29] <cehteh> the backend schedules a job rendering a frame if its not cached [2011-05-12 00:13:35] <ichthyo> yes, my thinking too -[2011-05-12 00:14:50] <cehteh> so 'job' is a baseclass .. but there can be highlevel subclasses cache_fetch_job and render_frame_job +(...) + +[2011-05-12 00:14:50] <cehteh> so 'job' is a baseclass .. but there can be highlevel subclasses + cache_fetch_job and render_frame_job [2011-05-12 00:15:04] <ichthyo> really? -[2011-05-12 00:15:11] <cehteh> i wonder .. makes sense or? +[2011-05-12 00:15:11] <cehteh> i wonder -- makes sense or? [2011-05-12 00:15:38] <ichthyo> *could* be done, but somehow goes against that simplicity -[2011-05-12 00:15:53] <cehteh> i mean each of this class shares a lot, how abortion is handled and more -[2011-05-12 00:16:43] <cehteh> ok on the low level you can always put a job together with all the little details needed and then arm it -[2011-05-12 00:17:35] <cehteh> i expect that we have maybe half a dozen classes of jobs +[2011-05-12 00:15:53] <cehteh> I mean each of this class shares a lot, how abortion is handled and more +[2011-05-12 00:16:43] <cehteh> Ok on the low level you can always put a job together with all the little + details needed and then arm it. I expect that we have maybe half a dozen classes of jobs [2011-05-12 00:17:03] <ichthyo> not only on the low level. That would also be the correct OO aproach [2011-05-12 00:17:14] <ichthyo> "prefer composition over inheritance" [2011-05-12 00:17:53] <ichthyo> because, if you have a care_fetch_job, etc etc, you end up switching on types [2011-05-12 00:18:13] <cehteh> ok .. leave it simple [2011-05-12 00:18:27] <ichthyo> better investigate data at the start, and then assemble a job which can just run as-is [2011-05-12 00:19:01] <cehteh> ok +---------------------------- +.-- Responsibilities: who cares for what part -- +[caption="☉Transcript☉ "] +---------------------------- [2011-05-12 00:20:00] <ichthyo> to expand on that [2011-05-12 00:20:11] <ichthyo> when we create a 'calculation stream' [2011-05-12 00:20:18] <ichthyo> we get the following informations: [2011-05-12 00:20:30] <ichthyo> - the timings (frame duration, stepping) [2011-05-12 00:20:36] <ichthyo> - the exit node to use -[2011-05-12 00:20:42] <cehteh> i wonder if i have to care anything about a caclulation stream .. thats purely a player/render thing +[2011-05-12 00:20:42] <cehteh> I wonder if i have to care anything about a caclulation stream -- + thats purely a player/render thing [2011-05-12 00:21:15] <ichthyo> *If* your realm just starts at the scheduler, then the answer is "no" [2011-05-12 00:21:22] <ichthyo> then you don't have to care for that [2011-05-12 00:21:42] <ichthyo> that means, I would implement that engine interface, kindof like a facade @@ -348,13 +458,16 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 00:28:07] <ichthyo> the callbacks and this stuff? [2011-05-12 00:28:31] <cehteh> yes i think you can abstract them better, you have knowledge about the session and user interaction [2011-05-12 00:28:37] <ichthyo> (I have no problem taking that responsibility, and implement it on top of the bare jobs) -[2011-05-12 00:29:38] <ichthyo> so that means, that I will also care for setting up the render nodes/invocation, before packaging that into a job function, right? -[2011-05-12 00:29:55] <cehteh> (and scheduling times, possibly a job can be in both queues!) +[2011-05-12 00:29:38] <ichthyo> so that means, that I will also care for setting up the render nodes/invocation, + before packaging that into a job function, right? [2011-05-12 00:29:57] <ichthyo> which would make sense, because I also implemented the render nodes themselves [2011-05-12 00:30:02] <cehteh> yes [2011-05-12 00:28:50] <ichthyo> ok +---------------------------- - +.-- Dedicated Time facility -- +[caption="☉Transcript☉ "] +---------------------------- [2011-05-12 00:22:48] <cehteh> we once talked about that we want a timer facility (outside of player) [2011-05-12 00:23:16] <cehteh> and a 3rd prio queue in the scheduler which is short and hard(er) realtime [2011-05-12 00:23:21] <ichthyo> because e.g. we need such for animating the GUI @@ -371,30 +484,42 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 00:29:03] <cehteh> so there will be the full blown jobs [2011-05-12 00:29:27] <cehteh> which can record dependencies on other jobs, have their state, a jobfunction +(...) + [2011-05-12 00:34:00] <cehteh> the exact setup for scheduling will likely become little more complicated -[2011-05-12 00:34:46] <cehteh> you dont just set a time when to render but timespans and priorities, possibly twice for each queue -[2011-05-12 00:35:33] <cehteh> at least my plan is that a job can be in the background render queue and in the high prio queue (we should give this a better name) at once +[2011-05-12 00:34:46] <cehteh> you dont just set a time when to render but timespans and priorities, + possibly twice for each queue -- at least my plan is that a job can be in the + background render queue and in the high prio queue (we should give this a better name) at once [2011-05-12 00:36:52] <cehteh> this interface needs to be discussed ... but for now it suffices that we know its there [2011-05-12 00:37:08] <cehteh> (also a reason to make the tinyjobs which are much simpler) +---------------------------- +.-- animating the GUI during playback -- +[caption="☉Transcript☉ "] +---------------------------- [2011-05-12 00:41:34] <ichthyo> cehteh: how would you deal with that 'animating' of the gui during playback? [2011-05-12 00:41:49] <ichthyo> that is, updating the time, moving the playhead cursor etc. [2011-05-12 00:42:02] <ichthyo> could that be a job too? [2011-05-12 00:42:38] <cehteh> well summarize: -[2011-05-12 00:43:08] <cehteh> we use the 'normal' queues for rendering .. there are job dependencies which eventually yield in a frame rendered -[2011-05-12 00:44:08] <cehteh> the actualy 'playout' whatever that means is done by the realtime tinyjobs from a special time-syncronized queue +[2011-05-12 00:43:08] <cehteh> we use the 'normal' queues for rendering .. there are job dependencies which + eventually yield in a frame rendered. The actualy 'playout' whatever that means + is done by the realtime tinyjobs from a special time-syncronized queue [2011-05-12 00:44:32] <cehteh> this is continutation based [2011-05-12 00:45:09] <cehteh> the last thing in the normal queues is setup this playout (if still in time) -[2011-05-12 00:45:43] <cehteh> blinking the gui doesnt need to be hard realtime it could be set up there too .. or can you inject gui messages from other threads? +[2011-05-12 00:45:43] <cehteh> blinking the gui doesnt need to be hard realtime it could be set up there too -- + or can you inject gui messages from other threads? [2011-05-12 00:46:56] <ichthyo> in GTK, you invoke so called "signals" -[2011-05-12 00:47:22] <ichthyo> which are functors. And they are wired such that the actual function is then dispatched from the event thread -[2011-05-12 00:48:02] <ichthyo> you can create that signals from any thread -[2011-05-12 00:48:18] <ichthyo> GTK cares to execute them through his event mechanism + which are functors. And they are wired such that the actual function + is then dispatched from the event thread + you can create that signals from any thread + GTK cares to execute them through his event mechanism [2011-05-12 00:48:44] <cehteh> this signals can carry some payload (rendered thumbnail?) [2011-05-12 00:48:56] <ichthyo> they are function objects [2011-05-12 00:49:02] <ichthyo> so they can do anything they want [2011-05-12 00:49:32] <ichthyo> typically you bind a method from some existing object -[2011-05-12 00:50:50] <cehteh> so we may add a notification layer there to give it the right distance for abstraction .. and the gui can maybe subscribe to certain notification as well as things which are queried by the gui are send back there +[2011-05-12 00:50:50] <cehteh> so we may add a notification layer there to give it the right distance for abstraction -- + and the gui can maybe subscribe to certain notification as well as things which are + queried by the gui are send back there [2011-05-12 00:51:07] <ichthyo> yes, my thinking too [2011-05-12 00:51:02] <ichthyo> for our problem that means that there will be some signal(s) to update the timecode [2011-05-12 00:51:23] <ichthyo> there is already even an interface called "GuiNotificationFacade" @@ -402,14 +527,18 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 00:51:41] <ichthyo> just Joel didn't get around filling it with life [2011-05-12 00:51:51] <ichthyo> that was shortly before he left -[2011-05-12 00:56:13] <cehteh> and anything not sound and video playback, just gui update related can be lets say 50ms off time -[2011-05-12 00:56:41] <cehteh> for sure it would be a bit pita when automation curves lag noticeable -[2011-05-12 00:56:55] <ichthyo> so it could even be enqueued with a lower priority than frame calculations scheduled for the same time +(...) + +[2011-05-12 00:56:13] <cehteh> and anything not sound and video playback, just gui update related can be lets say 50ms + off time -- for sure it would be a bit pita when automation curves lag noticeable +[2011-05-12 00:56:55] <ichthyo> so it could even be enqueued with a lower priority than frame calculations scheduled + for the same time [2011-05-12 00:58:28] <ichthyo> well... cinelerra e.g. scrolls the whole timeline pane keeping the cursor in the middle [2011-05-12 00:58:36] <cehteh> there are no much things which are time constraind in the gui but not video or sound [2011-05-12 00:58:37] <ichthyo> this is nice for the handling [2011-05-12 00:58:46] <cehteh> and depend on backend/proc -[2011-05-12 00:59:16] <ichthyo> anyway... that means that there might be thumbnails to process, and I take it that we handle that somehow through our engine as well, just with lower priority +[2011-05-12 00:59:16] <ichthyo> anyway... that means that there might be thumbnails to process, and I take it that we + handle that somehow through our engine as well, just with lower priority [2011-05-12 00:59:23] <cehteh> yes that costs a lot performance there .. [2011-05-12 01:00:01] <ichthyo> as a *user* I really don't know what to prefer [2011-05-12 01:00:10] <cehteh> btw if frames or thumbnails have higher priority might be a feature of 'perspectives' @@ -418,23 +547,31 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 01:00:54] <cehteh> haha [2011-05-12 01:00:56] <ichthyo> (e.g. ardour has this behaviour) [2011-05-12 01:00:59] <cehteh> neither is good -[2011-05-12 01:02:03] <ichthyo> probably the best solution (also the rather expensive to implement) is to make that GUI update in several stages, and each with a different priority +[2011-05-12 01:02:03] <ichthyo> probably the best solution (also the rather expensive to implement) is to make that GUI + update in several stages, and each with a different priority [2011-05-12 01:02:22] <ichthyo> e.g. a wireframe has high priority, thumbnails and waveforms have low priority -[2011-05-12 01:03:29] <cehteh> well the timeline can be rendere into an extra canvas .. then displaying it with scrolling is certainly more efficent than redraw it completely 60 times a second +[2011-05-12 01:03:29] <cehteh> well the timeline can be rendere into an extra canvas .. then displaying it with scrolling + is certainly more efficent than redraw it completely 60 times a second [2011-05-12 01:03:37] <ichthyo> well... you could skip the thumbnails, yet still have an animated cursor and running timecode -[2011-05-12 01:04:15] <cehteh> yes but you dont need to care in the gui .. you just send the jobs with configured piority and deadline and render when the data arrives +[2011-05-12 01:04:15] <cehteh> yes but you dont need to care in the gui .. you just send the jobs with configured piority + and deadline and render when the data arrives [2011-05-12 01:04:29] <ichthyo> yeah, thats what I mean +(...) + [2011-05-12 01:06:06] <cehteh> i am not gui programmer -[2011-05-12 01:06:53] <cehteh> but if, i would make a timeline in a canvas (or even multiple layers, automation on top) and render that in background even with some safe areas around wich are not visible -[2011-05-12 01:07:09] <cehteh> then scolling in is just a fast hardware operation +[2011-05-12 01:06:53] <cehteh> but if, i would make a timeline in a canvas (or even multiple layers, + automation on top) and render that in background even with some safe areas around + which are not visible, then scolling in is just a fast hardware operation. [2011-05-12 01:07:36] <ichthyo> exactly, thats the way I think we should do it -[2011-05-12 01:07:51] <cehteh> well i was already suprised that no one pre-renders menus when the mouse is hovering over them and when the user clicks displaying them in instant +[2011-05-12 01:07:51] <cehteh> well i was already suprised that no one pre-renders menus when the mouse is hovering + over them and when the user clicks displaying them in instant [2011-05-12 01:07:52] <ichthyo> ok... the key point is to do that rendering asynchroneously [2011-05-12 01:08:05] <cehteh> thats how i would do it [2011-05-12 01:08:47] <ichthyo> makes many GUI programmers feel queasy [2011-05-12 01:08:58] <cehteh> hey we all like snappy guis -[2011-05-12 01:09:48] <ichthyo> actually, I must say it's kind of embarrasing when we consider the level of functionality provided by many GUI toolkits even today +[2011-05-12 01:09:48] <ichthyo> actually, I must say it's kind of embarrasing when we consider the level of + functionality provided by many GUI toolkits even today [2011-05-12 01:10:30] <ichthyo> basically the technology from 20 years ago [2011-05-12 01:10:35] <cehteh> yes [2011-05-12 01:10:53] <cehteh> and even with wayland and all new things it not going to change that fundamentals @@ -446,9 +583,15 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 01:13:28] <ichthyo> the parts should be composed in a functional way [2011-05-12 01:13:47] <ichthyo> and the toolkit would then invoke that functors in the right order and async [2011-05-12 01:13:53] <cehteh> yep +---------------------------- -[2011-05-12 01:14:46] <cehteh> the 'actual' interfaces how the backend queries a frame from proc and how proc asks the backend for a frame .. do we need to work this out? -[2011-05-12 01:15:26] <cehteh> but i think about not working it offically out at all in a big way, this interfaces prolly just happen and can be improved in small details +.-- interaction between proc and backend -- +[caption="☉Transcript☉ "] +---------------------------- +[2011-05-12 01:14:46] <cehteh> the 'actual' interfaces how the backend queries a frame from proc and how proc + asks the backend for a frame .. do we need to work this out? + But i think about not working it offically out at all in a big way, + this interfaces prolly just happen and can be improved in small details [2011-05-12 01:16:10] <ichthyo> in the (half finished) code in the ProcNode, I used placeholder / facade interfaces [2011-05-12 01:16:21] <ichthyo> that is enough to set up the backbone [2011-05-12 01:16:52] <ichthyo> that is, for node -> backend (to get a frame) @@ -461,25 +604,34 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 01:19:15] <ichthyo> and if I recall right, you were rather engaged into the framecache / frame database [2011-05-12 01:20:10] <cehteh> the general interface is simple (in my head) .. but there are some dirty corners to worked out -[2011-05-12 01:20:48] <cehteh> if proc asks the backend for a frame, the backend may ask proc for rendering it .. and this going pingpoing with all dependenices -[2011-05-12 01:21:32] <cehteh> but the inital query must provide enough information to construct all of the rest which involves setting up jobs which need deadlines and priorities +(...) + +[2011-05-12 01:20:48] <cehteh> if proc asks the backend for a frame, the backend may ask proc for rendering it -- + and this going pingpoing with all dependenices, but the inital query must provide enough + information to construct all of the rest which involves setting up jobs which need + deadlines and priorities [2011-05-12 01:22:05] <cehteh> or we may rethink this somehow [2011-05-12 01:22:17] <cehteh> doing the ping-pong more in the higher levels [2011-05-12 01:22:26] <cehteh> (no W then) [2011-05-12 01:22:56] <ichthyo> well... still a W, but between two function levels a bit higher then -[2011-05-12 01:22:59] <cehteh> asking the cache if it has a frame, if not then the initiator is responsible for setting up the rendering job +[2011-05-12 01:22:59] <cehteh> asking the cache if it has a frame, if not then the initiator is responsible + for setting up the rendering job [2011-05-12 01:23:08] <cehteh> might become a M then :) [2011-05-12 01:23:15] <ichthyo> yes, kindof that -[2011-05-12 01:23:56] <cehteh> well this needs some careful thinking -- thats an important design decision we can not easily revert -[2011-05-12 01:26:00] <ichthyo> well... until now I've kept that decision rather flexible. That is, in the part I wrote in the nodes +[2011-05-12 01:23:56] <cehteh> well this needs some careful thinking -- + thats an important design decision we can not easily revert +[2011-05-12 01:26:00] <ichthyo> well... until now I've kept that decision rather flexible. + That is, in the part I wrote in the nodes [2011-05-12 01:26:40] <cehteh> for now .. eventually this becomes nailed down -[2011-05-12 01:27:35] <ichthyo> you recall, there was the controversy if we should split and schedule every calculation step, even if it doesn't include fetching some aditional data +[2011-05-12 01:27:35] <ichthyo> you recall, there was the controversy if we should split and schedule + every calculation step, even if it doesn't include fetching some aditional data [2011-05-12 01:28:43] <cehteh> huh? ... well it wasnt about the fetching it was about finer granulared planning for rendering [2011-05-12 01:29:00] <cehteh> note that when you render too early the caches cool down (or fetch too early) [2011-05-12 01:29:59] <cehteh> so i was about discovery before fetching, and fetching in time, and then rendering [2011-05-12 01:30:12] <cehteh> and possibly add some more steps as need arises [2011-05-12 01:30:27] <ichthyo> yes... that was kind of the middle ground we agreed on -[2011-05-12 01:31:10] <ichthyo> I retained the possibility to decide if and where a chain of calculations for a single frame should be split and scheduled as separate jobs +[2011-05-12 01:31:10] <ichthyo> I retained the possibility to decide if and where a chain of calculations for a single + frame should be split and scheduled as separate jobs [2011-05-12 01:31:24] <cehteh> ah [2011-05-12 01:34:15] <cehteh> run only the heavy lifting in single jobs [2011-05-12 01:34:38] <ichthyo> I allways assume that 1 frame is one job @@ -487,7 +639,8 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 01:34:53] <cehteh> well refine 'rendering one frame is one job' [2011-05-12 01:35:02] <ichthyo> the controversy was to what extend a single frame should be split into multiple jobs [2011-05-12 01:35:10] <cehteh> i dont care if discovery over a sequence is one job for multiple frames -[2011-05-12 01:36:54] <ichthyo> at that point (more than a year ago), you insisted that every calculation be done in a separate job, and I didn't agree to that +[2011-05-12 01:36:54] <ichthyo> at that point (more than a year ago), you insisted that every calculation + be done in a separate job, and I didn't agree to that [2011-05-12 01:37:00] <cehteh> yes [2011-05-12 01:37:08] <ichthyo> that is, even if its just the application of yet another effect to the same frame [2011-05-12 01:37:09] <cehteh> i think we can try out and see @@ -495,7 +648,11 @@ The next meeting will be as usual, at Wednesday June 8, 20:00 UTC [2011-05-12 01:37:37] <ichthyo> while it is pretty much clear that the fetching of data should be a separate job; i.e. the IO wait [2011-05-12 01:39:12] <cehteh> prolly scales well this way, but we may need refinements for branches in the graph [2011-05-12 01:39:41] <ichthyo> especially for those cases, which combine several frames (e.g. time average, motion tracking) +---------------------------- +.-- handling of prerequisite ressources -- +[caption="☉Transcript☉ "] +---------------------------- [2011-05-12 01:43:35] <cehteh> resource transfer .. we talked about that [2011-05-12 01:43:46] <cehteh> waiting for resources not jobs [2011-05-12 01:44:26] <ichthyo> yes, of course .... I took that for granted From eafbd4b1cb53b0bc23665d6adee9cf5270505adf Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 27 Jan 2011 14:35:46 +0100 Subject: [PATCH 042/496] fix a comment --- src/lib/time/timevalue.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 7e3d370ad..014d3283d 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -109,7 +109,7 @@ namespace time { * @note supports scaling by a factor, * which \em deliberately is chosen * as int, not gavl_time_t, because the - * multiplying times is meaningless. + * multiplying of times is meaningless. */ class TimeVar : public TimeValue @@ -170,8 +170,11 @@ namespace time { : public TimeValue { protected: + /** generally immutable, + * but derived classes allow some limited mutation + * through special API calls */ Offset& - operator= (Offset const& o) ///< derived classes allow mutation + operator= (Offset const& o) { TimeValue::operator= (o); return *this; From ab72b528c8c7b0e3a92155010e2136e61aabd59c Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 7 Mar 2011 02:52:47 +0100 Subject: [PATCH 043/496] draft test for changing time specs --- tests/40components.tests | 5 + tests/lib/time/time-mutation-test.cpp | 127 ++++++++++++++++++++++++++ wiki/renderengine.html | 11 ++- 3 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 tests/lib/time/time-mutation-test.cpp diff --git a/tests/40components.tests b/tests/40components.tests index 98e5c290a..7b3bdd31d 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -678,6 +678,11 @@ return: 0 END +TEST "Changing time specifications" TimeMutation_test <<END +return: 0 +END + + TEST "Quantiser API basics" QuantiserBasics_test <<END return: 0 END diff --git a/tests/lib/time/time-mutation-test.cpp b/tests/lib/time/time-mutation-test.cpp new file mode 100644 index 000000000..d47c7d3fb --- /dev/null +++ b/tests/lib/time/time-mutation-test.cpp @@ -0,0 +1,127 @@ +/* + TimeMutation(Test) - explicitly changing time spcifications + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/time/timevalue.hpp" +#include "lib/time/timequant.hpp" +//#include "lib/time/display.hpp" +#include "lib/util.hpp" + +#include <boost/lexical_cast.hpp> +#include <iostream> +//#include <cstdlib> +#include <string> + +using boost::lexical_cast; +using util::isnil; +//using std::rand; +using std::cout; +using std::endl; +using std::string; + +//using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE; + +namespace lib { +namespace time{ +namespace test{ + + namespace { + inline string + pop (Arg arg) + { + if (isnil (arg)) return ""; + string entry = arg[0]; + arg.erase (arg.begin()); + return entry; + } + } + + + /**************************************************************** + * @test cover all basic cases for mutating a time specification. + * - change to a given value + * - change by an offset + * - change using a grid value + * - apply an (grid) increment + */ + class TimeMutation_test : public Test + { + gavl_time_t + random_or_get (string arg) + { + if (isnil(arg)) + return 1 + (rand() % 10000); + else + return lexical_cast<gavl_time_t> (arg); + } + + + virtual void + run (Arg arg) + { + TimeValue o (random_or_get (pop(arg))); + TimeValue c (random_or_get (pop(arg))); + + PQuant fixQ (new FixedFrameQuantiser(FrameRate::PAL)); + QuTime qVal (o, fixQ); + FrameNr count(qVal); + + mutate_by_Value (o, c); + mutate_by_Offset (o, Offset(c)); + mutate_quantised (o, qVal); + mutate_by_Increment(o, count); + } + + + void + mutate_by_Value(TimeValue o, TimeValue change) + { + } + + + void + mutate_by_Offset (TimeValue o, Offset change) + { + } + + + void + mutate_quantised (TimeValue o, QuTime change) + { + } + + + void + mutate_by_Increment (TimeValue o, int change) + { + } + }; + + + /** Register this test class... */ + LAUNCHER (TimeMutation_test, "unit common"); + + + +}}} // namespace lib::time::test diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 1985b9c83..96acf0937 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6618,8 +6618,8 @@ 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]] </pre> </div> -<div title="TimeMutation" modifier="Ichthyostega" modified="201101232133" created="201101231344" tags="spec discuss draft" changecount="20"> -<pre>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 noticeable point to note is that now the effect can be decoupled from 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 change itself might be related to a time grid. +<div title="TimeMutation" modifier="Ichthyostega" modified="201103070149" created="201101231344" tags="spec discuss draft" changecount="26"> +<pre>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 * the whole system should fit in naturally and just feel like using raw time values @@ -6631,7 +6631,7 @@ At this point, we should recall some general assumptions within Lumiera * there are no hard-coded defaults regarding the specific nature of the manipulated objects (format, number of channels) * there is not "the" timeline, but we have multiple timelines * sequences might be nesetd and thus be brought into a different context -* framerate is a property of the stream type of a channel; it depends on the output. Ther is no global format or framerate. +* framerate is a property of the stream type of a channel; it depends on the output. There is no global format or framerate. !Usage situations The complexity arises from the mixture of several concerns, which often blend into a single usage situation. @@ -6680,7 +6680,10 @@ As rationale, consider the following * 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.// +* only accept an abstract //modification object.// &rarr;{{red{No! any kind of abstraction requires an indirection}}} + +{{red{Question 3/11}}} -- can we avoid accepting mutations for Time (and thus only mutate ~TimeSpan)? +Rationale: allowing mutations for Time bears the danger of making ~TimeVar obsolete !!!usage considerations Question is: how fine grained and configurable needs this to be? From 9eb4f66372bc258482576e5851e98369b350b29c Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 2 Apr 2011 04:01:06 +0200 Subject: [PATCH 044/496] draft more convenience handling for TimeSpan --- src/lib/time/timevalue.hpp | 45 +++++++++------ tests/lib/time/time-value-test.cpp | 89 ++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 23 deletions(-) diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 014d3283d..315f192cb 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -240,18 +240,15 @@ namespace time { * * Lumiera Time provides some limited capabilities for * direct manipulation; Time values can be created directly - * from \c (h,m,s,ms) specification and there is an string - * representation intended for internal use (reporting and - * debugging). Any real output, formatting and persistent + * from \c (ms,sec,min,hour) specification and there is an + * string representation intended for internal use (reporting + * and debugging). Any real output, formatting and persistent * storage should be based on the (quantised) timecode * formats though, which can be generated from time values. * - * Non constant Time objects can receive an encapsulated - * \em mutation message, which is also the basis for - * changing time spans, durations and for re-aligning - * quantised values to some other grid. - * - * @todo define these mutations + * Similar to TimeValue, also Time objects are considered + * immutable values. As convenience shortcut, some operators + * are provided, creating a TimVar for further calculations. */ class Time : public TimeValue @@ -300,10 +297,8 @@ namespace time { /** * Duration is the internal Lumiera time metric. * It is an absolute (positive) value, but can be - * promoted from an offset. Similar to Time, - * Duration can receive mutation messages. - * - * @todo define these mutations + * promoted from an offset. Like plain TimeValue, + * Duration is an immutable value. */ class Duration : public Offset @@ -342,8 +337,13 @@ namespace time { * to fully specify the temporal properties of an * object within the model. * - * Similar to Time and Duration, a TimeSpan may - * also receive an (opaque) mutation message. + * As an exception to the generally immutable Time + * entities, a non constant TimeSpan may receive + * \em mutation messages, both for the start point + * and the duration. This allows for changing + * position and length of objects in the timeline. + * + * @todo define these mutations */ class TimeSpan : public Time @@ -351,11 +351,16 @@ namespace time { Duration dur_; public: - TimeSpan(Time start, Duration length) + TimeSpan(TimeValue start, Duration length) : Time(start) , dur_(length) { } - ///////////TODO creating timespans needs to be more convenient.... + + TimeSpan(TimeValue start, FSecs(duration_in_secs)) + : Time(start) + , dur_(duration_in_secs) + { } + Duration duration() const @@ -363,6 +368,12 @@ namespace time { return dur_; } + Time + start() const + { + return *this; + } + Time end() const { diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index e73354ea8..06cb251e7 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -71,11 +71,12 @@ namespace test{ checkBasicTimeValues (ref); checkMutableTime (ref); - checkTimeConveniance (ref); + checkTimeConvenience (ref); verify_invalidFramerateProtection(); createOffsets (ref); buildDuration (ref); buildTimeSpan (ref); + compareTimeSpan (ref); } @@ -159,7 +160,7 @@ namespace test{ * especially by the canonical Time values. */ void - checkTimeConveniance (TimeValue org) + checkTimeConvenience (TimeValue org) { Time o1(org); TimeVar v(org); @@ -296,15 +297,91 @@ namespace test{ CHECK (theLength == Offset(org,five).abs()); Time endpoint = interval.end(); - TimeSpan successor (endpoint, Duration(Time(0,2,0,0))); + TimeSpan successor (endpoint, FSecs(2)); CHECK (Offset(interval,endpoint) == Offset(org,five).abs()); CHECK (Offset(endpoint,successor.end()) == Duration(successor)); - cout << "Interval-1: " << interval - << " Interval-2: " << successor + cout << "Interval-1: " << interval + << " Interval-2: " << successor << " End point: " << successor.end() - << endl; + << endl; + } + + + void + compareTimeSpan (TimeValue org) + { + TimeSpan span1 (Time(org), Duration(Time(org))); + TimeSpan span2 (Time(org), Offset(org, TimeValue(0))); + TimeSpan span3 (Time(org), FSecs(5,2)); + TimeSpan span4 (Time(org), FSecs(5,-2)); + + CHECK (span1 == span2); + CHECK (span2 == span1); + CHECK (span3 == span4); + CHECK (span4 == span3); + + CHECK (span1 != span3); + CHECK (span3 != span1); + CHECK (span1 != span4); + CHECK (span4 != span1); + CHECK (span2 != span3); + CHECK (span3 != span2); + CHECK (span2 != span4); + CHECK (span4 != span2); + + // especially note that creating a TimeSpan + // based on offset will take the offset absolute + CHECK (span1.end() == span2.end()); + CHECK (span3.end() == span4.end()); + + // Verify the extended order on time intervals + TimeSpan span1x (Time(org)+Time(org), Duration(Time(org))); // starting later than span1 + TimeSpan span3y (Time(org), FSecs(2)); // shorter than span3 + TimeSpan span3z (Time(org)+Time(org), FSecs(2)); // starting later and shorter than span3 + + CHECK (span1 != span1x); + CHECK (span3 != span3y); + CHECK (span3 != span3z); + + CHECK ( span1 < span1x); + CHECK ( span1 <= span1x); + CHECK (!(span1 > span1x)); + CHECK (!(span1 >= span1x)); + + CHECK ( span3 > span3y); + CHECK ( span3 >= span3y); + CHECK (!(span3 < span3y)); + CHECK (!(span3 <= span3y)); + + CHECK ( span3 < span3z); // Note: the start point takes precedence on comparison + CHECK ( span3y < span3z); + + // Verify this order is really different + // than the basic ordering of time values + CHECK (span1 < span1x); + CHECK (span1.duration() == span1x.duration()); + CHECK (span1.start() < span1x.start()); + CHECK (span1.end() < span1x.end()); + + CHECK (span3 > span3y); + CHECK (span3.duration() > span3y.duration()); + CHECK (span3.start() == span3y.start()); + CHECK (span3.end() == span3y.end()); + CHECK (Time(span3) == Time(span3y)); + + CHECK (span3 < span3z); + CHECK (span3.duration() > span3z.duration()); + CHECK (span3.start() < span3z.start()); + CHECK (span3.end() < span3z.end()); + CHECK (Time(span3) < Time(span3z)); + + CHECK (span3y < span3z); + CHECK (span3y.duration() == span3z.duration()); + CHECK (span3y.start() < span3z.start()); + CHECK (span3.end() < span3z.end()); + CHECK (Time(span3) < Time(span3z)); } }; From 0e4ed856d7b0438800d9c1f28481ba58bf9561f8 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 2 Apr 2011 04:02:05 +0200 Subject: [PATCH 045/496] WIP: get back into the topic of time value mutations start drafting a unit test.... --- tests/lib/time/time-mutation-test.cpp | 26 ++++++++++++++++++++++++-- wiki/renderengine.html | 11 +++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/tests/lib/time/time-mutation-test.cpp b/tests/lib/time/time-mutation-test.cpp index d47c7d3fb..e7c3dbe5f 100644 --- a/tests/lib/time/time-mutation-test.cpp +++ b/tests/lib/time/time-mutation-test.cpp @@ -25,6 +25,7 @@ #include "lib/test/test-helper.hpp" #include "lib/time/timevalue.hpp" #include "lib/time/timequant.hpp" +#include "proc/asset/meta/time-grid.hpp" //#include "lib/time/display.hpp" #include "lib/util.hpp" @@ -46,6 +47,8 @@ namespace lib { namespace time{ namespace test{ + using asset::meta::TimeGrid; + namespace { inline string pop (Arg arg) @@ -76,6 +79,19 @@ namespace test{ return lexical_cast<gavl_time_t> (arg); } + struct TestValues + { + TimeVar var; + TimeSpan span; + QuTime quant; + + TestValues (TimeValue o) + : var(o) + , span(o, Offset(o)) + , quant(o, "test_grid") + { } + }; + virtual void run (Arg arg) @@ -83,8 +99,10 @@ namespace test{ TimeValue o (random_or_get (pop(arg))); TimeValue c (random_or_get (pop(arg))); - PQuant fixQ (new FixedFrameQuantiser(FrameRate::PAL)); - QuTime qVal (o, fixQ); + // using a 25fps-grid, but with an time origin offset by 1/50sec + TimeGrid::build("test_grid", FrameRate::PAL, Time(FSecs(1,50))); + + QuTime qVal (o, "test_grid"); FrameNr count(qVal); mutate_by_Value (o, c); @@ -97,24 +115,28 @@ namespace test{ void mutate_by_Value(TimeValue o, TimeValue change) { + TestValues t(o); } void mutate_by_Offset (TimeValue o, Offset change) { + TestValues t(o); } void mutate_quantised (TimeValue o, QuTime change) { + TestValues t(o); } void mutate_by_Increment (TimeValue o, int change) { + TestValues t(o); } }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 96acf0937..3b1a5caf9 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6618,7 +6618,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]] </pre> </div> -<div title="TimeMutation" modifier="Ichthyostega" modified="201103070149" created="201101231344" tags="spec discuss draft" changecount="26"> +<div title="TimeMutation" modifier="Ichthyostega" modified="201104020013" created="201101231344" tags="spec discuss draft" changecount="29"> <pre>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 @@ -6664,11 +6664,12 @@ This results in the following combinations to be implemented: |~|Duration || -- | |~|~TimeSpan ||set start | |~|quantised ||set raw value | -| Offset|Time ||offset the value | -|~|Duration ||adjust by offset | +| Duration|~TimeSpan ||set length | +| Offset|Time || -- | +|~|Duration || -- | |~|~TimeSpan ||adjust start/length | |~|quantised ||adjust raw value | -| ~QuTime|any raw ||treat as raw | +| ~QuTime|any raw ||treat as raw ~TimeValue | |~|~QuTime ||materialise, then set raw | | increment|~QuTime ||snap to grid point | As rationale, consider the following @@ -6684,6 +6685,8 @@ As rationale, consider the following {{red{Question 3/11}}} -- 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 !!!usage considerations Question is: how fine grained and configurable needs this to be? From 9364d717b069ca0f4097431bd8b7a9da0fdbc257 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 4 Apr 2011 00:41:38 +0200 Subject: [PATCH 046/496] implement total order on time intervals --- src/lib/time/timevalue.hpp | 22 +++++++++++++++++++--- tests/lib/time/time-value-test.cpp | 16 +++++++--------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 315f192cb..5ce03d679 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -312,7 +312,7 @@ namespace time { { } explicit - Duration (Time const& timeSpec) + Duration (TimeValue const& timeSpec) : Offset(Offset(timeSpec).abs()) { } @@ -347,16 +347,27 @@ namespace time { */ class TimeSpan : public Time + , boost::totally_ordered<TimeSpan> { Duration dur_; public: - TimeSpan(TimeValue start, Duration length) + TimeSpan(TimeValue const& start, Duration const& length) : Time(start) , dur_(length) { } - TimeSpan(TimeValue start, FSecs(duration_in_secs)) + TimeSpan(TimeValue const& start, Offset const& reference_distance) + : Time(start) + , dur_(reference_distance) + { } + + TimeSpan(TimeValue const& start, TimeValue const& end) + : Time(start) + , dur_(Offset(start,end)) + { } + + TimeSpan(TimeValue const& start, FSecs(duration_in_secs)) : Time(start) , dur_(duration_in_secs) { } @@ -380,6 +391,11 @@ namespace time { TimeVar startPoint (*this); return (startPoint + dur_); } + + /// Supporting extended total order, based on start and interval length + friend bool operator== (TimeSpan const& t1, TimeSpan const& t2) { return t1.t_==t2.t_ && t1.dur_==t2.dur_; } + friend bool operator< (TimeSpan const& t1, TimeSpan const& t2) { return t1.t_< t2.t_ || + (t1.t_==t2.t_ && t1.dur_< t2.dur_);} }; diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index 06cb251e7..27c4c1524 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -29,12 +29,10 @@ #include <boost/lexical_cast.hpp> #include <iostream> -//#include <cstdlib> #include <string> using boost::lexical_cast; using util::isnil; -//using std::rand; using std::cout; using std::endl; using std::string; @@ -312,10 +310,10 @@ namespace test{ void compareTimeSpan (TimeValue org) { - TimeSpan span1 (Time(org), Duration(Time(org))); - TimeSpan span2 (Time(org), Offset(org, TimeValue(0))); - TimeSpan span3 (Time(org), FSecs(5,2)); - TimeSpan span4 (Time(org), FSecs(5,-2)); + TimeSpan span1 (Time(org), Time(org)+Time(org)); // using the distance between start and end point + TimeSpan span2 (Time(org), Offset(org, TimeValue(0))); // note: the offset is taken absolute, as Duration + TimeSpan span3 (Time(org), FSecs(5,2)); // Duration given explicitly, in seconds + TimeSpan span4 (Time(org), FSecs(5,-2)); // again: the Duration is taken absolute CHECK (span1 == span2); CHECK (span2 == span1); @@ -368,19 +366,19 @@ namespace test{ CHECK (span3 > span3y); CHECK (span3.duration() > span3y.duration()); CHECK (span3.start() == span3y.start()); - CHECK (span3.end() == span3y.end()); + CHECK (span3.end() > span3y.end()); CHECK (Time(span3) == Time(span3y)); CHECK (span3 < span3z); CHECK (span3.duration() > span3z.duration()); CHECK (span3.start() < span3z.start()); - CHECK (span3.end() < span3z.end()); + CHECK (span3.end() != span3z.end()); // it's shorter, and org can be random, so that's all we know CHECK (Time(span3) < Time(span3z)); CHECK (span3y < span3z); CHECK (span3y.duration() == span3z.duration()); CHECK (span3y.start() < span3z.start()); - CHECK (span3.end() < span3z.end()); + CHECK (span3y.end() < span3z.end()); CHECK (Time(span3) < Time(span3z)); } }; From 4d6bb3d54c43da5162c9186a4dcdca21dd45bd57 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 4 Apr 2011 02:07:09 +0200 Subject: [PATCH 047/496] design outline for a limited time::Mutation capability --- src/lib/time/mutation.cpp | 49 ++++++++++++ src/lib/time/mutation.hpp | 108 ++++++++++++++++++++++++++ src/lib/time/timequant.hpp | 4 + src/lib/time/timevalue.hpp | 12 ++- tests/lib/time/time-mutation-test.cpp | 2 + wiki/renderengine.html | 9 ++- 6 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 src/lib/time/mutation.cpp create mode 100644 src/lib/time/mutation.hpp diff --git a/src/lib/time/mutation.cpp b/src/lib/time/mutation.cpp new file mode 100644 index 000000000..e2f5b1ae4 --- /dev/null +++ b/src/lib/time/mutation.cpp @@ -0,0 +1,49 @@ +/* + Mutation - changing and adjusting time values + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/time/timevalue.hpp" +#include "lib/time/mutation.hpp" +//#include "lib/time.h" +//#include "lib/util.hpp" + +//#include <tr1/functional> + +//using std::string; +//using util::unConst; +//using util::floorwrap; + + +namespace lib { +namespace time { + + + Mutation::~Mutation() { } // emit VTable here.... + + + /** */ + + + + +}} // lib::time + diff --git a/src/lib/time/mutation.hpp b/src/lib/time/mutation.hpp new file mode 100644 index 000000000..b4d6961df --- /dev/null +++ b/src/lib/time/mutation.hpp @@ -0,0 +1,108 @@ +/* + MUTATION.hpp - changing and adjusting time values + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file mutation.hpp + ** Modifying time and timecode values. + ** Basically, time values are designed to be immutable. Under some circumstances + ** we need a means to change existing time values though. Moreover, actually performing + ** these changes gets quite involved at the implementation level, due to time / timecode + ** values being \em quantised (grid aligned). To keep this implementation logic separate + ** from the simple time values, and to allow for later extensions and more elaborate + ** change schemes, we choose the <b>visitor pattern</b> for implementing these + ** time mutating operations. + ** + ** \par usage situations + ** + ** There are four basic usage situations causing an existing time value to be mutated: + ** + ** - dragging an object or object boundary (resizing) + ** - nudging + ** - placing an object (e.g. a clip) + ** - update of a timecode display in the GUI + ** + ** All these include sending a change message to the original time spec, while the + ** actual content of the change message determines the concrete behaviour. Especially, + ** when the target time to be changed or the message content are quantised, an interference + ** between multiple time grids may be happen, possibly resulting in a re-quantisation of + ** the message into the target object's own time grid. + ** + ** @todo WIP-WIP-WIP + ** + */ + +#ifndef LIB_TIME_MUTATION_H +#define LIB_TIME_MUTATION_H + +#include "lib/time/timevalue.hpp" +//#include "lib/symbol.hpp" + +#include <boost/noncopyable.hpp> +//#include <iostream> +//#include <boost/operators.hpp> +//#include <string> + + +namespace lib { +namespace time { + +//using std::string; +//using lib::Literal; + + class QuTime; + + /** + * Interface: an opaque change imposed onto some time value. + * + * @see time::TimeSpan#accept(Mutation const&) + * @todo WIP-WIP-WIP + */ + class Mutation + : boost::noncopyable + { + public: + virtual ~Mutation(); + + virtual void change (Duration&) const =0; + virtual void change (TimeSpan&) const =0; + virtual void change (QuTime&) const =0; + + /* === convenience shortcuts for simple cases === */ + + static void changeTime (Time); + static void changeDuration (Duration); + static void nudge (int adjustment); + }; + + + /* === defining the visitation responses === */ + + inline void Duration::accept (Mutation const& muta) { muta.change (*this); } + inline void TimeSpan::accept (Mutation const& muta) { muta.change (*this); } + +#ifdef LIB_TIME_TIMEQUQNT_H + inline void QuTime::accept (Mutation const& muta) { muta.change (*this); } +#endif + + +}} // lib::time +#endif diff --git a/src/lib/time/timequant.hpp b/src/lib/time/timequant.hpp index 0580d2e6f..b12a761a3 100644 --- a/src/lib/time/timequant.hpp +++ b/src/lib/time/timequant.hpp @@ -75,6 +75,10 @@ namespace time { template<class TC> void castInto (TC& timecode) const; + + /** receive change message, which + might cause re-quantisation */ + void accept (Mutation const&); }; diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 5ce03d679..7fd185a62 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -293,12 +293,14 @@ namespace time { class TimeSpan; + class Mutation; /** * Duration is the internal Lumiera time metric. * It is an absolute (positive) value, but can be - * promoted from an offset. Like plain TimeValue, - * Duration is an immutable value. + * promoted from an offset. While Duration generally + * is treated as immutable value, there is the + * possibility to send a \em Mutation message. */ class Duration : public Offset @@ -325,6 +327,8 @@ namespace time { Duration (ulong count, FrameRate const& fps); static const Duration NIL; + + void accept (Mutation const&); }; @@ -392,6 +396,10 @@ namespace time { return (startPoint + dur_); } + /** may change start / duration */ + void accept (Mutation const&); + + /// Supporting extended total order, based on start and interval length friend bool operator== (TimeSpan const& t1, TimeSpan const& t2) { return t1.t_==t2.t_ && t1.dur_==t2.dur_; } friend bool operator< (TimeSpan const& t1, TimeSpan const& t2) { return t1.t_< t2.t_ || diff --git a/tests/lib/time/time-mutation-test.cpp b/tests/lib/time/time-mutation-test.cpp index e7c3dbe5f..7b9e21622 100644 --- a/tests/lib/time/time-mutation-test.cpp +++ b/tests/lib/time/time-mutation-test.cpp @@ -82,11 +82,13 @@ namespace test{ 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") { } diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 3b1a5caf9..0f414c368 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6618,7 +6618,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]] </pre> </div> -<div title="TimeMutation" modifier="Ichthyostega" modified="201104020013" created="201101231344" tags="spec discuss draft" changecount="29"> +<div title="TimeMutation" modifier="Ichthyostega" modified="201104040005" created="201101231344" tags="spec discuss draft" changecount="31"> <pre>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 @@ -6665,6 +6665,7 @@ This results in the following combinations to be implemented: |~|~TimeSpan ||set start | |~|quantised ||set raw value | | Duration|~TimeSpan ||set length | +|~|Duration ||set length | | Offset|Time || -- | |~|Duration || -- | |~|~TimeSpan ||adjust start/length | @@ -6687,12 +6688,18 @@ As rationale, consider the following 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)" +!!! 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. </pre> </div> <div title="TimeQuant" modifier="Ichthyostega" modified="201101232031" created="201012181753" tags="Concepts Player spec img discuss draft" changecount="50"> From eb733df1bb781449d4ef1f3e81a7c2c1b4977f1a Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 4 Apr 2011 02:43:01 +0200 Subject: [PATCH 048/496] proof-of-concept: implement simple mutation --- src/lib/time/mutation.cpp | 82 ++++++++++++++++++++++++++++++++++++++ src/lib/time/mutation.hpp | 6 +++ src/lib/time/timevalue.hpp | 3 ++ 3 files changed, 91 insertions(+) diff --git a/src/lib/time/mutation.cpp b/src/lib/time/mutation.cpp index e2f5b1ae4..070386c62 100644 --- a/src/lib/time/mutation.cpp +++ b/src/lib/time/mutation.cpp @@ -21,7 +21,9 @@ * *****************************************************/ +#include "lib/error.hpp" #include "lib/time/timevalue.hpp" +#include "lib/time/timequant.hpp" #include "lib/time/mutation.hpp" //#include "lib/time.h" //#include "lib/util.hpp" @@ -32,15 +34,95 @@ //using util::unConst; //using util::floorwrap; +namespace error = lumiera::error; namespace lib { namespace time { + LUMIERA_ERROR_DEFINE (INVALID_MUTATION, "Changing a time value in this way was not designated"); + + Mutation::~Mutation() { } // emit VTable here.... + /** @internal actually force a change + * into a target time entity to mutate. + * Mutation is declared fried to TimeValue + * and thus is allowed to influence the basic + * value stored in each time entity + */ + void + Mutation::imposeChange (TimeValue& target, TimeValue valueToSet) + { + target = valueToSet; + } + + + + + /** + * concrete time value mutation: + * impose fixed new start time. + */ + class SetNewStartTime + : public Mutation + { + TimeValue newTime_; + + virtual void + change (Duration&) const + { + throw error::Logic ("mutating the start point of a pure Duration doesn't make sense" + , LUMIERA_ERROR_INVALID_MUTATION); + } + + + virtual void + change (TimeSpan& target) const + { + imposeChange (target, newTime_); + } + + + /** @note the re-quantisation happens automatically + * when the (changed) QuTime is materialised */ + virtual void + change (QuTime& target) const + { + imposeChange (target, newTime_); + } + + + public: + explicit + SetNewStartTime (TimeValue t) + : newTime_(t) + { } + }; + + + /** */ + void + Mutation::changeTime (Time newStartTime) + { + UNIMPLEMENTED ("perform a trivial mutation to set a new fixed time value"); + } + + + void + Mutation::changeDuration (Duration) + { + UNIMPLEMENTED ("perform a trivial mutation to set a new Duration"); + } + + + void + Mutation::nudge (int adjustment) + { + UNIMPLEMENTED ("Nudge by a predefined nudge amount"); + } diff --git a/src/lib/time/mutation.hpp b/src/lib/time/mutation.hpp index b4d6961df..bbb325911 100644 --- a/src/lib/time/mutation.hpp +++ b/src/lib/time/mutation.hpp @@ -53,6 +53,7 @@ #ifndef LIB_TIME_MUTATION_H #define LIB_TIME_MUTATION_H +#include "lib/error.hpp" #include "lib/time/timevalue.hpp" //#include "lib/symbol.hpp" @@ -68,6 +69,8 @@ namespace time { //using std::string; //using lib::Literal; + LUMIERA_ERROR_DECLARE (INVALID_MUTATION); ///< Changing a time value in this way was not designated + class QuTime; /** @@ -91,6 +94,9 @@ namespace time { static void changeTime (Time); static void changeDuration (Duration); static void nudge (int adjustment); + + protected: + static void imposeChange (TimeValue&, TimeValue); }; diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 7fd185a62..b6fc7d28f 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -74,6 +74,9 @@ namespace time { return *this; } + /** some subclasses may receive modification messages */ + friend class Mutation; + public: /** explicit limit of allowed time range */ static gavl_time_t limited (gavl_time_t raw); From 0aaa010350799e7b764bf6609a7be734c226ccd6 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 20 Apr 2011 19:11:08 +0200 Subject: [PATCH 049/496] WIP draft how polymorphic value objects are expected to behave --- tests/lib/polymorphic-value-test.cpp | 208 +++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 tests/lib/polymorphic-value-test.cpp diff --git a/tests/lib/polymorphic-value-test.cpp b/tests/lib/polymorphic-value-test.cpp new file mode 100644 index 000000000..d56e2a122 --- /dev/null +++ b/tests/lib/polymorphic-value-test.cpp @@ -0,0 +1,208 @@ +/* + PolymorphicValue(Test) - verify handling of opaque polymorphic values + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/util.hpp" +#include "lib/util-foreach.hpp" + +#include "lib/opaque-holder.hpp" +//#include "lib/bool-checkable.hpp" + +#include <iostream> +//#include <cstdlib> +#include <vector> + + +namespace lib { +namespace test{ + + using ::Test; +// using util::isnil; + using util::for_each; + using util::isSameObject; +// using lumiera::error::LUMIERA_ERROR_INVALID; + using lumiera::error::LUMIERA_ERROR_ASSERTION; + + using std::vector; +// using std::cout; +// using std::endl; + + namespace { // test dummy hierarchy + // Note: largely varying space requirements + // correct function depending on concrete class + + + struct Interface + { + virtual ~Interface() {} + virtual long apiFunc() =0; + + virtual long& localSum() =0; + + bool + operator== (Interface const& o) + { + return localSum() == o.localSum(); + } + }; + + + const uint MAX_RAND = 1000; + const uint MAX_SIZ = sizeof(long[111]); + + long _checkSum = 0; + long _callSum = 0; + uint _created = 0; + + + template<uint ii> + struct Imp : Interface + { + long localData_[ii]; + + Imp() + { + REQUIRE (0 < ii); + localSum() = 0; + note(ii); + ++ _created; + } + + ~Imp() + { + note (-1 * localSum()); + CHECK (0 == localSum()); + } + + + long + apiFunc() + { + long rr = ii * (rand() % MAX_RAND); + note (rr); + _callSum += rr; + return rr; + } + + long& + localSum() + { + return localData[ii-1]; + } + + void + note (long markerValue) + { + localSum() += markerValue; + _checkSum += markerValue; + } + }; + + + /** maximum additional storage maybe wasted + * due to alignment of the contained object + * within the embedded Holder/Buffer + */ + const size_t _ALIGN_ = sizeof(size_t); + + } + + typedef PolymorphicValue<Interface, MAX_SIZ> PolyVal; + typedef vector<PolyVal> TestList; + + + + /********************************************************************************** + * @test build a bunch of PolymorphicValue objects. Handle them like copyable + * value objects, without knowing the exact implementation type; moreover + * execute implementation internals only disclosed to the concrete subtype. + * Verify correctness through checksums. + */ + class PolymorphicValue_test : public Test + { + + virtual void + run (Arg) + { + _checkSum = 0; + _callSum = 0; + _created = 0; + + { + TestList objs = createOpaqueValues (); + for_each (objs, operate); + } + CHECK (0 == _checksum); // all dead + + verifyOverrunProtection(); + } + + + TestList + createOpaqueValues () + { + TestList list; + list.push_back (PolyVal::build<Imp<1> > () ); + list.push_back (PolyVal::build<Imp<11> > () ); + list.push_back (PolyVal::build<Imp<111> >() ); + list.push_back (PolyVal::build<Imp<23> > () ); + list.push_back (PolyVal::build<Imp<5> > () ); + return list; + } //note: copy + + + static void + operate (PolyVal& elm) + { + PolyVal myLocalVal(elm); + CHECK (elm == myLocalVal); + + long prevSum = _callSum; + long randVal = myLocalVal.apiFunc(); + CHECK (prevSum + randVal == _callSum); + CHECK (elm != myLocalVal); + + elm = myLocalVal; + CHECK (elm == myLocalVal); + CHECK (!isSameObject (elm, myLocalVal)); + + CHECK (sizeof(myLocalVal) <= MAX_SIZ + _ALIGN_); + } + + + void + verifyOverrunProtection() + { +#if false ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT + VERIFY_ERROR (ASSERTION, PolyVal::build<Imp<112> >() ); +#endif ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT + } + }; + + + LAUNCHER (PolymorphicValue_test, "unit common"); + + +}} // namespace lib::test From 9aa601d0048bdf0c67aff83ba904860122b36abb Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 21 Apr 2011 19:35:09 +0200 Subject: [PATCH 050/496] WIP first implementation version of PolymorphicValue holder template --- src/lib/polymorphic-value.hpp | 287 +++++++++++++++++++++++++++ tests/lib/polymorphic-value-test.cpp | 21 +- 2 files changed, 299 insertions(+), 9 deletions(-) create mode 100644 src/lib/polymorphic-value.hpp diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp new file mode 100644 index 000000000..bd343342f --- /dev/null +++ b/src/lib/polymorphic-value.hpp @@ -0,0 +1,287 @@ +/* + POLYMORPHIC-VALUE.hpp - building opaque polymorphic value objects + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file polymorphic-value.hpp + ** A mechanism to allow for opaque polymorphic value objects. + ** This template helps to overcome a problem frequently encountered in + ** C++ programming, based on the fundamental design of C++, favouring + ** explicit low-level control, copying of values and strict ctor-dtor pairs. + ** Many object oriented design patterns build on polymorphism, where the + ** actual type of an object isn't disclosed and collaborations rely on + ** common interfaces. This doesn't mix well with the emphasis the C/C++ + ** language puts on efficient handling of small data elements as values + ** and explicit control of the used storage; indeed several of the modern + ** object oriented and functional programming techniques more or less + ** assume the presence of a garbage collector or similar mechanism, + ** so 'objects' need just to be mentioned by reference. + ** + ** In C++ to employ many of the well known techniques, you're more or less + ** bound to explicitly put the objects somewhere in hash allocated memory + ** and then pass an interface pointer or reference into the actual algorithm. + ** Often, this hinders a design based on constant values and small descriptor + ** objects used inline, thus forcing into unnecessarily complex and heavyweight + ** alternatives. While it's certainly pointless to fight the fundamental nature + ** of the programming language, we may try to pull some (template based) trickery + ** to make polymorphic objects fit better with the handling of small copyable + ** value objects. Especially, C++ gives a special meaning to passing parameters + ** as \c const& -- typically constructing an anonymous temporary object conveniently + ** just for passing an abstraction barrier (while the optimiser can be expected to + ** remove this barrier and the accompanying nominal copy operations altogether in + ** the generated code). Thus, the ability to return a polymorphic object from a + ** factory or configuration function <i>by value</i> would open a lot of + ** straight forward design possibilities and concise formulations. + ** + ** \par how to build a copyable value without knowing it's layout in detail + ** + ** So the goal is to build a copyable and assignable type with value semantics, + ** without disclosing the actual implementation and object layout at the usage site. + ** This seemingly contradictory can be achieved, provided that + ** - the space occupied by the actual implementation object is limited, + ** so it can be placed as binary data into an otherwise opaque holder buffer + ** - and the actual implementation object assists with copying and cloning + ** itself, observing the actual implementation type and layout + ** + ** @see polymorphic-value-test.cpp + ** @see opaque-holder.hpp other similar opaque inline buffer templates + ** @see lib::time::Mutation usage example + */ + + +#ifndef LIB_POLYMORPHIC_VALUE_H +#define LIB_POLYMORPHIC_VALUE_H + + +#include "lib/error.hpp" +//#include "lib/bool-checkable.hpp" +//#include "lib/access-casted.hpp" +//#include "lib/util.hpp" + +//#include <boost/noncopyable.hpp> + + +namespace lib { + +// using lumiera::error::LUMIERA_ERROR_WRONG_TYPE; +// using util::isSameObject; +// using util::unConst; + + + namespace { // implementation helpers... + + + } + + + + + + /** + * Template to build polymorphic value objects. + * Inline buffer with value semantics, yet holding and owning an object + * while concealing the concrete type, exposing only the public interface. + * Access to the contained object is by implicit conversion to this public + * interface. The actual implementation object might be placed into the + * buffer through a builder function; later, this buffer may be copied + * and passed on without knowing the actual contained type. + * + * For using PolymorphicValue, several \b assumptions need to be fulfilled + * - any instance placed into OpaqueHolder is below the specified maximum size + * - the caller cares for thread safety. No concurrent get calls while in mutation! + * + * @warning when a create or copy-into operation fails with exception, the whole + * PolymorphicValue object is in undefined state and must not be used further. + */ + template + < class IFA ///< the nominal Base/Interface class for a family of types + , size_t siz ///< maximum storage required for the targets to be held inline + > + class PolymorphicValue + { + mutable char buf_[siz]; + + template<class IMP> + IMP& + access() const + { + return reinterpret_cast<IMP&> (buf_); + } + + void + destroy() + { + access<IFA>().~IFA(); + } + +// REQUIRE (siz >= sizeof(IMP)); + + template<class IMP> + PolymorphicValue (IMP*) + { + new(&buf_) IMP(); + } + + template<class IMP, typename A1> + PolymorphicValue (IMP*, A1& a1) + { + new(&buf_) IMP (a1); + } + + template<class IMP, typename A1, typename A2> + PolymorphicValue (IMP*, A1& a1, A2& a2) + { + new(&buf_) IMP (a1,a2); + } + + template<class IMP, typename A1, typename A2, typename A3> + PolymorphicValue (IMP*, A1& a1, A2& a2, A3& a3) + { + new(&buf_) IMP (a1,a2,a3); + } + + + class HandlingAdapter + { + public: + virtual ~HandlingAdapter() { }; + virtual void cloneInto (void* targetBuffer) const =0; + virtual void copyInto (IFA& targetBase) const =0; + }; + + template<class IMP> + class Adapter + : public IMP + , public HandlingAdapter + { + virtual void + cloneInto (void* targetBuffer) const + { + new(targetBuffer) Adapter(*this); + } + + virtual void + copyInto (IFA& targetBase) const + { + REQUIRE (INSTANCEOF (Adapter, &targetBase)); + Adapter& target = static_cast<Adapter&> (targetBase); + target = (*this); + } + + public: + /* using default copy and assignment */ + Adapter() : IMP() { } + + template<typename A1> + Adapter (A1& a1) : IMP(a1) { } + + template<typename A1, typename A2> + Adapter (A1& a1, A2& a2) : IMP(a1,a2) { } + + template<typename A1, typename A2, typename A3> + Adapter (A1& a1, A2& a2, A3& a3) : IMP(a1,a2,a3) { } + }; + + + HandlingAdapter& + accessHandlingInterface () const + { + IFA& bufferContents = access<IFA>(); + REQUIRE (INSTANCEOF (HandlingAdapter, &bufferContents)); + HandlingAdapter& hap = dynamic_cast<HandlingAdapter&> (bufferContents); + return hap; + } + + public: + operator IFA& () + { + return access<IFA>(); + } + operator IFA const& () const + { + return access<IFA>(); + } + + ~PolymorphicValue() + { + destroy(); + } + + PolymorphicValue (PolymorphicValue const& o) + { + o.accessHandlingInterface().cloneInto (&buf_); + } + + PolymorphicValue& + operator= (PolymorphicValue const& o) + { + o.accessHandlingInterface().copyInto (this->access<IFA>()); + return *this; + } + + template<class IMP> + static PolymorphicValue + build () + { + Adapter<IMP>* type_to_build_in_buffer; + return PolymorphicValue (type_to_build_in_buffer); + } + + template<class IMP, typename A1> + static PolymorphicValue + build (A1& a1) + { + Adapter<IMP>* type_to_build_in_buffer; + return PolymorphicValue (type_to_build_in_buffer, a1); + } + + template<class IMP, typename A1, typename A2> + static PolymorphicValue + build (A1& a1, A2& a2) + { + Adapter<IMP>* type_to_build_in_buffer; + return PolymorphicValue (type_to_build_in_buffer, a1,a2); + } + + template<class IMP, typename A1, typename A2, typename A3> + static PolymorphicValue + build (A1& a1, A2& a2, A3& a3) + { + Adapter<IMP>* type_to_build_in_buffer; + return PolymorphicValue (type_to_build_in_buffer, a1,a2,a3); + } + + friend bool + operator== (PolymorphicValue const& v1, PolymorphicValue const& v2) + { + return v1.access<IFA>() == v2.access<IFA>(); + } + friend bool + operator!= (PolymorphicValue const& v1, PolymorphicValue const& v2) + { + return ! (v1 == v2); + } + }; + + + + +} // namespace lib +#endif diff --git a/tests/lib/polymorphic-value-test.cpp b/tests/lib/polymorphic-value-test.cpp index d56e2a122..755cbe3a9 100644 --- a/tests/lib/polymorphic-value-test.cpp +++ b/tests/lib/polymorphic-value-test.cpp @@ -27,7 +27,7 @@ #include "lib/util.hpp" #include "lib/util-foreach.hpp" -#include "lib/opaque-holder.hpp" +#include "lib/polymorphic-value.hpp" //#include "lib/bool-checkable.hpp" #include <iostream> @@ -41,11 +41,12 @@ namespace test{ using ::Test; // using util::isnil; using util::for_each; + using util::unConst; using util::isSameObject; // using lumiera::error::LUMIERA_ERROR_INVALID; using lumiera::error::LUMIERA_ERROR_ASSERTION; - using std::vector; +// using std::vector; // using std::cout; // using std::endl; @@ -62,15 +63,16 @@ namespace test{ virtual long& localSum() =0; bool - operator== (Interface const& o) + operator== (Interface const& o) const { - return localSum() == o.localSum(); + return unConst(this)->localSum() + == unConst(o).localSum(); } }; const uint MAX_RAND = 1000; - const uint MAX_SIZ = sizeof(long[111]); + const uint MAX_SIZ = sizeof(long[113]); /////////////////////TODO: using just 111 causes SEGV ---> suspect the HandlingAdapter mixin to require additional storage long _checkSum = 0; long _callSum = 0; @@ -109,7 +111,7 @@ namespace test{ long& localSum() { - return localData[ii-1]; + return localData_[ii-1]; } void @@ -130,7 +132,7 @@ namespace test{ } typedef PolymorphicValue<Interface, MAX_SIZ> PolyVal; - typedef vector<PolyVal> TestList; + typedef std::vector<PolyVal> TestList; @@ -154,7 +156,7 @@ namespace test{ TestList objs = createOpaqueValues (); for_each (objs, operate); } - CHECK (0 == _checksum); // all dead + CHECK (0 == _checkSum); // all dead verifyOverrunProtection(); } @@ -180,7 +182,8 @@ namespace test{ CHECK (elm == myLocalVal); long prevSum = _callSum; - long randVal = myLocalVal.apiFunc(); + Interface& subject = myLocalVal; + long randVal = subject.apiFunc(); CHECK (prevSum + randVal == _callSum); CHECK (elm != myLocalVal); From a10e381cfaffa7d3181d5c120be21e08d4ad2025 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 22 Apr 2011 03:29:12 +0200 Subject: [PATCH 051/496] fix error in test logic need to adjust the checksum on copy :) --- tests/40components.tests | 5 +++ tests/lib/polymorphic-value-test.cpp | 58 ++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/tests/40components.tests b/tests/40components.tests index 7b3bdd31d..51b9c54bc 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -505,6 +505,11 @@ out: can_IterForEach<CustomForwardIter> : Yes END +TEST "Polymorphic value objects" PolymorphicValue_test <<END +return: 0 +END + + PLANNED "RefArray_test" RefArray_test <<END return: 0 END diff --git a/tests/lib/polymorphic-value-test.cpp b/tests/lib/polymorphic-value-test.cpp index 755cbe3a9..7674b677e 100644 --- a/tests/lib/polymorphic-value-test.cpp +++ b/tests/lib/polymorphic-value-test.cpp @@ -84,26 +84,42 @@ namespace test{ { long localData_[ii]; + ~Imp() + { + mark (-1 * localSum()); + CHECK (0 == localSum()); + } + Imp() { REQUIRE (0 < ii); localSum() = 0; - note(ii); + mark(ii); ++ _created; } - ~Imp() + Imp (Imp const& o) { - note (-1 * localSum()); - CHECK (0 == localSum()); + ++ _created; + copyData (o); + _checkSum += localSum(); + }// adjust, because we've gotten two identical instances + + Imp& + operator= (Imp const& o) + { + _checkSum -= localSum(); + copyData (o); + _checkSum += localSum(); + return *this; } - - long + private: + virtual long apiFunc() { long rr = ii * (rand() % MAX_RAND); - note (rr); + mark (rr); _callSum += rr; return rr; } @@ -115,11 +131,18 @@ namespace test{ } void - note (long markerValue) + mark (long markerValue) { localSum() += markerValue; _checkSum += markerValue; } + + void + copyData (Imp const& o) + { + for (uint i=0; i<ii; ++i) + localData_[i] = o.localData_[i]; + } }; @@ -153,6 +176,8 @@ namespace test{ _created = 0; { + verifyBasics(); + TestList objs = createOpaqueValues (); for_each (objs, operate); } @@ -195,6 +220,23 @@ namespace test{ } + void + verifyBasics() + { + long prevSum = _checkSum; + uint prevCnt = _created; + + PolyVal val = PolyVal::build<Imp<111> >(); + CHECK (prevSum+111 == _checkSum); // We got one primary ctor call + CHECK (prevCnt+1 <= _created); // Note: usually, the compiler optimises + CHECK (prevCnt+2 >= _created); // and skips the spurious copy-operation + CHECK (sizeof(PolyVal) > sizeof(Imp<111>)); + Interface& embedded = val; + CHECK (isSameObject(embedded,val)); + CHECK (INSTANCEOF(Imp<111>, &embedded)); + } + + void verifyOverrunProtection() { From 710ae8fa0fa2f396ab8743b1b17aac5571138734 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 23 Apr 2011 20:19:29 +0200 Subject: [PATCH 052/496] Allow for improved performance in special cases Using a policy based switch in case the client interface collaborates and provides copy operations; in this case, a direct static downcast instead of the expensive indirect (dynamic) cast to the management interface can be employed. --- src/lib/polymorphic-value.hpp | 145 +++++++++++++++++++++++---- tests/lib/polymorphic-value-test.cpp | 2 +- 2 files changed, 125 insertions(+), 22 deletions(-) diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp index bd343342f..70a8bd35a 100644 --- a/src/lib/polymorphic-value.hpp +++ b/src/lib/polymorphic-value.hpp @@ -23,7 +23,7 @@ /** @file polymorphic-value.hpp ** A mechanism to allow for opaque polymorphic value objects. ** This template helps to overcome a problem frequently encountered in - ** C++ programming, based on the fundamental design of C++, favouring + ** C++ programming, based on the fundamental design of C++, which favours ** explicit low-level control, copying of values and strict ctor-dtor pairs. ** Many object oriented design patterns build on polymorphism, where the ** actual type of an object isn't disclosed and collaborations rely on @@ -35,7 +35,7 @@ ** so 'objects' need just to be mentioned by reference. ** ** In C++ to employ many of the well known techniques, you're more or less - ** bound to explicitly put the objects somewhere in hash allocated memory + ** bound to explicitly put the objects somewhere in heap allocated memory ** and then pass an interface pointer or reference into the actual algorithm. ** Often, this hinders a design based on constant values and small descriptor ** objects used inline, thus forcing into unnecessarily complex and heavyweight @@ -46,19 +46,54 @@ ** as \c const& -- typically constructing an anonymous temporary object conveniently ** just for passing an abstraction barrier (while the optimiser can be expected to ** remove this barrier and the accompanying nominal copy operations altogether in - ** the generated code). Thus, the ability to return a polymorphic object from a - ** factory or configuration function <i>by value</i> would open a lot of + ** the generated code). Consequently the ability to return a polymorphic object + ** from a factory or configuration function <i>by value</i> would open a lot of ** straight forward design possibilities and concise formulations. ** ** \par how to build a copyable value without knowing it's layout in detail ** ** So the goal is to build a copyable and assignable type with value semantics, ** without disclosing the actual implementation and object layout at the usage site. - ** This seemingly contradictory can be achieved, provided that + ** This seemingly contradictory goal can be achieved, provided that ** - the space occupied by the actual implementation object is limited, ** so it can be placed as binary data into an otherwise opaque holder buffer ** - and the actual implementation object assists with copying and cloning - ** itself, observing the actual implementation type and layout + ** itself, observing the actual implementation data layout + ** + ** The PolymorphicValue template implements this idea, by exposing an copyable + ** container with value semantics to the client code. On instantiation, a common + ** base interface for the actual value objects needs to be provided; the resulting + ** instance will be automatically convertible to this interface. Obviously this + ** common interface must be an ABC or at least contain some virtual functions. + ** Moreover, the PolymorphicValue container provides static builder functions, + ** allowing to place a concrete instance of a subclass into the content buffer. + ** After construction, the actual type of this instance will be forgotten + ** (``type erasure''), but because the embedded vtable, on access the + ** proper implementation functions will be invoked. + ** + ** Expanding on that pattern, the copying and cloning operations of the whole + ** container can be implemented by forwarding to appropriate virtual functions + ** on the embedded payload (implementation) object -- the concrete implementation + ** of these virtual functions can be assumed to know the real type and thus be + ** able to invoke the correct copy ctor or assignment operator. For this to + ** work, the interface needs to expose those copy and clone operations somehow + ** as virtual functions. There are two alternatives to get at this point: + ** - in the general case, the common base interface doesn't expose such operations. + ** Thus we need to <i>mix in</i> an additional \em management interface; this + ** can be done by \em subclassing the desired implementation type, because + ** this concrete type is irrelevant after finishing the placement constructor. + ** In order to re-access this management interface, so to be able to invoke + ** the copy or clone operations, we need to do an elaborate re-cast operation, + ** first going down to the leaf type and then back up into the mixed in + ** management interface. Basically this operation is performed by using + ** an \c dynamic_cast<CopyAPI&>(bufferContents) + ** - but when the used client types provide some collaboration and implement + ** this management interface either directly on the API or as an immediate + ** sub-interface, then this copy/management interface is located within the + ** direct inheritance chain and can be reached by a simple \c static_cast. + ** Indeed, as we're just using a different meaning of the VTable, only a + ** single indirection (virtual function call) is required at runtime in + ** this case to invoke the copy ctor or assignment operator. ** ** @see polymorphic-value-test.cpp ** @see opaque-holder.hpp other similar opaque inline buffer templates @@ -71,11 +106,13 @@ #include "lib/error.hpp" +#include "lib/meta/duck-detector.hpp" //#include "lib/bool-checkable.hpp" //#include "lib/access-casted.hpp" //#include "lib/util.hpp" //#include <boost/noncopyable.hpp> +#include <boost/utility/enable_if.hpp> namespace lib { @@ -85,8 +122,74 @@ namespace lib { // using util::unConst; - namespace { // implementation helpers... + namespace polyvalue { // implementation helpers... + using boost::enable_if; + using lumiera::Yes_t; + using lumiera::No_t; + + struct EmptyBase{ }; + + template<class IFA + ,class BA = EmptyBase + > + class CopySupport + : public BA + { + public: + virtual ~CopySupport() { }; + virtual void cloneInto (void* targetBuffer) const =0; + virtual void copyInto (IFA& targetBase) const =0; + }; + + + + + template<typename T> + class exposes_copySupportFunctions + { + + META_DETECT_FUNCTION(void, cloneInto, (void*) const); + + public: + enum{ value = HasFunSig_cloneInto<T>::value + }; + }; + + + template <class TY, class YES = void> + struct Trait + { + typedef CopySupport<TY> CopyAPI; + enum{ ADMIN_OVERHEAD = 2 * sizeof(void*) }; + + static CopyAPI& + accessCopyHandlingInterface (TY& bufferContents) + { + REQUIRE (INSTANCEOF (CopyAPI, &bufferContents)); + return dynamic_cast<CopyAPI&> (bufferContents); + } + + typedef CopyAPI AdapterAttachment; + }; + + + template <class TY> + struct Trait<TY, typename enable_if< exposes_copySupportFunctions<TY> >::type> + { + typedef TY CopyAPI; + enum{ ADMIN_OVERHEAD = 1 * sizeof(void*) }; + + template<class IFA> + static CopyAPI& + accessCopyHandlingInterface (IFA& bufferContents) + { + REQUIRE (INSTANCEOF (CopyAPI, &bufferContents)); + return static_cast<CopyAPI&> (bufferContents); + } + + typedef EmptyBase AdapterAttachment; + }; } @@ -112,10 +215,18 @@ namespace lib { */ template < class IFA ///< the nominal Base/Interface class for a family of types - , size_t siz ///< maximum storage required for the targets to be held inline + , size_t storage ///< maximum storage required for the target data to be held inline + , class CPY = IFA ///< special sub-interface to support copy operations > class PolymorphicValue { + + typedef polyvalue::Trait<CPY> _Traits; + typedef typename _Traits::CopyAPI _CopyHandlingAdapter; + enum{ + siz = storage + _Traits::ADMIN_OVERHEAD + }; + mutable char buf_[siz]; template<class IMP> @@ -158,18 +269,10 @@ namespace lib { } - class HandlingAdapter - { - public: - virtual ~HandlingAdapter() { }; - virtual void cloneInto (void* targetBuffer) const =0; - virtual void copyInto (IFA& targetBase) const =0; - }; - template<class IMP> class Adapter : public IMP - , public HandlingAdapter + , public _Traits::AdapterAttachment { virtual void cloneInto (void* targetBuffer) const @@ -200,15 +303,15 @@ namespace lib { }; - HandlingAdapter& + _CopyHandlingAdapter& accessHandlingInterface () const { IFA& bufferContents = access<IFA>(); - REQUIRE (INSTANCEOF (HandlingAdapter, &bufferContents)); - HandlingAdapter& hap = dynamic_cast<HandlingAdapter&> (bufferContents); - return hap; + _CopyHandlingAdapter& hap = _Traits::accessCopyHandlingInterface (bufferContents); + return hap; ////TODO cleanup unnecessary temporary } + public: operator IFA& () { diff --git a/tests/lib/polymorphic-value-test.cpp b/tests/lib/polymorphic-value-test.cpp index 7674b677e..26570c8ff 100644 --- a/tests/lib/polymorphic-value-test.cpp +++ b/tests/lib/polymorphic-value-test.cpp @@ -216,7 +216,7 @@ namespace test{ CHECK (elm == myLocalVal); CHECK (!isSameObject (elm, myLocalVal)); - CHECK (sizeof(myLocalVal) <= MAX_SIZ + _ALIGN_); + CHECK (sizeof(myLocalVal) <= MAX_SIZ + polyvalue::Trait<Interface>::ADMIN_OVERHEAD + _ALIGN_); } From 6daf14211ba2168a0fedcae9d30f22c278148125 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 24 Apr 2011 20:28:36 +0200 Subject: [PATCH 053/496] Finish the PolymorphicValue support template --- src/lib/polymorphic-value.hpp | 172 ++++++++++++++++++++------- tests/lib/polymorphic-value-test.cpp | 75 ++++++++---- 2 files changed, 186 insertions(+), 61 deletions(-) diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp index 70a8bd35a..94a4b4b09 100644 --- a/src/lib/polymorphic-value.hpp +++ b/src/lib/polymorphic-value.hpp @@ -94,6 +94,43 @@ ** Indeed, as we're just using a different meaning of the VTable, only a ** single indirection (virtual function call) is required at runtime in ** this case to invoke the copy ctor or assignment operator. + ** Thus, in this latter (optimal) case, the fact that PolymorphicValue allows + ** to conceal the actual implementation type comes with zero runtime overhead, + ** compared with direct usage of a family of polymorphic types (with VTable). + ** + ** \par using polymorphic value objects + ** + ** To start with, we need a situation where polymorphic treatment and type erasure + ** might be applicable. That is, we use a public API, and only that, in any client + ** code, while the concrete implementation is completely self contained. Thus, in + ** the intended use, the concrete implementation objects can be assembled once, + ** typically in a factory, and after that, no further knowledge of the actual + ** implementation type is required. All further use can be coded against + ** the exposed public API. + ** + ** Given such a situation, it might be desirable to conceal the whereabouts of + ** the implementation completely from the clients employing the generated objects. + ** For example, the actual implementation might rely on a complicated subsystem + ** with many compilation dependencies, and we don't want to expose all those + ** details on the public API. + ** + ** Now, to employ PolymorphicValue in such a situation, on the usage side (header): + ** - expose the public API, but not the implementation type of the objects + ** - define an instantiation of PolymorphicValue with this API + ** - be sure to define a hard wired size limit not to be exceeded by the + ** actual implementation objects (PolymorphicValue's ctor has an assertion + ** to verify this constraint) + ** - provide some kind of factory for the clients to get the actual polymorphic + ** value instances. Clients may then freely move and copy those objects, but + ** do not need to know anything about the actual implementation object layout + ** (it could be figured out using RTTI though) + ** + ** On the implementation side (separate compilation unit) + ** - include the definition of the PolymorphicValue instantiation (of course) + ** - define the implementation types to inherit from the public API + ** - implement the mentioned factory function, based on the static build + ** PolymorphicValue#build functions, using the actual implementation type + ** as parameter. ** ** @see polymorphic-value-test.cpp ** @see opaque-holder.hpp other similar opaque inline buffer templates @@ -107,22 +144,14 @@ #include "lib/error.hpp" #include "lib/meta/duck-detector.hpp" -//#include "lib/bool-checkable.hpp" -//#include "lib/access-casted.hpp" -//#include "lib/util.hpp" -//#include <boost/noncopyable.hpp> #include <boost/utility/enable_if.hpp> namespace lib { -// using lumiera::error::LUMIERA_ERROR_WRONG_TYPE; -// using util::isSameObject; -// using util::unConst; - - namespace polyvalue { // implementation helpers... + namespace polyvalue { // implementation details... using boost::enable_if; using lumiera::Yes_t; @@ -130,8 +159,23 @@ namespace lib { struct EmptyBase{ }; - template<class IFA - ,class BA = EmptyBase + /** + * Interface for active support of copy operations + * by the embedded client objects. When inserted into the + * inheritance chain \em above the concrete implementation objects, + * PolymorphicValue is able to perform copy operations trivially and + * without any \c dynamic_cast and other run time overhead besides a + * simple indirection through the VTable. To enable this support, the + * implementation objects should inherit from \c CopySupport<Interface> + * (where \c Interface would be the public API for all these embedded + * implementation objects). + * Alternatively, it's also possible to place this CopySupport API as parent + * to the public API (it might even be completely absent, but then you'd need + * to provide an explicit specialisation of the Traits template to tell + * PolymorphicValue how to access the copy support functions.) + */ + template<class IFA ///< the common public interface of all embedded objects + ,class BA = IFA ///< direct baseclass to use for this copy support API > class CopySupport : public BA @@ -144,7 +188,10 @@ namespace lib { - + /** + * helper to detect presence of a function + * to support clone operations + */ template<typename T> class exposes_copySupportFunctions { @@ -157,23 +204,38 @@ namespace lib { }; + /** + * traits template to deal with + * different ways to support copy operations. + * Default is no support by the API and implementation types. + * In this case, the CopySupport interface is mixed in at the + * level of the concrete implementation class and later on + * accessed through an \c dynamic_cast + */ template <class TY, class YES = void> struct Trait { - typedef CopySupport<TY> CopyAPI; + typedef CopySupport<TY,EmptyBase> CopyAPI; enum{ ADMIN_OVERHEAD = 2 * sizeof(void*) }; static CopyAPI& accessCopyHandlingInterface (TY& bufferContents) { REQUIRE (INSTANCEOF (CopyAPI, &bufferContents)); - return dynamic_cast<CopyAPI&> (bufferContents); + return dynamic_cast<CopyAPI&> (bufferContents); } typedef CopyAPI AdapterAttachment; }; + /** + * Special case when the embedded types support copying + * on the API level, e.g. there is a sub-API exposing a \c cloneInto + * function. In this case, the actual implementation classes can be + * instantiated as-is and the copy operations can be accessed by a + * simple \c static_cast without runtime overhead. + */ template <class TY> struct Trait<TY, typename enable_if< exposes_copySupportFunctions<TY> >::type> { @@ -185,13 +247,13 @@ namespace lib { accessCopyHandlingInterface (IFA& bufferContents) { REQUIRE (INSTANCEOF (CopyAPI, &bufferContents)); - return static_cast<CopyAPI&> (bufferContents); + return static_cast<CopyAPI&> (bufferContents); } typedef EmptyBase AdapterAttachment; }; - } + }//(End)implementation details @@ -205,7 +267,7 @@ namespace lib { * interface. The actual implementation object might be placed into the * buffer through a builder function; later, this buffer may be copied * and passed on without knowing the actual contained type. - * + * * For using PolymorphicValue, several \b assumptions need to be fulfilled * - any instance placed into OpaqueHolder is below the specified maximum size * - the caller cares for thread safety. No concurrent get calls while in mutation! @@ -227,57 +289,73 @@ namespace lib { siz = storage + _Traits::ADMIN_OVERHEAD }; + + /* === embedded object in buffer === */ + + /** Storage for embedded objects */ mutable char buf_[siz]; - template<class IMP> - IMP& - access() const + IFA& + accessEmbedded() const { - return reinterpret_cast<IMP&> (buf_); + return reinterpret_cast<IFA&> (buf_); } void - destroy() + destroyEmbedded() { - access<IFA>().~IFA(); + accessEmbedded().~IFA(); } -// REQUIRE (siz >= sizeof(IMP)); template<class IMP> PolymorphicValue (IMP*) { + REQUIRE (siz >= sizeof(IMP)); new(&buf_) IMP(); } template<class IMP, typename A1> PolymorphicValue (IMP*, A1& a1) { + REQUIRE (siz >= sizeof(IMP)); new(&buf_) IMP (a1); } template<class IMP, typename A1, typename A2> PolymorphicValue (IMP*, A1& a1, A2& a2) { + REQUIRE (siz >= sizeof(IMP)); new(&buf_) IMP (a1,a2); } template<class IMP, typename A1, typename A2, typename A3> PolymorphicValue (IMP*, A1& a1, A2& a2, A3& a3) { + REQUIRE (siz >= sizeof(IMP)); new(&buf_) IMP (a1,a2,a3); } + /** + * Implementation Helper: supporting copy operations. + * Actually instances of this Adapter template are placed + * into the internal buffer, such that they both inherit + * from the desired implementation type and the copy + * support interface. The implementation of the + * concrete copy operations is provided here + * forwarding to the copy operations + * of the implementation object. + */ template<class IMP> class Adapter : public IMP - , public _Traits::AdapterAttachment + , public _Traits::AdapterAttachment // mix-in, might be empty { virtual void cloneInto (void* targetBuffer) const { - new(targetBuffer) Adapter(*this); + new(targetBuffer) Adapter(*this); // forward to copy ctor } virtual void @@ -285,11 +363,11 @@ namespace lib { { REQUIRE (INSTANCEOF (Adapter, &targetBase)); Adapter& target = static_cast<Adapter&> (targetBase); - target = (*this); + target = (*this); // forward to assignment operator } - public: - /* using default copy and assignment */ + public: /* == forwarding ctor to implementation type == */ + Adapter() : IMP() { } template<typename A1> @@ -300,31 +378,40 @@ namespace lib { template<typename A1, typename A2, typename A3> Adapter (A1& a1, A2& a2, A3& a3) : IMP(a1,a2,a3) { } + + /* using default copy and assignment */ }; _CopyHandlingAdapter& accessHandlingInterface () const { - IFA& bufferContents = access<IFA>(); - _CopyHandlingAdapter& hap = _Traits::accessCopyHandlingInterface (bufferContents); - return hap; ////TODO cleanup unnecessary temporary + IFA& bufferContents = accessEmbedded(); + return _Traits::accessCopyHandlingInterface (bufferContents); } - public: - operator IFA& () + public: /* === PolymorphicValue public API === */ + + typedef IFA Interface; + + operator Interface& () { - return access<IFA>(); + return accessEmbedded(); } - operator IFA const& () const + operator Interface const& () const { - return access<IFA>(); + return accessEmbedded(); + } + Interface* + operator-> () const + { + return &( accessEmbedded() ); } ~PolymorphicValue() { - destroy(); + destroyEmbedded(); } PolymorphicValue (PolymorphicValue const& o) @@ -335,10 +422,12 @@ namespace lib { PolymorphicValue& operator= (PolymorphicValue const& o) { - o.accessHandlingInterface().copyInto (this->access<IFA>()); + o.accessHandlingInterface().copyInto (this->accessEmbedded()); return *this; } + /* === static factory functions === */ + template<class IMP> static PolymorphicValue build () @@ -371,10 +460,13 @@ namespace lib { return PolymorphicValue (type_to_build_in_buffer, a1,a2,a3); } + + /* === support Equality by forwarding to embedded === */ + friend bool operator== (PolymorphicValue const& v1, PolymorphicValue const& v2) { - return v1.access<IFA>() == v2.access<IFA>(); + return v1.accessEmbedded() == v2.accessEmbedded(); } friend bool operator!= (PolymorphicValue const& v1, PolymorphicValue const& v2) diff --git a/tests/lib/polymorphic-value-test.cpp b/tests/lib/polymorphic-value-test.cpp index 26570c8ff..bdc366f9c 100644 --- a/tests/lib/polymorphic-value-test.cpp +++ b/tests/lib/polymorphic-value-test.cpp @@ -24,14 +24,10 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" -#include "lib/util.hpp" #include "lib/util-foreach.hpp" #include "lib/polymorphic-value.hpp" -//#include "lib/bool-checkable.hpp" -#include <iostream> -//#include <cstdlib> #include <vector> @@ -39,16 +35,11 @@ namespace lib { namespace test{ using ::Test; -// using util::isnil; using util::for_each; using util::unConst; using util::isSameObject; -// using lumiera::error::LUMIERA_ERROR_INVALID; using lumiera::error::LUMIERA_ERROR_ASSERTION; -// using std::vector; -// using std::cout; -// using std::endl; namespace { // test dummy hierarchy // Note: largely varying space requirements @@ -72,15 +63,23 @@ namespace test{ const uint MAX_RAND = 1000; - const uint MAX_SIZ = sizeof(long[113]); /////////////////////TODO: using just 111 causes SEGV ---> suspect the HandlingAdapter mixin to require additional storage + const uint MAX_ELM = 111; + const uint MAX_SIZ = sizeof(long[MAX_ELM]); + /* Checksums to verify proper ctor-dtor calls and copy operations */ long _checkSum = 0; long _callSum = 0; uint _created = 0; - template<uint ii> - struct Imp : Interface + /** + * Template to generate concrete implementation classes. + * @note the generated classes vary largely in size, and + * moreover the actual place to store the checksum + * also depends on that size parameter. + */ + template<uint ii, class BASE=Interface> + struct Imp : BASE { long localData_[ii]; @@ -159,6 +158,7 @@ namespace test{ + /********************************************************************************** * @test build a bunch of PolymorphicValue objects. Handle them like copyable * value objects, without knowing the exact implementation type; moreover @@ -175,9 +175,9 @@ namespace test{ _callSum = 0; _created = 0; + verifyBasics(); + { - verifyBasics(); - TestList objs = createOpaqueValues (); for_each (objs, operate); } @@ -207,8 +207,7 @@ namespace test{ CHECK (elm == myLocalVal); long prevSum = _callSum; - Interface& subject = myLocalVal; - long randVal = subject.apiFunc(); + long randVal = myLocalVal->apiFunc(); CHECK (prevSum + randVal == _callSum); CHECK (elm != myLocalVal); @@ -223,25 +222,59 @@ namespace test{ void verifyBasics() { + typedef Imp<MAX_ELM> MaximumSizedImp; + + // Standard case: no copy support by client objects + verifyCreation_and_Copy<PolyVal, MaximumSizedImp>(); + + // Special case: client objects expose extension point for copy support + typedef polyvalue::CopySupport<Interface> CopySupportAPI; // Copy support API declared as sub-interface + typedef Imp<MAX_ELM,CopySupportAPI> CopySupportingImp; // insert this sub-interface between public API and Implementation + typedef PolymorphicValue<Interface, MAX_SIZ, CopySupportAPI> OptimalPolyVal; // Make the Holder use this special attachment point + CHECK (sizeof(OptimalPolyVal) < sizeof(PolyVal)); // results in smaller Holder and less implementation overhead + + verifyCreation_and_Copy<OptimalPolyVal, CopySupportingImp>(); + } + + + template<class PV,class IMP> + void + verifyCreation_and_Copy() + { + typedef PV Holder; + typedef IMP ImpType; + typedef typename PV::Interface Api; + long prevSum = _checkSum; uint prevCnt = _created; - PolyVal val = PolyVal::build<Imp<111> >(); + Holder val = Holder::template build<ImpType>(); CHECK (prevSum+111 == _checkSum); // We got one primary ctor call CHECK (prevCnt+1 <= _created); // Note: usually, the compiler optimises CHECK (prevCnt+2 >= _created); // and skips the spurious copy-operation - CHECK (sizeof(PolyVal) > sizeof(Imp<111>)); - Interface& embedded = val; + CHECK (sizeof(Holder) >= sizeof(ImpType)); + Api& embedded = val; CHECK (isSameObject(embedded,val)); - CHECK (INSTANCEOF(Imp<111>, &embedded)); + CHECK (INSTANCEOF(ImpType, &embedded)); + + prevCnt = _created; + Holder val2(val); // invoke copy ctor without knowing the implementation type + embedded.apiFunc(); + CHECK (val != val2); // invoking the API function had an sideeffect on the state + val = val2; // assignment of copy back to the original... + CHECK (val == val2); // cancels the side effect + + CHECK (prevCnt+1 == _created); // one new embedded instance was created by copy ctor } void verifyOverrunProtection() { + typedef Imp<MAX_ELM+1> OversizedImp; + CHECK (MAX_SIZ < sizeof(OversizedImp)); #if false ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT - VERIFY_ERROR (ASSERTION, PolyVal::build<Imp<112> >() ); + VERIFY_ERROR (ASSERTION, PolyVal::build<OversizedImp>() ); #endif ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT } }; From 6fa11ffcf6b0a7fcb95c1d052e5ee215ea81b350 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 25 Apr 2011 04:49:05 +0200 Subject: [PATCH 054/496] special case: support cloning, but not copy needed to add yet another policy template, so PolymorphicValue doesn't invoke the assignment operator in such cases --- src/lib/meta/duck-detector.hpp | 20 ++++++- src/lib/polymorphic-value.hpp | 83 ++++++++++++++++++++++++++-- tests/lib/polymorphic-value-test.cpp | 25 +++++++++ 3 files changed, 121 insertions(+), 7 deletions(-) diff --git a/src/lib/meta/duck-detector.hpp b/src/lib/meta/duck-detector.hpp index 0091a894f..84363616e 100644 --- a/src/lib/meta/duck-detector.hpp +++ b/src/lib/meta/duck-detector.hpp @@ -60,9 +60,25 @@ ** specific function signature and then try to assign the named member. This allows even ** to determine if a member function of a type in question has the desired signature. ** - ** All these detection building blocks are written such as to provide a bool member \v ::value, + ** All these detection building blocks are written such as to provide a bool member \c ::value, ** which is in accordance to the conventions of boost metaprogramming. I.e. you can immediately - ** use them within boost::enable_if + ** use them within \c boost::enable_if + ** + ** \par some pitfalls to consider + ** + ** @warning The generated metafunctions all yield the \c false value by default. + ** Effectively this means that an error in the test expression might go unnoticed; + ** you'd be better off explicitly checking the detection result by an unit test. + ** + ** There are several typical problems to care about + ** - a member can be both a variable or a function of that name + ** - function signatures need to match precisely, including const modifiers + ** - the generated metafunction (template) uses a type parameter 'TY', which could + ** shadow or conflict with an type parameter in the enclosing scope + ** - the member and function checks rely on member pointers, which generally rely on + ** the explicit static type. These checks don't see any inherited members / functions. + ** - obviously, all those checks are never able to detect anything depending on runtime + ** types or RTTI ** ** @see util-foreach.hpp usage example ** @see duck-detector-test.cpp diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp index 94a4b4b09..0f13a9b98 100644 --- a/src/lib/polymorphic-value.hpp +++ b/src/lib/polymorphic-value.hpp @@ -153,6 +153,7 @@ namespace lib { namespace polyvalue { // implementation details... + namespace error = lumiera::error; using boost::enable_if; using lumiera::Yes_t; using lumiera::No_t; @@ -186,6 +187,21 @@ namespace lib { virtual void copyInto (IFA& targetBase) const =0; }; + /** + * A variation for limited copy support. + * Sometimes, only cloning (copy construction) of value objects is allowed, + * but not assignment of new values to existing objects. In this case, the + * concrete values can inherit from this variant of the support API. + */ + template<class BA> ///< direct baseclass to use for this clone support API + class CloneValueSupport + : public BA + { + public: + virtual ~CloneValueSupport() { }; + virtual void cloneInto (void* targetBuffer) const =0; + }; + /** @@ -193,7 +209,7 @@ namespace lib { * to support clone operations */ template<typename T> - class exposes_copySupportFunctions + class exposes_CloneFunction { META_DETECT_FUNCTION(void, cloneInto, (void*) const); @@ -203,8 +219,58 @@ namespace lib { }; }; + /** + * helper to detect if the API supports only + * copy construction, but no assignment + */ + template<typename T> + class allow_Clone_but_no_Copy + { + META_DETECT_MEMBER(copyInto); + + public: + enum{ value = exposes_CloneFunction<T>::value + && ! HasMember_copyInto<T>::value + }; + }; - /** + /** + * Policy class for invoking the assignment operator. + * By default the embedded payload objects are assumed + * to be freely assignable + */ + template<class API, class YES =void> + struct AssignmentPolicy + { + template<class IMP> + static void + assignEmbedded(IMP& dest,IMP const& src) + { + dest = src; + } + }; + + /** + * special case when the embedded payload objects permit + * copy construction, but no assignment to existing instances. + * Instead of invoking the assignment operator (which typically + * isn't defined at all in such cases), a misguided assignment + * to the container will raise an exception + */ + template<class API> + struct AssignmentPolicy<API, typename enable_if< allow_Clone_but_no_Copy<API> >::type> + { + template<class IMP> + static void + assignEmbedded(IMP&,IMP const&) + { + throw error::Logic("attempt to overwrite unmodifiable value"); + } + }; + + + + /** * traits template to deal with * different ways to support copy operations. * Default is no support by the API and implementation types. @@ -226,6 +292,7 @@ namespace lib { } typedef CopyAPI AdapterAttachment; + typedef AssignmentPolicy<CopyAPI> Assignment; }; @@ -237,7 +304,7 @@ namespace lib { * simple \c static_cast without runtime overhead. */ template <class TY> - struct Trait<TY, typename enable_if< exposes_copySupportFunctions<TY> >::type> + struct Trait<TY, typename enable_if< exposes_CloneFunction<TY> >::type> { typedef TY CopyAPI; enum{ ADMIN_OVERHEAD = 1 * sizeof(void*) }; @@ -251,6 +318,7 @@ namespace lib { } typedef EmptyBase AdapterAttachment; + typedef AssignmentPolicy<CopyAPI> Assignment; }; }//(End)implementation details @@ -259,6 +327,10 @@ namespace lib { + + + + /** * Template to build polymorphic value objects. * Inline buffer with value semantics, yet holding and owning an object @@ -285,6 +357,7 @@ namespace lib { typedef polyvalue::Trait<CPY> _Traits; typedef typename _Traits::CopyAPI _CopyHandlingAdapter; + typedef typename _Traits::Assignment _AssignmentPolicy; enum{ siz = storage + _Traits::ADMIN_OVERHEAD }; @@ -363,8 +436,8 @@ namespace lib { { REQUIRE (INSTANCEOF (Adapter, &targetBase)); Adapter& target = static_cast<Adapter&> (targetBase); - target = (*this); // forward to assignment operator - } + _AssignmentPolicy::assignEmbedded(target,*this); + } // forward to assignment operator public: /* == forwarding ctor to implementation type == */ diff --git a/tests/lib/polymorphic-value-test.cpp b/tests/lib/polymorphic-value-test.cpp index bdc366f9c..8ee1ef356 100644 --- a/tests/lib/polymorphic-value-test.cpp +++ b/tests/lib/polymorphic-value-test.cpp @@ -184,6 +184,7 @@ namespace test{ CHECK (0 == _checkSum); // all dead verifyOverrunProtection(); + verifyCopySupportDetectionMetafunctions(); } @@ -277,6 +278,30 @@ namespace test{ VERIFY_ERROR (ASSERTION, PolyVal::build<OversizedImp>() ); #endif ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT } + + + /** @Test internally, PolymorphicValue uses some metafunctions + * to pick a suitable code path, based on the presence of helper functions + * on the API of the embedded objects. Default is no support by these objects, + * which then requires to use a more expensive implementation. Sometimes it's + * desirable to support \em cloning only (copy ctor), but no assignment after + * the fact. In this special case, a support API with only a \cloneInto member + * can be implemented, causing the PolymorphicValue container to raise an + * exception in case the copy operator is invoked. + */ + void + verifyCopySupportDetectionMetafunctions() + { + typedef polyvalue::CopySupport<Interface> CopySupportAPI; + typedef polyvalue::CloneValueSupport<Interface> CloneOnlyAPI; + + CHECK ( !polyvalue::exposes_CloneFunction<Interface>::value ); + CHECK ( polyvalue::exposes_CloneFunction<CopySupportAPI>::value ); + CHECK ( polyvalue::exposes_CloneFunction<CloneOnlyAPI>::value ); + + CHECK ( polyvalue::allow_Clone_but_no_Copy<CloneOnlyAPI>::value ); + CHECK ( !polyvalue::allow_Clone_but_no_Copy<CopySupportAPI>::value ); + } }; From 58513326284b189981dab7a58399e4154b3902d2 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 25 Apr 2011 05:42:05 +0200 Subject: [PATCH 055/496] back to business: now able to implement time mutation messages --- src/lib/time/mutation.cpp | 72 ++++++++++++++++++++++++++++++++------ src/lib/time/mutation.hpp | 20 ++++++++--- src/lib/time/timevalue.hpp | 6 ++++ src/proc/asset.hpp | 2 +- wiki/renderengine.html | 10 +++--- 5 files changed, 90 insertions(+), 20 deletions(-) diff --git a/src/lib/time/mutation.cpp b/src/lib/time/mutation.cpp index 070386c62..4cc06d931 100644 --- a/src/lib/time/mutation.cpp +++ b/src/lib/time/mutation.cpp @@ -65,8 +65,8 @@ namespace time { * concrete time value mutation: * impose fixed new start time. */ - class SetNewStartTime - : public Mutation + class SetNewStartTimeMutation + : public ClonableMutation { TimeValue newTime_; @@ -96,29 +96,81 @@ namespace time { public: explicit - SetNewStartTime (TimeValue t) + SetNewStartTimeMutation (TimeValue t) : newTime_(t) { } }; + /** + * concrete time value mutation: + * impose fixed new start time. + */ + class SetNewDuration + : public ClonableMutation + { + Duration changedDuration_; + + virtual void + change (Duration& target) const + { + imposeChange (target, changedDuration_); + } + + + virtual void + change (TimeSpan& target) const + { + imposeChange (target.duration(), changedDuration_); + } + + + /** @note the re-quantisation happens automatically + * when the (changed) QuTime is materialised */ + virtual void + change (QuTime&) const + { + throw error::Logic ("mutating the duration of a (quantised) time point doesn't make sense" + , LUMIERA_ERROR_INVALID_MUTATION); + } + + + public: + explicit + SetNewDuration (Duration dur) + : changedDuration_(dur) + { } + }; - /** */ - void + + + /** Convenience factory to yield a simple Mutation changing the absolute start time. + * This whole procedure might look quite inefficient, but actually most of the + * abstractions are removed at runtime, leaving only a single indirection + * through the VTable of the Mutation Interface. + * @throw error::Logic when attempting to change the (non existent) start time of a Duration + * @return EncapsulatedMutation, which is an "polymorphic value object", + * actually carrying an embedded Mutation subclass with the new start time + */ + EncapsulatedMutation Mutation::changeTime (Time newStartTime) { - UNIMPLEMENTED ("perform a trivial mutation to set a new fixed time value"); + return EncapsulatedMutation::build<SetNewStartTimeMutation> (newStartTime); } - void - Mutation::changeDuration (Duration) + /** Convenience factory: simple Mutation to adjust the duration or length of a timespan + * @throw error::Logic when attempting to change the "duration" of a quantised time point + * @return EncapsulatedMutation, carrying the new duration value to impose + */ + EncapsulatedMutation + Mutation::changeDuration (Duration changedDur) { - UNIMPLEMENTED ("perform a trivial mutation to set a new Duration"); + return EncapsulatedMutation::build<SetNewDuration> (changedDur); } - void + EncapsulatedMutation Mutation::nudge (int adjustment) { UNIMPLEMENTED ("Nudge by a predefined nudge amount"); diff --git a/src/lib/time/mutation.hpp b/src/lib/time/mutation.hpp index bbb325911..c5e920356 100644 --- a/src/lib/time/mutation.hpp +++ b/src/lib/time/mutation.hpp @@ -55,6 +55,7 @@ #include "lib/error.hpp" #include "lib/time/timevalue.hpp" +#include "lib/polymorphic-value.hpp" //#include "lib/symbol.hpp" #include <boost/noncopyable.hpp> @@ -72,6 +73,18 @@ namespace time { LUMIERA_ERROR_DECLARE (INVALID_MUTATION); ///< Changing a time value in this way was not designated class QuTime; + class Mutation; + + + /* The following typedefs allow to hand out + * "unspecific" mutation value objects from + * factory functions, without disclosing any + * implementation details here in this header. + */ + enum{ MUTATION_IMPL_SIZE = sizeof(TimeValue) }; + + typedef lib::polyvalue::CloneValueSupport<Mutation> ClonableMutation; + typedef lib::PolymorphicValue<Mutation, MUTATION_IMPL_SIZE, ClonableMutation> EncapsulatedMutation; /** * Interface: an opaque change imposed onto some time value. @@ -80,7 +93,6 @@ namespace time { * @todo WIP-WIP-WIP */ class Mutation - : boost::noncopyable { public: virtual ~Mutation(); @@ -91,9 +103,9 @@ namespace time { /* === convenience shortcuts for simple cases === */ - static void changeTime (Time); - static void changeDuration (Duration); - static void nudge (int adjustment); + static EncapsulatedMutation changeTime (Time); + static EncapsulatedMutation changeDuration (Duration); + static EncapsulatedMutation nudge (int adjustment); protected: static void imposeChange (TimeValue&, TimeValue); diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index b6fc7d28f..d7fe90b30 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -380,6 +380,12 @@ namespace time { { } + Duration& + duration() + { + return dur_; + } + Duration duration() const { diff --git a/src/proc/asset.hpp b/src/proc/asset.hpp index bef9463ac..bd26b92ae 100644 --- a/src/proc/asset.hpp +++ b/src/proc/asset.hpp @@ -130,7 +130,7 @@ namespace asset { * - asset::Media representing mediafiles * - asset::Proc representing media data processing components (e.g. Effects) * - asset::Struct representing structural components used in the session (e.g. Tracks) - * - asset::Meta representing meta objects created while editing (e.g. Automation) + * - asset::Meta representing metadata, parametrisation, customisation. * * And of course there are various concrete Asset subclasses, like asset::Clip, * asset::Effect, asset::Codec, asset::Track, asset::Dataset. diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 0f414c368..d0649b5c1 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6618,7 +6618,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]] </pre> </div> -<div title="TimeMutation" modifier="Ichthyostega" modified="201104040005" created="201101231344" tags="spec discuss draft" changecount="31"> +<div title="TimeMutation" modifier="Ichthyostega" modified="201104250335" created="201101231344" tags="spec discuss draft" changecount="34"> <pre>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 @@ -6666,9 +6666,10 @@ This results in the following combinations to be implemented: |~|quantised ||set raw value | | Duration|~TimeSpan ||set length | |~|Duration ||set length | +|~|quantised || -- | | Offset|Time || -- | -|~|Duration || -- | -|~|~TimeSpan ||adjust start/length | +|~|Duration ||adjust length | +|~|~TimeSpan ||adjust start | |~|quantised ||adjust raw value | | ~QuTime|any raw ||treat as raw ~TimeValue | |~|~QuTime ||materialise, then set raw | @@ -6698,8 +6699,7 @@ Question is: how fine grained and configurable needs this to be? !!! 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. +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. </pre> </div> <div title="TimeQuant" modifier="Ichthyostega" modified="201101232031" created="201012181753" tags="Concepts Player spec img discuss draft" changecount="50"> From baf601c16bc99c982320b315ae5e372a69302584 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 25 Apr 2011 06:46:36 +0200 Subject: [PATCH 056/496] first time mutation test cases pass --- src/lib/time/mutation.cpp | 2 +- src/lib/time/mutation.hpp | 2 +- tests/lib/time/time-mutation-test.cpp | 41 +++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/lib/time/mutation.cpp b/src/lib/time/mutation.cpp index 4cc06d931..3a8ae6681 100644 --- a/src/lib/time/mutation.cpp +++ b/src/lib/time/mutation.cpp @@ -53,7 +53,7 @@ namespace time { * value stored in each time entity */ void - Mutation::imposeChange (TimeValue& target, TimeValue valueToSet) + Mutation::imposeChange (TimeValue& target, TimeValue const& valueToSet) { target = valueToSet; } diff --git a/src/lib/time/mutation.hpp b/src/lib/time/mutation.hpp index c5e920356..fd66bb010 100644 --- a/src/lib/time/mutation.hpp +++ b/src/lib/time/mutation.hpp @@ -108,7 +108,7 @@ namespace time { static EncapsulatedMutation nudge (int adjustment); protected: - static void imposeChange (TimeValue&, TimeValue); + static void imposeChange (TimeValue&, TimeValue const&); }; diff --git a/tests/lib/time/time-mutation-test.cpp b/tests/lib/time/time-mutation-test.cpp index 7b9e21622..3a973763b 100644 --- a/tests/lib/time/time-mutation-test.cpp +++ b/tests/lib/time/time-mutation-test.cpp @@ -25,6 +25,7 @@ #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/time/display.hpp" #include "lib/util.hpp" @@ -107,7 +108,7 @@ namespace test{ QuTime qVal (o, "test_grid"); FrameNr count(qVal); - mutate_by_Value (o, c); + mutate_by_Value (o, Time(c)); mutate_by_Offset (o, Offset(c)); mutate_quantised (o, qVal); mutate_by_Increment(o, count); @@ -115,9 +116,43 @@ namespace test{ void - mutate_by_Value(TimeValue o, TimeValue change) + mutate_by_Value(TimeValue original, Time newStart) { - TestValues t(o); + TestValues t(original); + + CHECK (t.span.start() == original); + t.span.accept (Mutation::changeTime (newStart)); + CHECK (t.span.start() != original); + CHECK (t.span.start() == newStart); + + // instead of invoking directly, we can store and copy mutation messages + EncapsulatedMutation change_back(Mutation::changeTime (Time(original))); + t.span.accept (change_back); + CHECK (t.span.start() == original); + + CHECK (t.quant == original); + t.quant.accept (Mutation::changeTime (newStart)); + CHECK (t.quant != original); + CHECK (t.quant == newStart); + + // Durations have no start time... + VERIFY_ERROR (INVALID_MUTATION, t.dur.accept(change_back)); + VERIFY_ERROR (INVALID_MUTATION, t.span.duration().accept(change_back)); + + CHECK (t.dur == original); + t.dur.accept (Mutation::changeDuration (Duration(2*t.var))); + CHECK (t.dur != original); + CHECK (t.dur == t.var*2); + + CHECK (t.span.start() == original); + CHECK (t.span.duration() == original); + t.span.accept (Mutation::changeDuration(Duration(3*t.var))); + CHECK (t.span.duration() != original); + CHECK (t.span.duration() == t.var*3); // affects the duration, + CHECK (t.span.start() == original); // while the start time remains unaltered + + // can't change the 'duration' of a quantised time point... + VERIFY_ERROR (INVALID_MUTATION, t.quant.accept(Mutation::changeDuration (Duration(t.var)))); } From 203540525106fad012ed95397f140585847b931e Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 25 Apr 2011 18:51:50 +0200 Subject: [PATCH 057/496] add time Mutation to adjust by offset --- src/lib/time/mutation.cpp | 53 ++++++++++++++++++++++++++- src/lib/time/mutation.hpp | 1 + src/lib/time/timevalue.hpp | 8 ++++ src/proc/asset/meta.hpp | 5 ++- tests/lib/time/time-mutation-test.cpp | 25 ++++++++++++- 5 files changed, 87 insertions(+), 5 deletions(-) diff --git a/src/lib/time/mutation.cpp b/src/lib/time/mutation.cpp index 3a8ae6681..6555c22b4 100644 --- a/src/lib/time/mutation.cpp +++ b/src/lib/time/mutation.cpp @@ -104,7 +104,7 @@ namespace time { /** * concrete time value mutation: - * impose fixed new start time. + * set a new overall duration for an extended timespan. */ class SetNewDuration : public ClonableMutation @@ -143,6 +143,46 @@ namespace time { }; + /** + * concrete time value mutation: + * adjust the given time entity by an offset amount. + */ + class ImposeOffsetMutation + : public ClonableMutation + { + Offset adjustment_; + + virtual void + change (Duration& target) const + { + imposeChange (target, TimeVar(target)+=adjustment_); + } + + + virtual void + change (TimeSpan& target) const + { + imposeChange (target, TimeVar(target)+=adjustment_); + } + + + /** @note the re-quantisation happens automatically + * when the (changed) QuTime is materialised */ + virtual void + change (QuTime& target) const + { + imposeChange (target, TimeVar(target)+=adjustment_); + } + + + public: + explicit + ImposeOffsetMutation (Offset adj) + : adjustment_(adj) + { } + }; + + /** Convenience factory to yield a simple Mutation changing the absolute start time. * This whole procedure might look quite inefficient, but actually most of the @@ -170,6 +210,17 @@ namespace time { } + /** Convenience factory: simple Mutation to adjust the duration or length of a timespan + * @throw error::Logic when attempting to change the "duration" of a quantised time point + * @return EncapsulatedMutation, carrying the new duration value to impose + */ + EncapsulatedMutation + Mutation::adjust (Offset change) + { + return EncapsulatedMutation::build<ImposeOffsetMutation> (change); + } + + EncapsulatedMutation Mutation::nudge (int adjustment) { diff --git a/src/lib/time/mutation.hpp b/src/lib/time/mutation.hpp index fd66bb010..13c1b6bca 100644 --- a/src/lib/time/mutation.hpp +++ b/src/lib/time/mutation.hpp @@ -105,6 +105,7 @@ namespace time { static EncapsulatedMutation changeTime (Time); static EncapsulatedMutation changeDuration (Duration); + static EncapsulatedMutation adjust (Offset); static EncapsulatedMutation nudge (int adjustment); protected: diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index d7fe90b30..aa08c00ca 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -198,6 +198,9 @@ namespace time { { return TimeValue(std::llabs (t_)); } + + // Supporting sign flip + Offset operator- () const; }; //-- support linear offset chaining --------------- @@ -224,6 +227,11 @@ namespace time { return factor*distance; } + inline Offset + Offset::operator- () const + { + return -1 * (*this); + } diff --git a/src/proc/asset/meta.hpp b/src/proc/asset/meta.hpp index 77e08b71a..9ac73ea4f 100644 --- a/src/proc/asset/meta.hpp +++ b/src/proc/asset/meta.hpp @@ -48,7 +48,7 @@ ** and can be another already existing asset::Meta (inheriting from meta::Descriptor) ** Generally this yields a Builder object, which can be used for outfitting the new ** or changed metadata entry, finally \em committing this builder to yield a new - ** asset::Meta (which, in case of a mutation, might superseede an existin one). + ** asset::Meta (which, in case of a mutation, might supersede an existing one). ** ** @see asset.hpp explanation of assets in general ** @see MetaFactory creating concrete asset::Meta instances @@ -120,7 +120,8 @@ namespace asset { public: static MetaFactory create; - virtual const ID<Meta>& getID() const ///< @return ID of kind Meta + /** @return ID of kind Meta */ + virtual const ID<Meta>& getID() const { return static_cast<const ID<Meta>& > (Asset::getID()); } diff --git a/tests/lib/time/time-mutation-test.cpp b/tests/lib/time/time-mutation-test.cpp index 3a973763b..65bd35612 100644 --- a/tests/lib/time/time-mutation-test.cpp +++ b/tests/lib/time/time-mutation-test.cpp @@ -157,9 +157,30 @@ namespace test{ void - mutate_by_Offset (TimeValue o, Offset change) + mutate_by_Offset (TimeValue original, Offset change) { - TestValues t(o); + TestValues t(original); + TimeValue& should_be(t.var+=change); // use as ref for verification + + CHECK (t.span == original); + CHECK (t.span != should_be); + t.span.accept (Mutation::adjust (change)); + CHECK (t.span == should_be); + + t.dur.accept (Mutation::adjust (change)); + CHECK (t.dur == should_be); + + t.quant.accept (Mutation::adjust (change)); + CHECK (t.quant == should_be); + + // adjustment is cumulative + EncapsulatedMutation back_off = Mutation::adjust (-change); + t.span.accept (back_off); + CHECK (t.span == original); + t.span.accept (back_off); + t.span.accept (back_off); + t.span.accept (back_off); + CHECK (t.span == Time(original) - 3*change); } From bf61ff7248544cf79cd8c80c8dcf8e2590a03ad5 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 26 Apr 2011 06:11:31 +0200 Subject: [PATCH 058/496] WIP test-driven brainstorming: nudge by grid --- src/lib/time/formats.hpp | 2 +- src/lib/time/timecode.cpp | 2 +- tests/lib/time/time-mutation-test.cpp | 74 +++++++++++++++++++++++++-- wiki/renderengine.html | 29 ++++++----- 4 files changed, 88 insertions(+), 19 deletions(-) diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index b82a8f631..aae1226a5 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -192,7 +192,7 @@ namespace time { flags_.set (typeID<F>()); return define(FS()); } - Supported define(NullType) { return *this;} ///< recursion end + Supported define(NullType) { return *this;} ///< @internal recursion end Supported() { } ///< @note use #formats to set up a new descriptor diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index dd4b67015..587293995 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -51,7 +51,7 @@ namespace time { void Frames::rebuild (FrameNr& framecnt, QuantR quantiser, TimeValue const& rawTime) { - framecnt.setValueRaw(quantiser.gridPoint (rawTime)); + framecnt.setValueRaw (quantiser.gridPoint (rawTime)); } /** calculate the time point denoted by this frame count */ diff --git a/tests/lib/time/time-mutation-test.cpp b/tests/lib/time/time-mutation-test.cpp index 65bd35612..e1d461334 100644 --- a/tests/lib/time/time-mutation-test.cpp +++ b/tests/lib/time/time-mutation-test.cpp @@ -101,6 +101,7 @@ namespace test{ { 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))); @@ -185,16 +186,81 @@ namespace test{ void - mutate_quantised (TimeValue o, QuTime change) + mutate_quantised (TimeValue original, QuTime change) { - TestValues t(o); + TestValues t(original); + t.var = change; + CHECK (Time(change) == t.var); // the underlying raw time value + + CHECK (t.span == original); + t.span.accept (Mutation::materialise (change)); + CHECK (t.span != original); + CHECK (t.span != t.var); // really materialised (grid-aligned) + + // simulate what happened by explicit operations... + Secs seconds = change.formatAs<format::Seconds>(); + Time materialised = seconds.getTime(); + CHECK (t.span == materialised); + + CHECK (t.span.duration() == original); // not affected by mutation as usual + VERIFY_ERROR (INVALID_MUTATION, t.dur.accept (Mutation::materialise (change))); + // not surprising, a time point has no duration!! + + CHECK (t.quant == original); + t.quant.accept (Mutation::materialise (change)); + CHECK (t.quant != original); + CHECK (t.quant == materialised); + // but note, here we checked the underlying raw value. + // because t.quant is itself quantised, this might + // result in a second, chained quantisation finally + + // Here accidentally both the change and t.quant use the same grid. + // For a more contrived example, we try to use a different grid... + TimeGrid::build("special_funny_grid", 1); // (1 frame per second, no offset) + QuTime funny (original, "special_funny_grid"); + funny.accept (Mutation::materialise (change)); + CHECK (funny == t.quant); // leading to the same raw value this far + + Time doublyQuantised = Secs(funny).getTime(); + CHECK (doublyQuantised != materialised); } void - mutate_by_Increment (TimeValue o, int change) + mutate_by_Increment (TimeValue original, int change) { - TestValues t(o); + TestValues t(original); + + // without any additional specification, + // the nudge-Mutation uses a 'natural grid' + t.span.accept (Mutation::nudge (change)); + t.dur.accept (Mutation::nudge (change)); + + t.var += Time(FSecs(change)); // natural grid is in seconds + CHECK (t.span.start() == t.var); + CHECK (t.dur == t.var); + + // any other grid can be specified explicitly + t.dur.accept (Mutation::nudge (change, "test_grid")); + CHECK (t.dur != t.var); + CHECK (t.dur == t.var + change * FrameRate::PAL.duration()); + // ....this time the change was measured in grid units, + // taken relative to the origin of the specified grid + PGrid testGrid = TimeGrid::retrieve("test_grid"); + Offset distance (testGrid->timeOf(0), testGrid->timeOf(change)); + CHECK (distance == change * FrameRate::PAL.duration()); + CHECK (Time(t.dur) - t.var == distance); + + + + // To the contrary, *quantised* values behave quite differently... + long frameNr = t.quant.formatAs<format::Frames>(); + + t.quant.accept (Mutation::nudge (change)); + CHECK (t.quant != original); + long frameNr_after = t.quant.formatAs<format::Frames>(); + CHECK (frameNr_after != frameNr + change); + // i.e., use the quantised time's own grid } }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index d0649b5c1..2cca48ddd 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -4193,18 +4193,18 @@ Viewed as a micro program, the processing patterns are ''weak typed'' &mdash <pre>a given Render Engine configuration is a list of Processors. Each Processor in turn contains a Graph of ProcNode.s to do the acutal data processing. In order to cary out any calculations, the Processor needs to be called with a StateProxy containing the state information for this RenderProcess </pre> </div> -<div title="QuantiserImpl" modifier="Ichthyostega" modified="201101081815" created="201101061212" tags="impl spec" changecount="21"> +<div title="QuantiserImpl" modifier="Ichthyostega" modified="201104252333" created="201101061212" tags="impl spec" changecount="24"> <pre>The Quantiser implementation works by determining the grid interval containing a given raw time. These grid intervals are denoted by ordinal numbers (frame numbers), with interval #0 starting at the grid's origin and negative ordinals allowed. !frame quantisation convention -Within Lumiera, there is a fixed convention how these frame intervals are to be defined (&rArr; [[time handling RfC|http://staging.lumiera.org/documentation/devel/rfc/TimeHandling.html]]) +Within Lumiera, there is a fixed convention how these frame intervals are to be defined (&rArr; [[time handling RfC|http://lumiera.org/documentation/devel/rfc/TimeHandling.html]]) [img[Lumiera's frame quantisation convention|draw/FramePositions1.png]] Especially, this convention is agnostic of the actual zero-point of the scale and allows direct length calculations and seamless sequences of intervals. The //nominal coordinate// of an interval is also the starting point -- for automation keys frames we'll utilise special provisions. !range limitation problems -because times are represented as 64bit integers, the time points addressable within a given scale grid can be limited, compared with time points addressable through raw (internal) time values. As an extreme example, consider a time scale with origin at Time::MIN -- such a scale is unable to represent any of the original scale's value above zero, because the resulting coordinates would exceed the range of the 64bit integer. Did I mention that 64bit micro ticks can represent about 300000 years? +because times are represented as 64bit integers, the time points addressable within a given scale grid can be limited, compared with time points addressable through raw (internal) time values. As an extreme example, consider a time scale with origin at {{{Time::MAX}}} -- such a scale is unable to represent any of the original scale's value above zero, because the resulting coordinates would exceed the range of the 64bit integer. Did I mention that 64bit micro ticks can represent about 300000 years? Now the actual problem is that using 64bit integers already means pushing to the limit. There is no easy escape hatch, like using a larger scale data type for intermediaries -- it //is// the largest built-in type. Basically we're touching the topic of ''safe integer arithmetics'' here, which is frequently discussed as a security concern. The situation is as follows: * every programmer promises "I'll do the checks when necessary" -- just to forget doing so in practice then. @@ -6618,7 +6618,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]] </pre> </div> -<div title="TimeMutation" modifier="Ichthyostega" modified="201104250335" created="201101231344" tags="spec discuss draft" changecount="34"> +<div title="TimeMutation" modifier="Ichthyostega" modified="201104252343" created="201101231344" tags="spec discuss draft" changecount="38"> <pre>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 @@ -6661,18 +6661,18 @@ A mutation or an increment gets applied to a grid-aligned variable, which then ( This results in the following combinations to be implemented: | !mutation|!receiver ||!result | | ~TimeValue|Time || -- | -|~|Duration || -- | +|~|Duration || ↯ | |~|~TimeSpan ||set start | |~|quantised ||set raw value | | Duration|~TimeSpan ||set length | |~|Duration ||set length | -|~|quantised || -- | +|~|quantised || ↯ | | Offset|Time || -- | |~|Duration ||adjust length | |~|~TimeSpan ||adjust start | |~|quantised ||adjust raw value | -| ~QuTime|any raw ||treat as raw ~TimeValue | -|~|~QuTime ||materialise, then set raw | +| ~QuTime|any raw ||materialise, then set raw | +|~|~QuTime ||same &rarr; chanined | | increment|~QuTime ||snap to grid point | As rationale, consider the following * immutable values are bliss. @@ -6764,7 +6764,7 @@ At the level of individual timecode formats, we're lacking a common denominator; &rarr; Quantiser [[implementation details|QuantiserImpl]] </pre> </div> -<div title="TimeUsage" modifier="Ichthyostega" modified="201101231331" created="201012230204" tags="design discuss" changecount="22"> +<div title="TimeUsage" modifier="Ichthyostega" modified="201104260244" created="201012230204" tags="design discuss" changecount="25"> <pre>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 @@ -6801,12 +6801,15 @@ We should note, that the problems regarding quantised durations also carry over Thus, an especially important instance of that problem is ''how to deal with updates in a quantised environment''. If we handle quantisation stictly as a view employed on output, we run into the problems with accumulating spurious information. On the other hand, allowing for quantised changes inevitably pulls in all the complexity of mixing quantised and non-quantised values. It would be desirable somehow to move these distinctions out of the scope of this design and offload them onto the client (code using these time classes). -Another closely related problem is ''when to allow mutations'', if at all. We can't completely do away with mutations, simply because we don't have a pure functional language at our disposal. The whole concept of //reference semantics// doesn't play so well with immutable objects. The Lumiera high-level (session) model certainly relies on objects intended to be //manipulated.// Thus we need a re-settable length field in {{{MObject}}} and we need a time variable for position calculations. Yet we could make any //derived objects// into immutable descriptor records, which certainly helps with parallelism. +Another closely related problem is ''when to allow [[mutations|TimeMutation]]'', if at all. We can't completely do away with mutations, simply because we don't have a pure functional language at our disposal. The whole concept of //reference semantics// doesn't play so well with immutable objects. The Lumiera high-level (session) model certainly relies on objects intended to be //manipulated.// Thus we need a re-settable length field in {{{MObject}}} and we need a time variable for position calculations. Yet we could make any //derived objects// into immutable descriptor records, which certainly helps with parallelism. -The ''problem with playback position'' is -- that 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 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. +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. 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 +The way this time value and quantisation handling is designed in Lumiera creates a typical usage path, which actually is a one-way route. We might start out with a textual representation according to a specific ''timecode'' format. This timecode string may be parsed, and typically it will be ''materialised'', by invoking the {{{value()}}}-function. This brings us (back) to the very origin, which is a raw timevalue (''internal time'' value). Now, this time value might be manipulated, compared to other values, combined into a ''time span'' (time point and duration -- the most basic notion of an //object// to be manipulated in the Session). Anyway, at some point these time values need to be related to some ''time scale'' again, leading to ''quantised'' time values, which -- finally -- can be cast into a timecode format for external representation again, thus closing the circle. + !substantial problems to be solved * how to [[align multiple grids|TimeGridAlignment]] * how to integrate [[modifications of quantised values|TimeMutation]]. @@ -6814,7 +6817,7 @@ Note that the ''display window might be treated as just an independent instance * how to design the relation of Timecode, Timecode formatting and Quantisation &rarr; [[more here|TimecodeFormat]] </pre> </div> -<div title="TimecodeFormat" modifier="Ichthyostega" modified="201101220135" created="201101100308" tags="Player spec discuss draft" changecount="25"> +<div title="TimecodeFormat" modifier="Ichthyostega" modified="201104260233" created="201101100308" tags="Player spec discuss draft" changecount="26"> <pre>The handling of [[Timecode]] is closely related to [[time representation and quantisation|TimeQuant]]. In fact, these two topics blend into one another. Time will be quantised into a //grid,// but this grid only makes sense when linked to a externally relevant meaning and representation, which is the Timecode. But a timecode value also denotes a specific point in time -- performing operations on a timecode is equivalent to manipulating a quantised time value. !Problem of dependencies @@ -6860,7 +6863,7 @@ But because for Lumiera the SMPTE format is just a presentation rule applied to Because this decision is considered arbitrary and any reasoning will be context dependant, the decision is to provide a hook for a //strategy// -- Currently (1/11), the strategy is implemented according to (1) and (4) above, leaving the actual configuration of a strategy for later. !!{{red{WIP 1/11}}}BUG -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 +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??}}} </pre> </div> <div title="Timeline" modifier="Ichthyostega" modified="201011220126" created="200706250721" tags="def" changecount="21"> From af1561068eaca02ba2be813c1f151f2ed98fd02e Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 27 Apr 2011 02:25:36 +0200 Subject: [PATCH 059/496] definitions and stubs to make it compile --- src/lib/time/mutation.cpp | 62 +++++++++++++++++++++++++++ src/lib/time/mutation.hpp | 5 ++- src/lib/time/quantiser.cpp | 11 +++++ src/lib/time/quantiser.hpp | 2 + tests/lib/time/time-mutation-test.cpp | 4 +- 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/lib/time/mutation.cpp b/src/lib/time/mutation.cpp index 6555c22b4..6e0082f6e 100644 --- a/src/lib/time/mutation.cpp +++ b/src/lib/time/mutation.cpp @@ -24,6 +24,7 @@ #include "lib/error.hpp" #include "lib/time/timevalue.hpp" #include "lib/time/timequant.hpp" +#include "lib/time/timecode.hpp" #include "lib/time/mutation.hpp" //#include "lib/time.h" //#include "lib/util.hpp" @@ -183,6 +184,23 @@ namespace time { }; + /** + * concrete time value mutation: + * make the grid aligned time value explicit, + * and impose the resulting value to the given + * time points (or start points). + */ + class MaterialiseIntoTarget + : public SetNewStartTimeMutation + { + public: + explicit + MaterialiseIntoTarget (QuTime const& quant) + : SetNewStartTimeMutation (Secs(quant).getTime()) + { } + }; + + /** Convenience factory to yield a simple Mutation changing the absolute start time. * This whole procedure might look quite inefficient, but actually most of the @@ -221,6 +239,28 @@ namespace time { } + /** Convenience factory: materialise the given quantised time into an explicit fixed + * internal time value, according to the underlying time grid; impose the resulting + * value then as new time point or start point to the target + * @note same as materialising and then invoking #changeTime + */ + EncapsulatedMutation + Mutation::materialise (QuTime const& gridAlignedTime) + { + return EncapsulatedMutation::build<MaterialiseIntoTarget> (gridAlignedTime); + } + + + /** build a time mutation to \em nudge the target time value by an offset, + * defined as number of steps on an implicit nudge grid. If the target is an + * continuous (not quantised) time value or duration, an internal 'default nudge grid' + * will be used to calculate the offset value. Typically, this grid counts in seconds. + * To the contrary, when the target is a quantised value, it will be aligned to the + * grid point relative to the current value's next grid point, measured in number + * of steps. This includes \em materialising the internal to the exact grid + * position. If especially the adjustment is zero, the internal value will + * be changed to literally equal the current value's next grid point. + */ EncapsulatedMutation Mutation::nudge (int adjustment) { @@ -228,6 +268,28 @@ namespace time { } + /** build a time mutation to \em nudge the target time value; the nudge time grid + * is specified explicitly here, instead of using a global or 'natural' nudge grid. + * In case the target itself is a quantised time value, a chaining of the two + * grids will happen: first, the nudge grid is used to get an offset value, + * according to the number of steps, then this offset is applied to the + * raw value underlying the quantised target. If this resulting target + * value later will be cast into any kind of time code or materialised + * otherwise, the quantised value's own grid will apply as well, + * resulting in the net result of two quantisation operations + * being applied in sequence. + * @param adjustment number of grid steps to apply as offset + * @param gridID symbolic name used to register or define a + * suitable nudge grid, typically somewhere globally + * in the session (as meta asset) + */ + EncapsulatedMutation + Mutation::nudge (int adjustment, Symbol gridID) + { + UNIMPLEMENTED ("Nudge by a predefined nudge amount"); + } + + }} // lib::time diff --git a/src/lib/time/mutation.hpp b/src/lib/time/mutation.hpp index 13c1b6bca..b05fcaacc 100644 --- a/src/lib/time/mutation.hpp +++ b/src/lib/time/mutation.hpp @@ -56,7 +56,7 @@ #include "lib/error.hpp" #include "lib/time/timevalue.hpp" #include "lib/polymorphic-value.hpp" -//#include "lib/symbol.hpp" +#include "lib/symbol.hpp" #include <boost/noncopyable.hpp> //#include <iostream> @@ -67,6 +67,7 @@ namespace lib { namespace time { + using lib::Symbol; //using std::string; //using lib::Literal; @@ -106,7 +107,9 @@ namespace time { static EncapsulatedMutation changeTime (Time); static EncapsulatedMutation changeDuration (Duration); static EncapsulatedMutation adjust (Offset); + static EncapsulatedMutation materialise (QuTime const&); static EncapsulatedMutation nudge (int adjustment); + static EncapsulatedMutation nudge (int adjustment, Symbol gridID); protected: static void imposeChange (TimeValue&, TimeValue const&); diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index 9c4eca790..6ae061587 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -101,6 +101,17 @@ namespace time { + /** Access an existing grid definition or quantiser, known by the given symbolic ID. + * Typically this fetches a meta::TimeGrid (asset) from the session. + * @throw error::Logic if the given gridID wasn't registered + * @return smart-ptr to the quantiser instance */ + PQuant + Quantiser::retrieve (Symbol gridID) + { + return retrieveQuantiser (gridID); + } + + /** alignment to a simple fixed size grid. * The actual calculation first determines the number * of the grid interval containing the given rawTime, diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index 369c48055..a8efad7a1 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -92,6 +92,8 @@ namespace time { return supportedFormats_.check<FMT>(); } + static PQuant retrieve (Symbol gridID); + //------Grid-API---------------------------------------------- virtual long gridPoint (TimeValue const& raw) const =0; diff --git a/tests/lib/time/time-mutation-test.cpp b/tests/lib/time/time-mutation-test.cpp index e1d461334..e54b32655 100644 --- a/tests/lib/time/time-mutation-test.cpp +++ b/tests/lib/time/time-mutation-test.cpp @@ -246,10 +246,10 @@ namespace test{ CHECK (t.dur == t.var + change * FrameRate::PAL.duration()); // ....this time the change was measured in grid units, // taken relative to the origin of the specified grid - PGrid testGrid = TimeGrid::retrieve("test_grid"); + PQuant testGrid = Quantiser::retrieve("test_grid"); Offset distance (testGrid->timeOf(0), testGrid->timeOf(change)); CHECK (distance == change * FrameRate::PAL.duration()); - CHECK (Time(t.dur) - t.var == distance); + CHECK (t.dur - t.var == distance); From 11137f76342cee6b7958de6bfd8e371c534a8649 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 28 Apr 2011 03:21:00 +0200 Subject: [PATCH 060/496] provide implementation of the nudge-Mutation --- src/lib/time/mutation.cpp | 87 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/src/lib/time/mutation.cpp b/src/lib/time/mutation.cpp index 6e0082f6e..e98bafb33 100644 --- a/src/lib/time/mutation.cpp +++ b/src/lib/time/mutation.cpp @@ -27,13 +27,14 @@ #include "lib/time/timecode.hpp" #include "lib/time/mutation.hpp" //#include "lib/time.h" -//#include "lib/util.hpp" +#include "lib/util.hpp" //#include <tr1/functional> //using std::string; //using util::unConst; //using util::floorwrap; +using util::isnil; namespace error = lumiera::error; @@ -201,6 +202,84 @@ namespace time { }; + /** + * concrete time value mutation: + * nudge target value by the given number of 'steps', + * relative to either the given grid. + */ + class NudgeMutation + : public ImposeOffsetMutation + { + + static Offset + materialiseGridPoint (Symbol gridID, int steps) + { + REQUIRE (!isnil (gridID)); + PQuant grid = Quantiser::retrieve(gridID); + return Offset(grid->timeOf(0), grid->timeOf(steps)); + } + + public: + NudgeMutation (int relativeSteps, Symbol gridID) + : ImposeOffsetMutation(materialiseGridPoint (gridID,relativeSteps)) + { } + }; + + + /** + * concrete time value mutation: + * nudge based on a implicit grid, which is + * either a quantised target value's own grid, + * or a \em natural grid. + * @note currently the natural grid is hard wired, + * just interpreting the step parameter as + * offset in seconds. + */ + class NaturalNudgeMutation + : public ClonableMutation + { + int steps_; + + virtual void + change (Duration& target) const + { + imposeChange (target, TimeVar(target)+=Time(FSecs(steps_))); + } + + + virtual void + change (TimeSpan& target) const + { + imposeChange (target, TimeVar(target)+=Time(FSecs(steps_))); + } + + + /** Special treatment: use the quantised time's own grid; + * retrieve the corresponding grid point, offset it by the step-parameter, + * then retrieve the corresponding time from the quantised time's + * underlying quantiser (grid). + * @note when the #steps_ parameter is zero, what happens here effectively + * is the materialisation of the quantised target time, i.e. making + * the quantisation explicit and storing the resulting value. */ + virtual void + change (QuTime& target) const + { + long gridPoint = FrameNr(target)+steps_; + imposeChange (target, PQuant(target)->timeOf(gridPoint)); + } + + + public: + explicit + NaturalNudgeMutation (int relativeSteps) + : steps_(relativeSteps) + { } + }; + + + + + /** Convenience factory to yield a simple Mutation changing the absolute start time. * This whole procedure might look quite inefficient, but actually most of the @@ -257,14 +336,14 @@ namespace time { * will be used to calculate the offset value. Typically, this grid counts in seconds. * To the contrary, when the target is a quantised value, it will be aligned to the * grid point relative to the current value's next grid point, measured in number - * of steps. This includes \em materialising the internal to the exact grid + * of steps. This includes \em materialising the internal time to the exact grid * position. If especially the adjustment is zero, the internal value will * be changed to literally equal the current value's next grid point. */ EncapsulatedMutation Mutation::nudge (int adjustment) { - UNIMPLEMENTED ("Nudge by a predefined nudge amount"); + return EncapsulatedMutation::build<NaturalNudgeMutation> (adjustment); } @@ -286,7 +365,7 @@ namespace time { EncapsulatedMutation Mutation::nudge (int adjustment, Symbol gridID) { - UNIMPLEMENTED ("Nudge by a predefined nudge amount"); + return EncapsulatedMutation::build<NudgeMutation> (adjustment, gridID); } From 80f7dba3341b6e3c2af8ec5b7c8f54c3644e80b9 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 29 Apr 2011 04:04:48 +0200 Subject: [PATCH 061/496] WIP some comments and considerations --- src/lib/time/formats.hpp | 2 +- src/lib/time/timecode.cpp | 4 ++-- src/lib/time/timecode.hpp | 26 ++++++++++++++++++++++++-- tests/lib/time/time-formats-test.cpp | 3 +++ tests/lib/time/time-mutation-test.cpp | 2 +- 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index aae1226a5..c96496c22 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -84,7 +84,7 @@ namespace time { static void rebuild (SmpteTC&, QuantR, TimeValue const&); static TimeValue evaluate (SmpteTC const&, QuantR); static uint getFramerate (QuantR, TimeValue const&); - static void rangeLimitStrategy (SmpteTC&); + static void applyRangeLimitStrategy (SmpteTC&); }; diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 587293995..14b11fa10 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -131,7 +131,7 @@ namespace time { * orientation (0:0:0:0 - 1sec = -1:59:59:0) */ void - Smpte::rangeLimitStrategy (SmpteTC& tc) + Smpte::applyRangeLimitStrategy (SmpteTC& tc) { if (tc.hours < 0) tc.invertOrientation(); @@ -171,7 +171,7 @@ namespace time { wrapHours (SmpteTC* thisTC, int rawHours) { thisTC->hours.setValueRaw (rawHours); - format::Smpte::rangeLimitStrategy (*thisTC); + format::Smpte::applyRangeLimitStrategy (*thisTC); } diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index 3b5f320a8..dbaef3829 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -101,7 +101,29 @@ namespace time { /** + * Classical Timecode value reminiscent to SMPTE format. + * After quantisation, the resulting (materialised) time value is + * decimated into a hours, a minutes, a seconds part and the remainder + * is cast into a frame number relative to the seconds. Consequently, + * a SmpteTC representation is always linked implicitly to a specific framerate. * + * \par range extensions + * Historically, SMPTE timecode format was focused mainly on how to encode a + * unique timestamp in a way allowing to 'piggyback' these timestamps into an + * existing (analogue) media data format. As a side effect, quite tight limits + * had to be imposed on the possible component values in such a fixed-length format. + * This whole concern is completely beyond the scope of a typical computer based video + * implementation; thus we can consider ways to extend the value range to be represented + * in this SMPTE-like timecode format: + * - we can allow time values below the zero point + * - we can allow time values beyond 24 hours. + * Several different schemes how to do this extensions could be devised (and in addition, + * we could also wrap around the hours field, jumping from 23:59:59:## to 0:0:0:0). + * Currently, we implement an extension, where the timecode representation is symmetrical + * to the zero point and the hours field is just extended beyond 23 hours. To give an + * example: \c 0:0:0:0 minus 1 frame yields \c -0:0:0:1 + * + * @todo the range extension scheme could be a configurable strategy */ class SmpteTC : public TCode @@ -141,7 +163,7 @@ namespace time { /** - * + * @warning missing implementation */ class HmsTC : public TCode @@ -166,7 +188,7 @@ namespace time { /** - * + * @warning partially missing implementation */ class Secs : public TCode diff --git a/tests/lib/time/time-formats-test.cpp b/tests/lib/time/time-formats-test.cpp index eab218d58..fad531de4 100644 --- a/tests/lib/time/time-formats-test.cpp +++ b/tests/lib/time/time-formats-test.cpp @@ -74,6 +74,9 @@ namespace test{ checkTimecodeUsageCycle () { UNIMPLEMENTED ("full usage cycle for a timecode value"); + + /////////TODO: parse-funktion, wie? + /////////TODO: vieleicht doch irgendwo eine Funktion zum Materialisieren? } diff --git a/tests/lib/time/time-mutation-test.cpp b/tests/lib/time/time-mutation-test.cpp index e54b32655..c21a58a7b 100644 --- a/tests/lib/time/time-mutation-test.cpp +++ b/tests/lib/time/time-mutation-test.cpp @@ -216,7 +216,7 @@ namespace test{ // Here accidentally both the change and t.quant use the same grid. // For a more contrived example, we try to use a different grid... - TimeGrid::build("special_funny_grid", 1); // (1 frame per second, no offset) + TimeGrid::build("special_funny_grid", 1, Time(0,-10)); // (1 frame per second, zero point at -10s) QuTime funny (original, "special_funny_grid"); funny.accept (Mutation::materialise (change)); CHECK (funny == t.quant); // leading to the same raw value this far From ccd40c3fee25e73e01c83e37f17745846f1271e5 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 5 May 2011 19:05:48 +0200 Subject: [PATCH 062/496] better use a dedicated 'materialise' operation on the Quantiser --- src/lib/time/mutation.cpp | 8 +++++--- src/lib/time/quantiser.cpp | 23 ++++++++++++++++++++++- src/lib/time/quantiser.hpp | 1 + 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/lib/time/mutation.cpp b/src/lib/time/mutation.cpp index e98bafb33..cd2b205c3 100644 --- a/src/lib/time/mutation.cpp +++ b/src/lib/time/mutation.cpp @@ -197,7 +197,7 @@ namespace time { public: explicit MaterialiseIntoTarget (QuTime const& quant) - : SetNewStartTimeMutation (Secs(quant).getTime()) + : SetNewStartTimeMutation (PQuant(quant)->materialise(quant)) { } }; @@ -264,8 +264,10 @@ namespace time { virtual void change (QuTime& target) const { - long gridPoint = FrameNr(target)+steps_; - imposeChange (target, PQuant(target)->timeOf(gridPoint)); + PQuant const& grid (target); + int64_t originalGridPoint = grid->gridPoint(target); + int64_t adjustedGridPoint = originalGridPoint + steps_; + imposeChange (target, grid->timeOf (adjustedGridPoint)); } diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index 6ae061587..6a43b014a 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -47,7 +47,7 @@ namespace time { advice::Request<PQuant> query(gridID); PQuant grid_found = query.getAdvice(); if (!grid_found) - throw error::Logic ("unable to fetch the quantisation grid -- is it already defined?" + throw error::Logic ("unable to fetch the quantisation grid -- was it already defined?" ////////TICKET #197 , LUMIERA_ERROR_UNKNOWN_GRID); return grid_found; } @@ -112,6 +112,27 @@ namespace time { } + + /** convenience shortcut: \em materialise a raw time value + * based on this grid or time axis, but returning a raw time value. + * Implemented as combination of the #gridAlign and #timeOf operations, + * i.e. we quantise into this scale, but transform the result back onto + * the raw time value scale. + * @warning this operation incurs information loss. Values may be rounded + * and / or clipped, according to the grid used. And, contrary to + * a QuTime value, the information about the actual grid is + * discarded. Please don't use this operation if you just + * "want a number" but feel to too lazy to understand + * properly what quantisation means! + */ + TimeValue + Quantiser::materialise (TimeValue const& raw) const + { + return timeOf (gridPoint (raw)); + } + + + /** alignment to a simple fixed size grid. * The actual calculation first determines the number * of the grid interval containing the given rawTime, diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index a8efad7a1..33c4e9d3f 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -93,6 +93,7 @@ namespace time { } static PQuant retrieve (Symbol gridID); + TimeValue materialise (TimeValue const& raw) const; //------Grid-API---------------------------------------------- From 10215948a55f000442856642aecd325ba0cacd61 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 5 May 2011 19:11:07 +0200 Subject: [PATCH 063/496] Fix another 64/32bit loss-of-precision problem --- src/lib/time/grid.hpp | 4 ++-- src/lib/time/quantiser.cpp | 4 ++-- src/lib/time/quantiser.hpp | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/time/grid.hpp b/src/lib/time/grid.hpp index 4053fa5b0..7ace4a12e 100644 --- a/src/lib/time/grid.hpp +++ b/src/lib/time/grid.hpp @@ -62,9 +62,9 @@ namespace time { virtual ~Grid(); ///< this is an Interface - virtual long gridPoint (TimeValue const& raw) const =0; + virtual int64_t gridPoint (TimeValue const& raw) const =0; virtual TimeValue gridAlign (TimeValue const& raw) const =0; - virtual TimeValue timeOf (long gridPoint) const =0; + virtual TimeValue timeOf (int64_t gridPoint) const =0; virtual TimeValue timeOf (FSecs, int =0) const =0; }; diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index 6a43b014a..fcb916004 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -160,7 +160,7 @@ namespace time { * range when converting back into a TimeValue. * @see #lumiera_quantise_frames */ - long + int64_t FixedFrameQuantiser::gridPoint (TimeValue const& rawTime) const { return lumiera_quantise_frames (_raw(rawTime), _raw(origin_), _raw(raster_)); @@ -173,7 +173,7 @@ namespace time { * valid range of lumiera::Time */ TimeValue - FixedFrameQuantiser::timeOf (long gridPoint) const + FixedFrameQuantiser::timeOf (int64_t gridPoint) const { return TimeValue (lumiera_time_of_gridpoint (gridPoint, _raw(origin_), _raw(raster_))); } diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index 33c4e9d3f..06df4d3f1 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -97,9 +97,9 @@ namespace time { //------Grid-API---------------------------------------------- - virtual long gridPoint (TimeValue const& raw) const =0; + virtual int64_t gridPoint (TimeValue const& raw) const =0; virtual TimeValue gridAlign (TimeValue const& raw) const =0; - virtual TimeValue timeOf (long gridPoint) const =0; + virtual TimeValue timeOf (int64_t gridPoint) const =0; virtual TimeValue timeOf (FSecs, int =0) const =0; }; @@ -126,9 +126,9 @@ namespace time { FixedFrameQuantiser (FrameRate const& frames_per_second, TimeValue referencePoint =TimeValue(0)); FixedFrameQuantiser (Duration const& frame_duration, TimeValue referencePoint =TimeValue(0)); - long gridPoint (TimeValue const&) const; + int64_t gridPoint (TimeValue const&) const; TimeValue gridAlign (TimeValue const&) const; - TimeValue timeOf (long gridPoint) const; + TimeValue timeOf (int64_t gridPoint) const; TimeValue timeOf (FSecs, int =0) const; }; From 3139fb7f1ebda5f00e5bb2a0d42dc3df77470e5d Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 13 May 2011 04:04:02 +0200 Subject: [PATCH 064/496] more 64/32bit fun --- src/lib/time.cpp | 24 ++++++++++++++++-------- src/lib/time.h | 20 +++++++++++++++++++- src/lib/time/timecode.cpp | 16 ++++++++-------- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/lib/time.cpp b/src/lib/time.cpp index 7cbe53004..9a760ac4d 100644 --- a/src/lib/time.cpp +++ b/src/lib/time.cpp @@ -81,6 +81,14 @@ lumiera_rational_to_time (FSecs const& fractionalSeconds) return rational_cast<gavl_time_t> (GAVL_TIME_SCALE * fractionalSeconds); } +gavl_time_t +lumiera_framecount_to_time (uint64_t frameCount, FrameRate const& fps) +{ + // convert to 64bit + boost::rational<uint64_t> framerate (fps.numerator(), fps.denominator()); + + return rational_cast<gavl_time_t> (GAVL_TIME_SCALE * frameCount / framerate); +} gavl_time_t lumiera_frame_duration (FrameRate const& fps) @@ -122,7 +130,7 @@ lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) } gavl_time_t -lumiera_time_of_gridpoint (long nr, gavl_time_t origin, gavl_time_t grid) +lumiera_time_of_gridpoint (int64_t nr, gavl_time_t origin, gavl_time_t grid) { gavl_time_t offset = nr * grid; return origin + offset; @@ -154,14 +162,14 @@ lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours gavl_time_t lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours) { - int total_mins = 60 * hours + mins; - int total_frames = 108000 * hours - + 1800 * mins - + 30 * secs - + frames - - 2 * (total_mins - total_mins / 10); + uint64_t total_mins = 60 * hours + mins; + uint64_t total_frames = 108000 * hours + + 1800 * mins + + 30 * secs + + frames + - 2 * (total_mins - total_mins / 10); - return (total_frames / 29.97f) * 1000 * GAVL_TIME_SCALE_MS; + return lumiera_framecount_to_time (total_frames, FrameRate::NTSC); } int diff --git a/src/lib/time.h b/src/lib/time.h index fb1a7992a..c017f6713 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -69,6 +69,16 @@ gavl_time_t lumiera_rational_to_time (lib::time::FSecs const& fractionalSeconds); +/** + * Converts a frame count into Lumiera's internal time scale. + * based on a framerate given as rational number (e.g. NTSC) + * @note handles only positive frame counts and assumes the + * origin to be at zero. + */ +gavl_time_t +lumiera_framecount_to_time (uint64_t frameCount, lib::time::FrameRate const& fps); + + /** * Calculates the duration of one frame in Lumiera time units. * @param framerate underlying framerate as rational number @@ -125,7 +135,7 @@ lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid); * @return time point (frame start) on the Lumiera internal time scale */ gavl_time_t -lumiera_time_of_gridpoint (long nr, gavl_time_t origin, gavl_time_t grid); +lumiera_time_of_gridpoint (int64_t nr, gavl_time_t origin, gavl_time_t grid); /** * Builds a time value by summing up the given components. @@ -140,6 +150,11 @@ lumiera_build_time (long millis, uint secs, uint mins, uint hours); /** * Builds a time value by summing up the given components. * @todo replace float framerates by lib::time::FrameRate + * @deprecated this function doesn't fit in the general time handling concept. + * SMPTE is a timecode format, and should be distinguished from a + * raw time value. For any timecode format, we always require an explicit + * 'time grid' (coordinate system), which also defines a zero point. + * Usually, this time grid is managed and provided by the session */ gavl_time_t lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours); @@ -183,6 +198,9 @@ lumiera_time_frames (gavl_time_t time, float fps); /** * Extract the frame count for the given time, using the given fps. * @todo use the rational lib::time::FrameRate instead of a float + * @deprecated should be moved down into the implementation; + * this function doesn't fit into the general time handling, + * because it doesn't link to a time grid (coordinate system) */ int lumiera_time_frame_count (gavl_time_t time, float fps); diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 14b11fa10..6ec229ae6 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -79,11 +79,11 @@ namespace time { TimeValue Smpte::evaluate (SmpteTC const& tc, QuantR quantiser) { - long frameRate = tc.getFps(); - long gridPoint = tc.frames - + tc.secs * frameRate - + tc.mins * frameRate * 60 - + tc.hours * frameRate * 60 * 60; + uint frameRate = tc.getFps(); + int64_t gridPoint(tc.frames); + gridPoint += int64_t(tc.secs) * frameRate; + gridPoint += int64_t(tc.mins) * frameRate * 60; + gridPoint += int64_t(tc.hours) * frameRate * 60 * 60; return quantiser.timeOf (tc.sgn * gridPoint); } @@ -103,9 +103,9 @@ namespace time { uint Smpte::getFramerate (QuantR quantiser_, TimeValue const& rawTime) { - long refCnt = quantiser_.gridPoint(rawTime); - long newCnt = quantiser_.gridPoint(Time(0.5,1) + rawTime); - long effectiveFrames = newCnt - refCnt; + int64_t refCnt = quantiser_.gridPoint(rawTime); + int64_t newCnt = quantiser_.gridPoint(Time(0,1) + rawTime); + int64_t effectiveFrames = newCnt - refCnt; ENSURE (1000 > effectiveFrames); ENSURE (0 < effectiveFrames); return uint(effectiveFrames); From 6b1b6cb805a306a76d8f66fda2350edec3c526ff Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 13 May 2011 06:28:55 +0200 Subject: [PATCH 065/496] draft how to integrate parsing of timecode formats --- src/lib/query.cpp | 3 +- src/lib/time/formats.hpp | 8 +++ src/lib/time/timecode.cpp | 103 ++++++++++++++++++++++++++- src/lib/time/timecode.hpp | 21 ++++-- src/lib/util.hpp | 2 +- tests/lib/time/time-formats-test.cpp | 2 +- wiki/renderengine.html | 8 +-- 7 files changed, 132 insertions(+), 15 deletions(-) diff --git a/src/lib/query.cpp b/src/lib/query.cpp index e6ff8e918..5683a0ac7 100644 --- a/src/lib/query.cpp +++ b/src/lib/query.cpp @@ -23,6 +23,7 @@ #include "lib/query.hpp" #include "lib/util.hpp" +#include "lib/symbol.hpp" #include "include/logging.h" #include <boost/algorithm/string.hpp> @@ -74,7 +75,7 @@ namespace lumiera { map<Symbol, regex> regexTable; - Symbol matchArgument = "\\(\\s*([\\w_\\.\\-]+)\\s*\\),?\\s*"; + Literal matchArgument = "\\(\\s*([\\w_\\.\\-]+)\\s*\\),?\\s*"; regex findPredicate (string("(\\w+)")+matchArgument); inline regex& diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index c96496c22..a2af01c6f 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -31,6 +31,7 @@ #include "lib/typed-counter.hpp" #include <tr1/memory> +#include <string> #include <bitset> @@ -53,6 +54,9 @@ namespace time { namespace format { + LUMIERA_ERROR_DECLARE (INVALID_TIMECODE); ///< timecode format error, illegal value encountered. + + using std::string; using lib::meta::NoInstance; // the following types are for metaprogramming only... @@ -67,6 +71,7 @@ namespace time { struct Frames : NoInstance<Frames> { + static TimeValue parse (string const&, QuantR); static void rebuild (FrameNr&, QuantR, TimeValue const&); static TimeValue evaluate (FrameNr const&, QuantR); }; @@ -81,6 +86,7 @@ namespace time { struct Smpte : NoInstance<Smpte> { + static TimeValue parse (string const&, QuantR); static void rebuild (SmpteTC&, QuantR, TimeValue const&); static TimeValue evaluate (SmpteTC const&, QuantR); static uint getFramerate (QuantR, TimeValue const&); @@ -98,6 +104,7 @@ namespace time { struct Hms : NoInstance<Hms> { + static TimeValue parse (string const&, QuantR); static void rebuild (HmsTC&, QuantR, TimeValue const&); static TimeValue evaluate (HmsTC const&, QuantR); }; @@ -115,6 +122,7 @@ namespace time { struct Seconds : NoInstance<Seconds> { + static TimeValue parse (string const&, QuantR); static void rebuild (Secs&, QuantR, TimeValue const&); static TimeValue evaluate (Secs const&, QuantR); }; diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 6ec229ae6..3f0331e7e 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -30,12 +30,19 @@ #include "lib/util.hpp" #include <tr1/functional> +#include <boost/regex.hpp> +#include <boost/lexical_cast.hpp> using std::string; using util::unConst; using util::isSameObject; using util::floorwrap; +using boost::regex; +using boost::smatch; +using boost::regex_search; +using boost::lexical_cast; +namespace error = lumiera::error; namespace lib { namespace time { @@ -43,7 +50,101 @@ namespace time { TCode::~TCode() { } // emit VTable here.... - namespace format { + + namespace format { /* ================= Timecode implementation details ======== */ + + LUMIERA_ERROR_DEFINE (INVALID_TIMECODE, "timecode format error, illegal value encountered"); + + + /** try to parse a frame number specification + * @param frameNumber string containing an integral number with trailing '#' + * @param frameGrid coordinate system (and thus framerate) to use for the conversion + * @return (opaque internal) lumiera time value of the given frame's start position + * @throw error::Invalid in case of parsing failure + * @note the string may contain any additional content, as long as a + * regular-expression search is able to pick out a suitable value + */ + TimeValue + Frames::parse (string const& frameNumber, QuantR frameGrid) + { + static regex frameNr_parser ("(-?\\d+)#"); + smatch match; + if (regex_search (frameNumber, match, frameNr_parser)) + return frameGrid.timeOf (lexical_cast<int64_t> (match[1])); + else + throw error::Invalid ("unable to parse framecount \""+frameNumber+"\"" + , LUMIERA_ERROR_INVALID_TIMECODE); + } + + + TimeValue + Smpte::parse (string const&, QuantR) + { + UNIMPLEMENTED("parsing SMPTE timecode"); + } + + + TimeValue + Hms::parse (string const&, QuantR) + { + UNIMPLEMENTED("parse a hours:mins:secs time specification"); + } + + + /** try to parse a time specification in seconds or fractional seconds. + * The value is interpreted relative to the origin of a the given time grid + * This parser recognises full seconds, fractional seconds and both together. + * In any case, the actual number is required to end with a trailing \c 'sec' + * @par Example specifications +\verbatim + 12sec --> 12 * GAVL_TIME_SCALE + -4sec --> -4 * GAVL_TIME_SCALE + 5/4sec --> 1.25 * GAVL_TIME_SCALE + -5/25sec --> -0.2 * GAVL_TIME_SCALE + 1+1/2sec --> 1.5 * GAVL_TIME_SCALE + 1-1/25sec --> 0.96 * GAVL_TIME_SCALE + -12-1/4sec --> -11.75 * GAVL_TIME_SCALE +\endverbatim + * @param seconds string containing a time spec in seconds + * @param grid coordinate system the parsed value is based on + * @return the corresponding (opaque internal) lumiera time value + * @throw error::Invalid in case of parsing failure + * @note the string may contain any additional content, as long as a + * regular-expression search is able to pick out a suitable value + */ + TimeValue + Seconds::parse (string const& seconds, QuantR grid) + { + static regex fracSecs_parser ("(-?\\d+)(?:([\\-\\+]\\d+)?/(\\d+))?sec"); + + #define SUB_EXPR(N) lexical_cast<long> (match[N]) + smatch match; + if (regex_search (seconds, match, fracSecs_parser)) + if (match[2].matched) + { + // complete spec with all parts + FSecs fractionalPart (SUB_EXPR(2), SUB_EXPR(3)); + long fullSeconds (SUB_EXPR(1)); + return grid.timeOf (fullSeconds + fractionalPart); + } + else + if (match[3].matched) + { + // only a fractional part was given + return grid.timeOf (FSecs (SUB_EXPR(1), SUB_EXPR(3))); + } + else + { + // just simple non-fractional seconds + return grid.timeOf (FSecs (SUB_EXPR(1))); + } + else + throw error::Invalid ("unable to parse \""+seconds+"\" as (fractional)seconds" + , LUMIERA_ERROR_INVALID_TIMECODE); + } + + + /** build up a frame count * by quantising the given time value diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index dbaef3829..412b14f3b 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -45,7 +45,7 @@ namespace time { /** * Interface: fixed format timecode specification. - * @see time::Format + * @see time::format * @todo WIP-WIP-WIP */ class TCode @@ -76,18 +76,25 @@ namespace time { /** * A frame counting timecode value. - * This is an hard-coded representation of - * TCode<format::Frames>, with additional convenience - * constructors and conversions, which basically make - * FrameNr values interchangeable with integral numbers. + * This is the hard-coded standard representation of + * format::Frames, and is defined such as to make FrameNr + * values interchangeable with integral numbers. + * Like any concrete TCode subclass, it can be created + * based on a QuTime value. This way, not only the (raw) TimeValue + * is provided, but also the (frame)-Grid to base the frame count on. + * But contrary to a QuTime value, a FrameNr value is \em materialised + * (rounded) into a definite integral number, stripping the excess + * precision contained in the original (raw) TimeValue. + * As framecount values are implemented as single display field for an + * integral value (time::Digxel), they allow for simple presentation. */ class FrameNr : public TCode , public CountVal { - string show() const { return string(CountVal::show())+"fr"; } - Literal tcID() const { return "Frame-count"; } + string show() const { return string(CountVal::show())+"#"; } + Literal tcID() const { return "Framecount"; } TimeValue value() const { return Format::evaluate (*this, *quantiser_); } public: diff --git a/src/lib/util.hpp b/src/lib/util.hpp index 3fc2cb97b..037881883 100644 --- a/src/lib/util.hpp +++ b/src/lib/util.hpp @@ -315,7 +315,7 @@ namespace util { * will start with an alphanumeric character. * * @par Example Conversions -\verbatim +\verbatim "Word" --> 'Word' "a Sentence" --> 'a_Sentence' "trailing Withespace \t \n" --> 'trailing_Withespace' diff --git a/tests/lib/time/time-formats-test.cpp b/tests/lib/time/time-formats-test.cpp index fad531de4..b9f6d4def 100644 --- a/tests/lib/time/time-formats-test.cpp +++ b/tests/lib/time/time-formats-test.cpp @@ -97,7 +97,7 @@ namespace test{ void checkSeconds () { - UNIMPLEMENTED ("verify seconds as timecode format"); + UNIMPLEMENTED ("verify fractional seconds as timecode format"); } diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 2cca48ddd..a8d999fa7 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6764,7 +6764,7 @@ At the level of individual timecode formats, we're lacking a common denominator; &rarr; Quantiser [[implementation details|QuantiserImpl]] </pre> </div> -<div title="TimeUsage" modifier="Ichthyostega" modified="201104260244" created="201012230204" tags="design discuss" changecount="25"> +<div title="TimeUsage" modifier="Ichthyostega" modified="201105122348" created="201012230204" tags="design discuss" changecount="27"> <pre>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 @@ -6808,7 +6808,7 @@ The ''problem with playback position'' is -- that indeed it's an attempt to conc 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 -The way this time value and quantisation handling is designed in Lumiera creates a typical usage path, which actually is a one-way route. We might start out with a textual representation according to a specific ''timecode'' format. This timecode string may be parsed, and typically it will be ''materialised'', by invoking the {{{value()}}}-function. This brings us (back) to the very origin, which is a raw timevalue (''internal time'' value). Now, this time value might be manipulated, compared to other values, combined into a ''time span'' (time point and duration -- the most basic notion of an //object// to be manipulated in the Session). Anyway, at some point these time values need to be related to some ''time scale'' again, leading to ''quantised'' time values, which -- finally -- can be cast into a timecode format for external representation again, thus closing the circle. +The way time value and quantisation handling is designed in Lumiera creates a typical usage path, which actually is a one-way route. We might start out with a textual representation according to a specific ''timecode'' format. Assumed we know the implicit underlying ''time grid'' (coordinate system, framerate), this timecode string may be parsed. This brings us (back) to the very origin, which is a raw ~TimeValue (''internal time'' value). Now, this time value might be manipulated, compared to other values, combined into a ''time span'' (time point and duration -- the most basic notion of an //object// to be manipulated in the Session). Anyway, at some point these time values need to be related to some ''time scale'' again, leading to ''quantised'' time values, which -- finally -- can be cast into a timecode format for external representation again, thus closing the circle. !substantial problems to be solved * how to [[align multiple grids|TimeGridAlignment]] @@ -7092,14 +7092,14 @@ For now, as of 6/10, we use specialised QueryResolver instances explicitly and d &rarr; QueryRegistration </pre> </div> -<div title="ViewerPlayConnection" modifier="Ichthyostega" modified="201011100202" created="201007110305" tags="Model Player spec draft" changecount="6"> +<div title="ViewerPlayConnection" modifier="Ichthyostega" modified="201105110009" created="201007110305" tags="Model Player spec draft" changecount="7"> <pre>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 cascaded behind 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. 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. </pre> </div> <div title="VirtualClip" modifier="Ichthyostega" modified="201011220152" created="200804110321" tags="def Model" changecount="17"> From c7bb7154a720071916737f5b4e2c4016d1a88cdc Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 13 May 2011 07:46:21 +0200 Subject: [PATCH 066/496] draft a test to cover parsing of timecode values --- src/lib/time/lumitime.cpp | 2 +- tests/40components.tests | 4 + tests/lib/time/time-parsing-test.cpp | 183 +++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 tests/lib/time/time-parsing-test.cpp diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index af3313772..bb1c0e1a0 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -58,7 +58,7 @@ namespace time { , uint hours ) : TimeValue(lumiera_build_time (millis,secs,mins,hours)) - { } + { } /** convenience constructor to build an Time value diff --git a/tests/40components.tests b/tests/40components.tests index 51b9c54bc..c8cd30aad 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -704,6 +704,10 @@ return: 0 END +PLANNED "Parsing time values" TimeParsing_test <<END +END + + TEST "Symbol_test" Symbol_test <<END out: one out: sizeof.+lib.Literal.+ = (4|8) diff --git a/tests/lib/time/time-parsing-test.cpp b/tests/lib/time/time-parsing-test.cpp new file mode 100644 index 000000000..53d5f33d8 --- /dev/null +++ b/tests/lib/time/time-parsing-test.cpp @@ -0,0 +1,183 @@ +/* + TimeParsing(Test) - handling textual time(code) specifications + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "proc/asset/meta/time-grid.hpp" +#include "lib/time/quantiser.hpp" +//#include "lib/time/timequant.hpp" +#include "lib/time/timecode.hpp" +//#include "lib/time/display.hpp" +#include "lib/symbol.hpp" +#include "lib/util.hpp" + +//#include <boost/lexical_cast.hpp> +//#include <iostream> +//#include <cstdlib> + +//using boost::lexical_cast; +//using util::isnil; +using lib::Symbol; +using util::cStr; +//using std::rand; +//using std::cout; +//using std::endl; + + +namespace lib { +namespace time{ +namespace test{ + + using asset::meta::TimeGrid; + using format::LUMIERA_ERROR_INVALID_TIMECODE; + + namespace { // Helper for writing test cases + + Symbol DEFAULT_GRID = "pal0"; + Symbol OFFSET_GRID = "pal10"; + + /** + * Expression builder + * for writing time value parsing tests + */ + template<class FMT> + class Parsing + { + string const& timeSpec_; + PQuant grid_; + + public: + Parsing (string const& toParse, Symbol gridID =DEFAULT_GRID) + : timeSpec_(toParse) + , grid_(Quantiser::retrieve(gridID)) + { } + + + void + should_yield (TimeValue const& expected) + { + TimeValue parsed = FMT::parse (timeSpec_, *grid_); + CHECK (parsed == expected, "parsing '%s' resulted in %s instead of %s" + , cStr(timeSpec_) + , cStr(Time(parsed)) + , cStr(Time(expected))); + } + + void + should_yield (FSecs const& expectedSecs) + { + should_yield (Time (expectedSecs)); + } + + void + should_fail () + { + VERIFY_ERROR (INVALID_TIMECODE, FMT::parse (timeSpec_, *grid_)); + } + + }; + + }//(End)Test case helper + + + + + /******************************************************** + * @test parse textual time specifications given + * in the supported timecode formats + */ + class TimeParsing_test : public Test + { + virtual void + run (Arg) + { + TimeGrid::build("pal0", FrameRate::PAL); + TimeGrid::build("pal10", FrameRate::PAL, Time(0,10)); + + parseFrames(); + parseFractionalSeconds(); +// parseHms(); +// parseSmpte(); +// parseDropFrame(); + } + + + void + parseFrames () + { + Parsing<format::Frames> ("0#") .should_yield (0); + Parsing<format::Frames> ("1#") .should_yield (FSecs(1,25) ); + Parsing<format::Frames> ("-1#") .should_yield (FSecs(-1,25) ); + Parsing<format::Frames> ("-0#") .should_yield (0); + Parsing<format::Frames> ("25#") .should_yield (1 ); + Parsing<format::Frames> ("26#") .should_yield (Time(40,1) ); + Parsing<format::Frames> ("25#", OFFSET_GRID).should_yield (1+10 ); + Parsing<format::Frames> ("-1#", OFFSET_GRID).should_yield (10 - FSecs(1,25)); + + Parsing<format::Frames> ("23") .should_fail(); + Parsing<format::Frames> ("23 #") .should_fail(); + Parsing<format::Frames> ("23.#") .should_fail(); + Parsing<format::Frames> ("23x#") .should_fail(); + + Parsing<format::Frames> ("xxx25#xxx") .should_yield (1); + Parsing<format::Frames> ("12 25#") .should_yield (1); + Parsing<format::Frames> ("12 25# 33#") .should_yield (1); + Parsing<format::Frames> ("12\n 25# \n 33#") .should_yield (1); + Parsing<format::Frames> ("12.25#") .should_yield (1); + } + + + void + parseFractionalSeconds () + { + UNIMPLEMENTED ("verify reading fractional seconds as timecode format"); + } + + + void + parseHms () + { + UNIMPLEMENTED ("verify reading hour-minutes-seconds-millis time specs"); + } + + + void + parseSmpte () + { + } + + + void + parseDropFrame () + { + UNIMPLEMENTED ("verify especially SMPTE-drop-frame timecode"); + } + }; + + + /** Register this test class... */ + LAUNCHER (TimeParsing_test, "unit common"); + + + +}}} // namespace lib::time::test From f85c86d6c81613dcf84bd3fff41df67a4e22e61b Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 14 May 2011 04:17:22 +0200 Subject: [PATCH 067/496] parsing fractional seconds: unit test pass --- src/lib/time/timecode.cpp | 5 ++- tests/40components.tests | 2 +- tests/lib/time/time-parsing-test.cpp | 64 +++++++++++++++++++++------- 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 3f0331e7e..4cce6fd66 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -67,7 +67,7 @@ namespace time { TimeValue Frames::parse (string const& frameNumber, QuantR frameGrid) { - static regex frameNr_parser ("(-?\\d+)#"); + static regex frameNr_parser ("(?<![\\.\\-\\d])(-?\\d+)#"); // no leading [.-\d], number+'#' smatch match; if (regex_search (frameNumber, match, frameNr_parser)) return frameGrid.timeOf (lexical_cast<int64_t> (match[1])); @@ -115,7 +115,8 @@ namespace time { TimeValue Seconds::parse (string const& seconds, QuantR grid) { - static regex fracSecs_parser ("(-?\\d+)(?:([\\-\\+]\\d+)?/(\\d+))?sec"); + static regex fracSecs_parser ("(?<![\\./\\-\\d])(-?\\d+)(?:([\\-\\+]\\d+)?/(\\d+))?sec"); + //__no leading[./-\d] number [+-] number '/' number 'sec' #define SUB_EXPR(N) lexical_cast<long> (match[N]) smatch match; diff --git a/tests/40components.tests b/tests/40components.tests index c8cd30aad..320c283c3 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -704,7 +704,7 @@ return: 0 END -PLANNED "Parsing time values" TimeParsing_test <<END +TEST "Parsing time values" TimeParsing_test <<END END diff --git a/tests/lib/time/time-parsing-test.cpp b/tests/lib/time/time-parsing-test.cpp index 53d5f33d8..812c893d7 100644 --- a/tests/lib/time/time-parsing-test.cpp +++ b/tests/lib/time/time-parsing-test.cpp @@ -25,23 +25,12 @@ #include "lib/test/test-helper.hpp" #include "proc/asset/meta/time-grid.hpp" #include "lib/time/quantiser.hpp" -//#include "lib/time/timequant.hpp" #include "lib/time/timecode.hpp" -//#include "lib/time/display.hpp" #include "lib/symbol.hpp" #include "lib/util.hpp" -//#include <boost/lexical_cast.hpp> -//#include <iostream> -//#include <cstdlib> - -//using boost::lexical_cast; -//using util::isnil; using lib::Symbol; using util::cStr; -//using std::rand; -//using std::cout; -//using std::endl; namespace lib { @@ -51,6 +40,7 @@ namespace test{ using asset::meta::TimeGrid; using format::LUMIERA_ERROR_INVALID_TIMECODE; + namespace { // Helper for writing test cases Symbol DEFAULT_GRID = "pal0"; @@ -111,17 +101,25 @@ namespace test{ virtual void run (Arg) { - TimeGrid::build("pal0", FrameRate::PAL); - TimeGrid::build("pal10", FrameRate::PAL, Time(0,10)); + defineTestTimeGrids(); parseFrames(); parseFractionalSeconds(); +/////////////////////////////////////////////TODO // parseHms(); // parseSmpte(); // parseDropFrame(); } + void + defineTestTimeGrids() + { + TimeGrid::build(DEFAULT_GRID, FrameRate::PAL); + TimeGrid::build(OFFSET_GRID, FrameRate::PAL, Time(0,10)); + } + + void parseFrames () { @@ -141,16 +139,50 @@ namespace test{ Parsing<format::Frames> ("xxx25#xxx") .should_yield (1); Parsing<format::Frames> ("12 25#") .should_yield (1); - Parsing<format::Frames> ("12 25# 33#") .should_yield (1); + Parsing<format::Frames> ("12 25# 33#") .should_yield (1); // note pitfall: the first valid number is used Parsing<format::Frames> ("12\n 25# \n 33#") .should_yield (1); - Parsing<format::Frames> ("12.25#") .should_yield (1); + Parsing<format::Frames> ("12.25#") .should_fail(); // rejected because of leading dot (ambiguity) } void parseFractionalSeconds () { - UNIMPLEMENTED ("verify reading fractional seconds as timecode format"); + Parsing<format::Seconds> ("0sec") .should_yield (0); + Parsing<format::Seconds> ("1sec") .should_yield (1); + Parsing<format::Seconds> ("10sec") .should_yield (10); + Parsing<format::Seconds> ("100sec") .should_yield (100); + Parsing<format::Seconds> ("-10sec") .should_yield (-10); + Parsing<format::Seconds> ("-0sec") .should_yield (0); + + Parsing<format::Seconds> ("1/2sec") .should_yield (Time(500,0) ); + Parsing<format::Seconds> ("1/25sec") .should_yield (Time( 40,0) ); + Parsing<format::Seconds> ("1/250sec") .should_yield (Time( 4,0) ); // no quantisation involved in parsing + Parsing<format::Seconds> ("1/250sec", OFFSET_GRID).should_yield (Time(4,10)); // ...but the origin of the grid is used + + Parsing<format::Seconds> ("10/2sec") .should_yield (5); + Parsing<format::Seconds> ("1000/200sec") .should_yield (5); + Parsing<format::Seconds> ("-10/2sec") .should_yield (-5); + Parsing<format::Seconds> ("10/-2sec") .should_fail(); // only leading sign allowed (ambiguity) + + Parsing<format::Seconds> ("1+1/2sec") .should_yield (Time(500,1) ); + Parsing<format::Seconds> ("1-1/2sec") .should_yield (Time(500,0) ); + Parsing<format::Seconds> ("-1-1/2sec") .should_yield (-Time(500,1) ); + Parsing<format::Seconds> ("-1+1/2sec") .should_yield (-Time(500,0) ); + Parsing<format::Seconds> ("-1+1/-2sec") .should_fail(); + + Parsing<format::Seconds> ("-12+24690/12345sec", OFFSET_GRID).should_yield(0); // origin=+10sec -12sec + 2/1sec == 0 + + Parsing<format::Seconds> ("1") .should_fail(); + Parsing<format::Seconds> ("1 sec") .should_fail(); + Parsing<format::Seconds> ("--1sec") .should_fail(); + Parsing<format::Seconds> ("/-1sec") .should_fail(); + Parsing<format::Seconds> ("1.2sec") .should_fail(); + Parsing<format::Seconds> ("1/.2sec") .should_fail(); + Parsing<format::Seconds> ("1 + 2 / 4 sec") .should_fail(); + Parsing<format::Seconds> ("1 + 2 / 4sec") .should_yield(4); // note pitfall: leading garbage not considered + Parsing<format::Seconds> ("xxx4secxxxx") .should_yield(4); + Parsing<format::Seconds> ("x1# 8/2sec 2sec").should_yield(4); // note pitfall: first valid number used } From 4bf53bba0f591f585690f23298fcd3ff3999f3f0 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 14 May 2011 21:18:13 +0200 Subject: [PATCH 068/496] code unit test to demonstrate full time/timecode usage cycle --- src/lib/time/timecode.hpp | 3 +- tests/40components.tests | 3 ++ tests/lib/time/time-formats-test.cpp | 75 +++++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index 412b14f3b..fafe52d9c 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -102,7 +102,8 @@ namespace time { FrameNr (QuTime const& quantisedTime); - // CountVal implicitly convertible to long + using TCode::operator string; + // CountVal implicitly convertible to long }; diff --git a/tests/40components.tests b/tests/40components.tests index 320c283c3..ca4203a44 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -699,6 +699,9 @@ END TEST "Time formats and timecodes" TimeFormats_test <<END +out: Framecount=".+#" time = .+:..:.. +out: SMPTE=".+:..:..:.." time = .+:..:.. +out-lit: ----SMPTE----- out-lit: SMPTE=" 5:42:23:13" time = 5:42:23.520 return: 0 END diff --git a/tests/lib/time/time-formats-test.cpp b/tests/lib/time/time-formats-test.cpp index b9f6d4def..d85649201 100644 --- a/tests/lib/time/time-formats-test.cpp +++ b/tests/lib/time/time-formats-test.cpp @@ -26,16 +26,19 @@ #include "proc/asset/meta/time-grid.hpp" #include "lib/time/timequant.hpp" #include "lib/time/timecode.hpp" +#include "lib/time/mutation.hpp" #include "lib/time/display.hpp" #include "lib/util.hpp" #include <boost/lexical_cast.hpp> #include <iostream> -//#include <cstdlib> +#include <string> +#include <cstdlib> using boost::lexical_cast; using util::isnil; -//using std::rand; +using std::rand; +using std::string; using std::cout; using std::endl; @@ -45,6 +48,24 @@ namespace time{ namespace test{ using asset::meta::TimeGrid; + using format::Frames; + using format::Smpte; + + namespace{ + const int MAX_FRAME = 265*24*60*60*25; + + string + generateRandomFrameNr() + { + uint frameNr(0); + while (!frameNr) + frameNr = rand() % (2*MAX_FRAME) - MAX_FRAME; + + return lexical_cast<string>(frameNr)+"#"; + } + } + + /******************************************************** * @test verify handling of grid aligned timecode values. @@ -60,7 +81,7 @@ namespace test{ { TimeGrid::build("pal0", FrameRate::PAL); -// checkTimecodeUsageCycle (); + checkTimecodeUsageCycle (); // checkFrames (); // checkSeconds (); // checkHms (); @@ -70,13 +91,54 @@ namespace test{ } + /** @test demonstrate a full usage cycle of timecode and time values. + * Starting with a textual representation according to a specific timecode format, + * and based on the knowledge of the implicit underlying time grid (coordinate system, + * here with origin=0 and framerate=25fps), this timecode string may be parsed. + * This brings us (back) to the very origin, which is a raw TimeValue (internal time). + * Now, this time value might be manipulated, compared to other values etc. + * Anyway, at some point these time values need to be related to some time scale again, + * leading to quantised time values, which — finally — can be cast into a timecode format + * for external representation again, thus closing the circle. + */ void checkTimecodeUsageCycle () { - UNIMPLEMENTED ("full usage cycle for a timecode value"); + string quellCode = generateRandomFrameNr(); + PQuant refScale = Quantiser::retrieve("pal0"); - /////////TODO: parse-funktion, wie? - /////////TODO: vieleicht doch irgendwo eine Funktion zum Materialisieren? + // get internal (raw) time value + TimeValue t1 = format::Frames::parse(quellCode, *refScale); + ENSURE (0 != t1); + + // manipulating + TimeVar v1(t1); + v1 += Time(FSecs(6,5)); + CHECK (t1 < v1); + + // quantising into an external grid + QuTime q1 (t1, "pal0"); + CHECK (q1 == t1); + + // further mutations (here nudge by +5 grid steps) + QuTime q2 = q1; + q2.accept (Mutation::nudge(+5)); + CHECK (q1 < q2); + + // converting (back) into a timecode format + FrameNr frames1(q1); + FrameNr frames2(q2); + CHECK (5 == frames2 - frames1); + + q2.accept (Mutation::changeTime(v1)); + CHECK (30 == q2.formatAs<Frames>() - frames1); + + CHECK (quellCode == string(frames1)); + CHECK (quellCode != string(frames2)); + + showTimeCode (frames1); + showTimeCode (frames2); + showTimeCode (q2.formatAs<Smpte>()); } @@ -125,6 +187,7 @@ namespace test{ QuTime t1 (raw, "pal0"); SmpteTC smpte(t1); + cout << "----SMPTE-----" << endl; showTimeCode(smpte); CHECK (" 5:42:23:13" == string(smpte)); CHECK (raw - Time(35,0) == smpte.getTime()); // timecode value got quantised towards next lower frame From 3e28c870f5c5e1686ac26247161d6e1fe38ca65d Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 14 May 2011 22:52:08 +0200 Subject: [PATCH 069/496] Finish basic Time mutations, unit test pass --- tests/lib/time/time-mutation-test.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/tests/lib/time/time-mutation-test.cpp b/tests/lib/time/time-mutation-test.cpp index c21a58a7b..7ad09d379 100644 --- a/tests/lib/time/time-mutation-test.cpp +++ b/tests/lib/time/time-mutation-test.cpp @@ -27,22 +27,18 @@ #include "lib/time/timequant.hpp" #include "lib/time/mutation.hpp" #include "proc/asset/meta/time-grid.hpp" -//#include "lib/time/display.hpp" #include "lib/util.hpp" #include <boost/lexical_cast.hpp> #include <iostream> -//#include <cstdlib> #include <string> using boost::lexical_cast; using util::isnil; -//using std::rand; using std::cout; using std::endl; using std::string; -//using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE; namespace lib { namespace time{ @@ -62,6 +58,8 @@ namespace test{ } + + /**************************************************************** * @test cover all basic cases for mutating a time specification. * - change to a given value @@ -75,7 +73,7 @@ namespace test{ random_or_get (string arg) { if (isnil(arg)) - return 1 + (rand() % 10000); + return gavl_time_t (1 + (rand() % 100000)) * GAVL_TIME_SCALE; else return lexical_cast<gavl_time_t> (arg); } @@ -106,12 +104,12 @@ namespace test{ // using a 25fps-grid, but with an time origin offset by 1/50sec TimeGrid::build("test_grid", FrameRate::PAL, Time(FSecs(1,50))); - QuTime qVal (o, "test_grid"); - FrameNr count(qVal); + QuTime qChange (c, "test_grid"); + FrameNr count(qChange); mutate_by_Value (o, Time(c)); mutate_by_Offset (o, Offset(c)); - mutate_quantised (o, qVal); + mutate_quantised (o, qChange); mutate_by_Increment(o, count); } @@ -126,7 +124,7 @@ namespace test{ CHECK (t.span.start() != original); CHECK (t.span.start() == newStart); - // instead of invoking directly, we can store and copy mutation messages + // instead of invoking directly, we can store and copy mutation messages EncapsulatedMutation change_back(Mutation::changeTime (Time(original))); t.span.accept (change_back); CHECK (t.span.start() == original); @@ -199,7 +197,8 @@ namespace test{ // simulate what happened by explicit operations... Secs seconds = change.formatAs<format::Seconds>(); - Time materialised = seconds.getTime(); + PQuant quantiser(change); + Time materialised (quantiser->materialise(change)); CHECK (t.span == materialised); CHECK (t.span.duration() == original); // not affected by mutation as usual @@ -221,7 +220,7 @@ namespace test{ funny.accept (Mutation::materialise (change)); CHECK (funny == t.quant); // leading to the same raw value this far - Time doublyQuantised = Secs(funny).getTime(); + Time doublyQuantised (PQuant(funny)->materialise(funny)); CHECK (doublyQuantised != materialised); } @@ -259,8 +258,8 @@ namespace test{ t.quant.accept (Mutation::nudge (change)); CHECK (t.quant != original); long frameNr_after = t.quant.formatAs<format::Frames>(); - CHECK (frameNr_after != frameNr + change); - // i.e., use the quantised time's own grid + CHECK (frameNr_after == frameNr + change); + //i.e. the quantised time's own grid is used } }; From 5f6cbdc5bc3c6107006bfe814e723b219bb91742 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 14 Dec 2010 06:05:36 +0100 Subject: [PATCH 070/496] detail analysis of fixture storage managment --- wiki/renderengine.html | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 1985b9c83..b6a506167 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1801,7 +1801,7 @@ Some further details * a special case of this factory use is the [[Singleton]] factory, which is used a lot within the Proy-Layer code </pre> </div> -<div title="Fixture" modifier="Ichthyostega" modified="201012092320" created="200706220324" tags="def spec Builder Model" changecount="44"> +<div title="Fixture" modifier="Ichthyostega" modified="201012140217" created="200706220324" tags="def spec Builder Model" changecount="46"> <pre>a specially configured view -- joining together high-level and low-level model * all MObjects have their position, length and configuration set up ready for rendering. * any nested sequences (or other kinds of indirections) have been resolved. @@ -1810,6 +1810,7 @@ Some further details * besides, there is a collection of all effective, possibly externally visible [[model ports|ModelPortRegistry]] As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine. [[Creating the Fixture|BuildFixture]] is an important sideeffect of running the [[Builder]] when createing the [[render engine network|LowLevelModel]]. +''Note'': all of the especially managed storage of the LowLevelModel is hooked up behind the Fixture &rarr; FixtureStorage !{{red{WIP}}} Structure of the fixture [<img[Structure of the Fixture|draw/Fixture1.png]] @@ -1831,6 +1832,32 @@ 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.// </pre> </div> +<div title="FixtureStorage" modifier="Ichthyostega" modified="201012140505" created="201012140231" tags="Builder impl operational draft" changecount="7"> +<pre>The Fixture datastructure 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 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. + +!!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. + +''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 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. + +!!exploiting the frame-dispatch step +Irrespective of the dicision for 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 proces 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 dispather 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. + +But the distinguishing question here is that regarding the impact of such an implementation. What would be the costs? +# creating individual dispatcher tables uses memory +# the per-frame lookup is efficient and negligible compared with just building the render context (StateProxy) for that frame +# when a process terminates, we need to take out that dispatcher and do deregistration stuff for each touched segment (?) +</pre> +</div> <div title="ForwardIterator" modifier="Ichthyostega" modified="200912190027" created="200910312114" tags="Concepts def spec" changecount="17"> <pre>The situation focussed by this concept is when an API needs to expose a sequence of results, values or objects, instead of just yielding a function result value. As the naive solution of passing an pointer or array creates coupling to internals, it was superseded by the ~GoF [[Iterator pattern|http://en.wikipedia.org/wiki/Iterator]]. Iteration can be implemented by convention, polymorphically or by generic programming; we use the latter approach. @@ -4802,7 +4829,7 @@ We need to detect attaching and detaching of * root &harr; [[Track]] </pre> </div> -<div title="Segmentation" modifier="Ichthyostega" modified="201012122202" created="201012121901" tags="def spec Builder" changecount="24"> +<div title="Segmentation" modifier="Ichthyostega" modified="201012140222" created="201012121901" tags="def spec Builder" changecount="27"> <pre>//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 @@ -4811,7 +4838,7 @@ When [[building the fixture|BuildFixture]], ~MObjects -- as handled by their Pla **** an ExplicitPlacement for each covered object **** an ExitNode for each ModelPort of the corresponding timeline -!Storage considerations +!Usage pattern ;(1) build process :&rarr; a tree walk yields the placements per timeline, which then get //resolved// :&rarr; after //sorting,// the segmentation can be established, thereby copying placements spanning multiple segments @@ -4832,12 +4859,14 @@ 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, we need somehow to organise access per groups of model ports, actually grouped by timeline +# at top level, access is structured by groups of model ports, actually grouped by timeline. This first access level 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. # the frame dispatch and the tracking of processes can be combined; data duplication is a virtue when it comes to parallel processes # the process of comparing and tainting is broken out into a separate data structure to be used just once + +Largely the storage of the render nodes network is hooked up behind the Fixture &rarr; [[storage considerations|FixtureStorage]] </pre> </div> <div title="Sequence" modifier="Ichthyostega" modified="201010180156" created="201001252327" tags="def" changecount="17"> From 29df0984ec46afcd9c7f59c200ad331bc09c6bae Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 16 Dec 2010 05:45:38 +0100 Subject: [PATCH 071/496] (cont) details of releasing storage of fixture segments --- wiki/renderengine.html | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index b6a506167..58d210d6e 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1832,8 +1832,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.// </pre> </div> -<div title="FixtureStorage" modifier="Ichthyostega" modified="201012140505" created="201012140231" tags="Builder impl operational draft" changecount="7"> -<pre>The Fixture datastructure acts as umbrella to hook up the elements of the render engine's processing nodes network (LowLevelModel). +<div title="FixtureStorage" modifier="Ichthyostega" modified="201012160307" created="201012140231" tags="Builder impl operational draft" changecount="25"> +<pre>The Fixture data structure 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 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.// @@ -1844,18 +1844,40 @@ Each segment owns an AllocationCluster, which in turn manages all the numerous s //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. -''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 judge GCC's internal documentation right, currently their barriers extend to //all// globally visible variables) +''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. !!exploiting the frame-dispatch step -Irrespective of the dicision for 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 proces 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 dispather 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. -But the distinguishing question here is that regarding the impact of such an implementation. What would be the costs? -# creating individual dispatcher tables uses memory +!!assessment +But the primary question here is to judge the impact of such an implementation. What would be the costs? +# creating individual dispatcher tables trades memory for simplified parallelism # the per-frame lookup is efficient and negligible compared with just building the render context (StateProxy) for that frame # when a process terminates, we need to take out that dispatcher and do deregistration stuff for each touched segment (?) + +!!!Estimations +* number of actually concurrent render processes is at or below 30 +* depending on the degree of cleverness on behalf of the scheduler, the throughput of processes might be multiplied (dull means few processes) +* the total number of segments within the Fixture could range into several thousand +* but esp. playback is focussed, which makes a number of rather several hundred tainted segments more likely + +&rArr; we should try quickly to dispose of the working storage after render process termination and just retain a small notification record +&rArr; so the frame dispatcher table should be allocated //within//&nbsp; the process' working storage; moreover it should be tiled +&rArr; we should spend a second thought about how actually to find and process the segments to be discarded + +!!!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 +* 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. + +;Model A +:use a logarithmic datastructure, e.g. a priority queue +;Model B +:keep all candidates around and track the tainted processes instead </pre> </div> <div title="ForwardIterator" modifier="Ichthyostega" modified="200912190027" created="200910312114" tags="Concepts def spec" changecount="17"> From 6dd762fa7ae24771d8674c8940c17f8cf8227a81 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 17 Dec 2010 00:42:44 +0100 Subject: [PATCH 072/496] (cont) details of organising the fixture datastructure --- wiki/renderengine.html | 49 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 58d210d6e..3fe450040 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1801,7 +1801,7 @@ Some further details * a special case of this factory use is the [[Singleton]] factory, which is used a lot within the Proy-Layer code </pre> </div> -<div title="Fixture" modifier="Ichthyostega" modified="201012140217" created="200706220324" tags="def spec Builder Model" changecount="46"> +<div title="Fixture" modifier="Ichthyostega" modified="201012162259" created="200706220324" tags="def spec Builder Model" changecount="47"> <pre>a specially configured view -- joining together high-level and low-level model * all MObjects have their position, length and configuration set up ready for rendering. * any nested sequences (or other kinds of indirections) have been resolved. @@ -1810,7 +1810,9 @@ Some further details * besides, there is a collection of all effective, possibly externally visible [[model ports|ModelPortRegistry]] As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine. [[Creating the Fixture|BuildFixture]] is an important sideeffect of running the [[Builder]] when createing the [[render engine network|LowLevelModel]]. -''Note'': all of the especially managed storage of the LowLevelModel is hooked up behind the Fixture &rarr; FixtureStorage +''Note'': all of the especially managed storage of the LowLevelModel is hooked up behind the Fixture +&rarr; FixtureStorage +&rarr; FixtureDatastructure !{{red{WIP}}} Structure of the fixture [<img[Structure of the Fixture|draw/Fixture1.png]] @@ -1832,8 +1834,38 @@ 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.// </pre> </div> -<div title="FixtureStorage" modifier="Ichthyostega" modified="201012160307" created="201012140231" tags="Builder impl operational draft" changecount="25"> -<pre>The Fixture data structure acts as umbrella to hook up the elements of the render engine's processing nodes network (LowLevelModel). +<div title="FixtureDatastructure" modifier="Ichthyostega" modified="201012162342" created="201012162304" tags="spec Builder" changecount="6"> +<pre>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. + +!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. + +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 +* rendering frame dispatch +** get or create the frame dispatcher table +** dispatch a single frame to yield the corresponding ExitNode +* (re)building +** create a new implementation frame +** 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 +** 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 +</pre> +</div> +<div title="FixtureStorage" modifier="Ichthyostega" modified="201012162324" created="201012140231" tags="Builder impl operational draft" changecount="30"> +<pre>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 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.// @@ -1852,7 +1884,7 @@ There are. As the builder is known to be run again and again, no one forces us t !!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. -!!assessment +!!assessment {{red{WIP 12/10}}} But the primary question here is to judge the impact of such an implementation. What would be the costs? # creating individual dispatcher tables trades memory for simplified parallelism # the per-frame lookup is efficient and negligible compared with just building the render context (StateProxy) for that frame @@ -1876,8 +1908,15 @@ Above estimation hints at the necessity of frequently finding some 30 to 100 seg ;Model A :use a logarithmic datastructure, e.g. a priority queue +:problem here is that the priorities change, which either means shared access or a lot of "superseeded" entries ;Model B :keep all candidates around and track the tainted processes instead +:problem here is how to get the tainted processes precisely and with low overhead +//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? +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. </pre> </div> <div title="ForwardIterator" modifier="Ichthyostega" modified="200912190027" created="200910312114" tags="Concepts def spec" changecount="17"> From ee5c9910e1d8bc3c9372d8d0618971f70d293b56 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 15 May 2011 22:10:26 +0200 Subject: [PATCH 073/496] dry-run: switch the lumitime-test.cpp to use the new Time framework --- src/lib/time/diagnostics.hpp | 96 ++++++++++++++++++++++++++++ tests/lib/lumitime-test.cpp | 120 ++++++++++++++++++----------------- 2 files changed, 158 insertions(+), 58 deletions(-) create mode 100644 src/lib/time/diagnostics.hpp diff --git a/src/lib/time/diagnostics.hpp b/src/lib/time/diagnostics.hpp new file mode 100644 index 000000000..7689e8257 --- /dev/null +++ b/src/lib/time/diagnostics.hpp @@ -0,0 +1,96 @@ +/* + DIAGNOSTICS.hpp - diagnostics and output helpers for time(code) values + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file diagnostics.hpp + ** Extension to the lib::time::Time wrapper, adding output and further + ** diagnostic aids. This shouldn't be confused with formatting into + ** distinctive \em Timecode formats. There is an elaborate framework + ** for the latter: basically you'd need first to create a frame quantised + ** time value (QuTime) -- from there you can build various timecode + ** representations. + ** + ** To the contrary, the purpose of this header is to help with debugging, + ** writing unit tests and similar diagnostic activities. + ** + ** @see timevalue.hpp + ** @see lumitime-test.cpp + ** + */ + + + + + +#ifndef LIB_TIME_DIAGNOSTICS_H +#define LIB_TIME_DIAGNOSTICS_H + +#include "lib/time/timevalue.hpp" +#include "lib/time.h" + +#include <iostream> +#include <string> + + +namespace lib { +namespace time { + + + /** writes time value, formatted as HH:MM:SS:mmm + * @see lumiera_tmpbuf_print_time */ + inline std::ostream& + operator<< (std::ostream& os, TimeValue const& t) + { + return os << std::string(Time(t)); + } + + + /* === H:M:S:mm component diagnostics === */ + + inline int + getHours (TimeValue const& t) + { + return lumiera_time_hours (_raw(t)); + } + + inline int + getMins (TimeValue const& t) + { + return lumiera_time_minutes (_raw(t)); + } + + inline int + getSecs (TimeValue const& t) + { + return lumiera_time_seconds (_raw(t)); + } + + inline int + getMillis (TimeValue t) + { + return lumiera_time_millis (_raw(t)); + } + + + +}} // lib::time +#endif diff --git a/tests/lib/lumitime-test.cpp b/tests/lib/lumitime-test.cpp index a427d7476..46a1d25d6 100644 --- a/tests/lib/lumitime-test.cpp +++ b/tests/lib/lumitime-test.cpp @@ -35,30 +35,34 @@ using std::cout; using std::endl; -#include "lib/lumitime.hpp" -#include "lib/lumitime-fmt.hpp" +#include "lib/time/timevalue.hpp" +#include "lib/time/diagnostics.hpp" -namespace lumiera{ -namespace test { +namespace lib { +namespace test{ + + using time::Time; + using time::TimeVar; + using time::FSecs; /******************************************** - * @test sanity of the C++ time wrapper. + * @test sanity check of basic Time handling. */ class LumiTime_test : public Test { virtual void run (Arg arg) { - long refval= isnil(arg)? 1 : lexical_cast<long> (arg[1]); + FSecs refval = isnil(arg)? 1 : lexical_cast<long> (arg[1]); - Time ref (refval); + Time org(refval); - checkBasics (ref); - checkComparisons (ref); - checkComponentAccess(); + checkBasics (org); + checkComparisons (org); + checkComponentDiagnostics(); } @@ -66,18 +70,18 @@ namespace test { checkBasics (Time const& ref) { Time zero; - Time one (1); + Time two (FSecs(2)); Time max (Time::MAX); Time min (Time::MIN); - Time val (ref); + TimeVar var (ref); - val += Time(2); - val *= 2; - CHECK (zero == (val - 2*(ref + Time(2))) ); + var += two; + var *= 2; + CHECK (zero == (var - 2*(ref + two)) ); - val = ref; - CHECK (zero == (val - ref)); + var = ref; + CHECK (zero == (var - ref)); } @@ -88,27 +92,27 @@ namespace test { Time max (Time::MAX); Time min (Time::MIN); - CHECK (zero == Time(0)); + CHECK (zero == Time()); CHECK (min < zero); CHECK (max > zero); - Time val (ref); - CHECK ( (val == ref) ); - CHECK (!(val != ref) ); - CHECK ( (val >= ref) ); - CHECK ( (val <= ref) ); - CHECK (!(val < ref) ); - CHECK (!(val > ref) ); + TimeVar var (ref); + CHECK ( (var == ref) ); + CHECK (!(var != ref) ); + CHECK ( (var >= ref) ); + CHECK ( (var <= ref) ); + CHECK (!(var < ref) ); + CHECK (!(var > ref) ); - val += Time(2); - CHECK (!(val == ref) ); - CHECK ( (val != ref) ); - CHECK ( (val >= ref) ); - CHECK (!(val <= ref) ); - CHECK (!(val < ref) ); - CHECK ( (val > ref) ); + var += Time(FSecs(2)); + CHECK (!(var == ref) ); + CHECK ( (var != ref) ); + CHECK ( (var >= ref) ); + CHECK (!(var <= ref) ); + CHECK (!(var < ref) ); + CHECK ( (var > ref) ); - gavl_time_t gat(val); + gavl_time_t gat(var); CHECK (!(gat == ref) ); CHECK ( (gat != ref) ); CHECK ( (gat >= ref) ); @@ -116,17 +120,17 @@ namespace test { CHECK (!(gat < ref) ); CHECK ( (gat > ref) ); - CHECK ( (val == gat) ); - CHECK (!(val != gat) ); - CHECK ( (val >= gat) ); - CHECK ( (val <= gat) ); - CHECK (!(val < gat) ); - CHECK (!(val > gat) ); + CHECK ( (var == gat) ); + CHECK (!(var != gat) ); + CHECK ( (var >= gat) ); + CHECK ( (var <= gat) ); + CHECK (!(var < gat) ); + CHECK (!(var > gat) ); } void - checkComponentAccess() + checkComponentDiagnostics() { int millis = rand() % 1000; int secs = rand() % 60; @@ -134,34 +138,34 @@ namespace test { int hours = rand() % 100; Time time(millis,secs,mins,hours); - CHECK (millis == time.getMillis()); - CHECK (secs == time.getSecs()); - CHECK (mins == time.getMins()); - CHECK (hours == time.getHours()); + CHECK (Time() < time); + CHECK (millis == getMillis(time)); + CHECK (secs == getSecs (time)); + CHECK (mins == getMins (time)); + CHECK (hours == getHours (time)); cout << time << endl; Time t2(2008,0); cout << t2 << endl; - CHECK ( 8 == t2.getMillis()); - CHECK ( 2 == t2.getSecs()); - CHECK ( 0 == t2.getMins()); - CHECK ( 0 == t2.getHours()); + CHECK ( 8 == getMillis(t2)); + CHECK ( 2 == getSecs (t2)); + CHECK ( 0 == getMins (t2)); + CHECK ( 0 == getHours (t2)); Time t3(2008,88); cout << t3 << endl; - CHECK ( 8 == t3.getMillis()); - CHECK (30 == t3.getSecs()); - CHECK ( 1 == t3.getMins()); - CHECK ( 0 == t3.getHours()); + CHECK ( 8 == getMillis(t3)); + CHECK (30 == getSecs (t3)); + CHECK ( 1 == getMins (t3)); + CHECK ( 0 == getHours (t3)); Time t4(2008,118,58); cout << t4 << endl; - CHECK ( 8 == t4.getMillis()); - CHECK ( 0 == t4.getSecs()); - CHECK ( 0 == t4.getMins()); - CHECK ( 1 == t4.getHours()); + CHECK ( 8 == getMillis(t4)); + CHECK ( 0 == getSecs (t4)); + CHECK ( 0 == getMins (t4)); + CHECK ( 1 == getHours (t4)); } - }; @@ -170,4 +174,4 @@ namespace test { -}} // namespace lumiera::test +}} // namespace lib::test From b9861ef88f4c1a47e6406d34374c4e3e8d815202 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 15 May 2011 22:51:02 +0200 Subject: [PATCH 074/496] WIP completely remove the old Time wrapper --- src/gui/gtk-base.hpp | 1 - src/gui/model/clip.hpp | 10 +- src/gui/widgets/timecode-widget.hpp | 2 - src/lib/lumitime-fmt.hpp | 68 ------------ src/lib/lumitime.hpp | 100 ------------------ src/lib/test/test-coll.hpp | 2 - src/lib/test/test-helper.hpp | 2 - src/lib/time/lumitime.cpp | 60 ----------- src/lib/time/timecode.cpp | 1 - src/proc/common.hpp | 2 +- src/proc/mobject/mobject-ref.hpp | 1 - .../proc/control/command-basic-test.cpp | 2 - .../proc/control/command-use3-test.cpp | 2 - .../handling-pattern-standard-impl-test.cpp | 2 - .../proc/mobject/mobject-interface-test.cpp | 2 - .../proc/mobject/mobject-ref-test.cpp | 2 - .../placement-object-identity-test.cpp | 2 - .../proc/mobject/placement-ref-test.cpp | 2 - .../session/session-modify-parts-test.cpp | 2 - .../session/session-structure-test.cpp | 2 - .../proc/mobject/session/testclip.hpp | 4 +- .../timeline-sequence-handling-test.cpp | 2 - .../lib/meta/iterable-classification-test.cpp | 2 - 23 files changed, 8 insertions(+), 267 deletions(-) delete mode 100644 src/lib/lumitime-fmt.hpp delete mode 100644 src/lib/lumitime.hpp diff --git a/src/gui/gtk-base.hpp b/src/gui/gtk-base.hpp index 37f342cc6..e02e9597d 100644 --- a/src/gui/gtk-base.hpp +++ b/src/gui/gtk-base.hpp @@ -47,7 +47,6 @@ #include "lib/error.hpp" #include "lib/util.hpp" -#include "lib/lumitime.hpp" #ifdef ENABLE_NLS diff --git a/src/gui/model/clip.hpp b/src/gui/model/clip.hpp index ab3e7d2a3..2da8a6056 100644 --- a/src/gui/model/clip.hpp +++ b/src/gui/model/clip.hpp @@ -26,14 +26,14 @@ #include <string> #include "gui/gtk-lumiera.hpp" -#include "lib/lumitime.hpp" +//#include "lib/lumitime.hpp"//////////////////////////////TODO // TODO: Remove once we get real measure of duration. // This is here *only* for purposes of testing the GUI. -extern "C" { -#include <stdint.h> -#include <gavl/gavltime.h> -} +//extern "C" { +//#include <stdint.h> +//#include <gavl/gavltime.h> +//} using Cairo::Pattern; diff --git a/src/gui/widgets/timecode-widget.hpp b/src/gui/widgets/timecode-widget.hpp index 3a280a589..beefa21ea 100644 --- a/src/gui/widgets/timecode-widget.hpp +++ b/src/gui/widgets/timecode-widget.hpp @@ -33,9 +33,7 @@ #include <gtkmm/label.h> #include <gtkmm/frame.h> -#include "lib/lumitime.hpp" -using lumiera::Time; namespace gui { namespace widgets { diff --git a/src/lib/lumitime-fmt.hpp b/src/lib/lumitime-fmt.hpp deleted file mode 100644 index b916c13a6..000000000 --- a/src/lib/lumitime-fmt.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - LUMITIME-FMT.hpp - output and formatting of time(code) values - - Copyright (C) Lumiera.org - 2009, Hermann Vosseler <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - - -/** @file lumitime-fmt.hpp - ** Extension to the lumiera::Time wrapper, adding output and specific - ** time formats. For now (7/09) this header serves the purpose to add - ** the (not so lightweight) includes necessary for formatting. For later - ** the intention is to have "format aware" time values (as a subclass), - ** carrying the additional information about current framerate and - ** other time-format preferences - ** - ** @todo this should be a mixed C/C++ header and really add the handling - ** of multiple timecode-formats :-P - ** - ** @see time.h - ** @see lumitime.hpp - ** - */ - - - - - -#ifndef LUMIERA_LUMITIME_FMT_H -#define LUMIERA_LUMITIME_FMT_H - -#include "lib/lumitime.hpp" -#include "lib/time.h" - -#include <iostream> - - -namespace lumiera { - - - /** writes time value, formatted as HH:MM:SS:mmm - * @see lumiera_tmpbuf_print_time */ - inline std::ostream& - operator<< (std::ostream& os, Time const& t) - { - return os << lumiera_tmpbuf_print_time(t); - } - - - - -} // namespace lumiera -#endif diff --git a/src/lib/lumitime.hpp b/src/lib/lumitime.hpp deleted file mode 100644 index 88c6a1764..000000000 --- a/src/lib/lumitime.hpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - LUMITIME.hpp - convenience wrapper for working with gavl_time in C++ - - Copyright (C) Lumiera.org - 2008, Hermann Vosseler <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - - -#ifndef LUMIERA_LUMITIME_H -#define LUMIERA_LUMITIME_H - -#include <boost/operators.hpp> -#include <string> - -extern "C" { -#include <stdint.h> -#include <gavl/gavltime.h> -} - -namespace lumiera { - - - /** - * Lumiera's internal time data. - * Time denotes a time point, specified as opaque value on an - * quasi continuous ("sufficiently precise") internal time scale, relative - * to an (implicit) timeline zero point. The actual implementation relies - * on gavl_time_t (long) values. - * - * @see lib::time::TimeVar for an number-like time value usable for calculations - * - * @todo it is not clear to which degree Time is mutable... - */ - class Time - : boost::additive<Time, - boost::totally_ordered<Time, - boost::totally_ordered<Time, gavl_time_t> > > - { - gavl_time_t t_; - - public: - static const Time MAX ; - static const Time MIN ; - - explicit - Time (gavl_time_t val=0) - : t_(val) - { } - - Time ( long millis - , uint secs - , uint mins =0 - , uint hours=0 - ); - - // component access - int getMillis () const; - int getSecs () const; - int getMins () const; - int getHours () const; - int getFrames () const; - - operator std::string () const; - operator gavl_time_t () const { return t_; } - - // Supporting additive - Time& operator+= (Time const& tx) { t_ += tx.t_; return *this; } - Time& operator-= (Time const& tx) { t_ -= tx.t_; return *this; } - - // Supporting multiplication with integral factor - Time& operator*= (int64_t fact) { t_ *= fact; return *this; } - - // Supporting totally_ordered - friend bool operator< (Time const& t1, Time const& t2) { return t1.t_ < t2.t_; } - friend bool operator< (Time const& t1, gavl_time_t t2) { return t1.t_ < t2 ; } - friend bool operator> (Time const& t1, gavl_time_t t2) { return t1.t_ > t2 ; } - friend bool operator== (Time const& t1, Time const& t2) { return t1.t_ == t2.t_; } - friend bool operator== (Time const& t1, gavl_time_t t2) { return t1.t_ == t2 ; } - }; - - - - -} // namespace lumiera -#endif diff --git a/src/lib/test/test-coll.hpp b/src/lib/test/test-coll.hpp index 907c19309..839cbbdab 100644 --- a/src/lib/test/test-coll.hpp +++ b/src/lib/test/test-coll.hpp @@ -26,7 +26,6 @@ //#include "lib/symbol.hpp" -//#include "lib/lumitime.hpp" //#include <typeinfo> //#include <cstdlib> @@ -41,7 +40,6 @@ namespace lib { namespace test{ // using lib::Literal; -// using lumiera::Time; // using std::string; // using std::rand; diff --git a/src/lib/test/test-helper.hpp b/src/lib/test/test-helper.hpp index a115e3a1f..e124055b1 100644 --- a/src/lib/test/test-helper.hpp +++ b/src/lib/test/test-helper.hpp @@ -26,7 +26,6 @@ #include "lib/symbol.hpp" -#include "lib/lumitime.hpp" #include <typeinfo> #include <string> @@ -38,7 +37,6 @@ namespace lib { namespace test{ using lib::Literal; - using lumiera::Time; using std::string; using std::rand; diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index bb1c0e1a0..cc40b26c7 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -21,7 +21,6 @@ * *****************************************************/ -#include "lib/lumitime.hpp" #include "lib/time/timevalue.hpp" #include "lib/time.h" @@ -131,62 +130,3 @@ namespace time { }} // namespace lib::Time - -///////////////////////////////////////////////////////////////////////////TODO leftover of the existing/initial lumitime-Implementation -namespace lumiera { - - - const Time Time::MAX ( +std::numeric_limits<int64_t>::max() ); - const Time Time::MIN ( -std::numeric_limits<int64_t>::max() ); - - - - Time::Time ( long millis - , uint secs - , uint mins - , uint hours - ) - : t_(lumiera_build_time (millis,secs,mins,hours)) - { } - - int - Time::getMillis() const - { - return lumiera_time_millis(t_); - } - - - int - Time::getSecs() const - { - return lumiera_time_seconds(t_); - } - - - int - Time::getMins() const - { - return lumiera_time_minutes(t_); - } - - - int - Time::getHours() const - { - return lumiera_time_hours(t_); - } - - int - Time::getFrames() const - { - // TODO - return 0; - } - - - Time::operator string() const - { - return string (lumiera_tmpbuf_print_time (t_)); - } - -} // namespace lumiera diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 4cce6fd66..857179461 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -21,7 +21,6 @@ * *****************************************************/ -#include "lib/lumitime.hpp" #include "lib/time/timecode.hpp" #include "lib/time/timevalue.hpp" #include "lib/time/timequant.hpp" diff --git a/src/proc/common.hpp b/src/proc/common.hpp index ebf5ca48f..b6f42f445 100644 --- a/src/proc/common.hpp +++ b/src/proc/common.hpp @@ -46,7 +46,7 @@ #include "lib/p.hpp" #include "lib/util.hpp" -#include "lib/lumitime.hpp" +#include "lib/time/timevalue.hpp" #include "lib/symbol.hpp" #include "lib/error.hpp" // pulls in NoBug via loggging.h diff --git a/src/proc/mobject/mobject-ref.hpp b/src/proc/mobject/mobject-ref.hpp index ac03f32a1..e8c7cad5c 100644 --- a/src/proc/mobject/mobject-ref.hpp +++ b/src/proc/mobject/mobject-ref.hpp @@ -61,7 +61,6 @@ //#include "pre.hpp" #include "lib/handle.hpp" -#include "lib/lumitime.hpp" #include "proc/mobject/placement.hpp" #include "proc/mobject/placement-ref.hpp" #include "proc/mobject/session/session-service-mutate.hpp" diff --git a/tests/components/proc/control/command-basic-test.cpp b/tests/components/proc/control/command-basic-test.cpp index 76ef584d2..c2413c95c 100644 --- a/tests/components/proc/control/command-basic-test.cpp +++ b/tests/components/proc/control/command-basic-test.cpp @@ -23,12 +23,10 @@ #include "lib/test/run.hpp" #include "proc/control/command-def.hpp" -#include "lib/lumitime.hpp" #include "lib/p.hpp" #include <cstdlib> -using lumiera::Time; using std::rand; diff --git a/tests/components/proc/control/command-use3-test.cpp b/tests/components/proc/control/command-use3-test.cpp index 5d3d29799..c2917b598 100644 --- a/tests/components/proc/control/command-use3-test.cpp +++ b/tests/components/proc/control/command-use3-test.cpp @@ -24,7 +24,6 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" #include "proc/control/command-def.hpp" -//#include "lib/lumitime.hpp" #include "lib/util.hpp" #include "proc/control/test-dummy-commands.hpp" @@ -43,7 +42,6 @@ namespace test { // using boost::format; // using boost::str; - //using lumiera::Time; //using util::contains; // using std::tr1::function; // using std::tr1::bind; diff --git a/tests/components/proc/control/handling-pattern-standard-impl-test.cpp b/tests/components/proc/control/handling-pattern-standard-impl-test.cpp index c73a11246..01ba9789a 100644 --- a/tests/components/proc/control/handling-pattern-standard-impl-test.cpp +++ b/tests/components/proc/control/handling-pattern-standard-impl-test.cpp @@ -36,7 +36,6 @@ #include "proc/control/command-impl.hpp" #include "proc/control/command-registry.hpp" //#include "proc/control/command-def.hpp" -//#include "lib/lumitime.hpp" //#include "lib/symbol.hpp" //#include "lib/util.hpp" @@ -56,7 +55,6 @@ namespace test { // using boost::format; // using boost::str; - //using lumiera::Time; //using util::contains; using std::tr1::function; // using std::tr1::bind; diff --git a/tests/components/proc/mobject/mobject-interface-test.cpp b/tests/components/proc/mobject/mobject-interface-test.cpp index b1c5a1bae..b3f3e83eb 100644 --- a/tests/components/proc/mobject/mobject-interface-test.cpp +++ b/tests/components/proc/mobject/mobject-interface-test.cpp @@ -22,7 +22,6 @@ #include "lib/test/run.hpp" -#include "lib/lumitime.hpp" #include "lib/symbol.hpp" #include "proc/asset/media.hpp" @@ -53,7 +52,6 @@ namespace test { using lib::Symbol; - using lumiera::Time; // using session::Clip; // using session::PMedia; diff --git a/tests/components/proc/mobject/mobject-ref-test.cpp b/tests/components/proc/mobject/mobject-ref-test.cpp index c4b9bbd50..3447deb8e 100644 --- a/tests/components/proc/mobject/mobject-ref-test.cpp +++ b/tests/components/proc/mobject/mobject-ref-test.cpp @@ -22,7 +22,6 @@ #include "lib/test/run.hpp" -#include "lib/lumitime.hpp" #include "proc/asset/media.hpp" #include "proc/mobject/mobject.hpp" #include "proc/mobject/mobject-ref.hpp" @@ -67,7 +66,6 @@ namespace test { } - using lumiera::Time; using session::Clip; using session::PMedia; diff --git a/tests/components/proc/mobject/placement-object-identity-test.cpp b/tests/components/proc/mobject/placement-object-identity-test.cpp index 6d3e1044c..62309d07f 100644 --- a/tests/components/proc/mobject/placement-object-identity-test.cpp +++ b/tests/components/proc/mobject/placement-object-identity-test.cpp @@ -22,7 +22,6 @@ #include "lib/test/run.hpp" -#include "lib/lumitime.hpp" #include "proc/asset/media.hpp" #include "proc/mobject/mobject.hpp" #include "proc/mobject/mobject-ref.hpp" @@ -45,7 +44,6 @@ namespace mobject { namespace test { - using lumiera::Time; using session::Clip; using session::SessionServiceMockIndex; diff --git a/tests/components/proc/mobject/placement-ref-test.cpp b/tests/components/proc/mobject/placement-ref-test.cpp index dce4db94d..c61036ade 100644 --- a/tests/components/proc/mobject/placement-ref-test.cpp +++ b/tests/components/proc/mobject/placement-ref-test.cpp @@ -29,13 +29,11 @@ #include "proc/mobject/session/session-service-mock-index.hpp" #include "proc/mobject/explicitplacement.hpp" #include "proc/mobject/test-dummy-mobject.hpp" -#include "lib/lumitime.hpp" #include "lib/util.hpp" #include <iostream> using util::isSameObject; -using lumiera::Time; using std::string; using std::cout; using std::endl; diff --git a/tests/components/proc/mobject/session/session-modify-parts-test.cpp b/tests/components/proc/mobject/session/session-modify-parts-test.cpp index f7cb3d974..481e6f136 100644 --- a/tests/components/proc/mobject/session/session-modify-parts-test.cpp +++ b/tests/components/proc/mobject/session/session-modify-parts-test.cpp @@ -27,7 +27,6 @@ //#include "proc/assetmanager.hpp" //////?? //#include "proc/asset/timeline.hpp" #include "proc/asset/sequence.hpp" -//#include "lib/lumitime.hpp" #include "lib/util-foreach.hpp" #include "proc/mobject/session/testclip.hpp" #include "proc/mobject/mobject-ref.hpp" @@ -59,7 +58,6 @@ namespace test { using asset::PSequence; using asset::Sequence; -// using lumiera::Time; using lumiera::Query; typedef MORef<session::Clip> RClip; diff --git a/tests/components/proc/mobject/session/session-structure-test.cpp b/tests/components/proc/mobject/session/session-structure-test.cpp index 6ae27c3ee..551a85bd0 100644 --- a/tests/components/proc/mobject/session/session-structure-test.cpp +++ b/tests/components/proc/mobject/session/session-structure-test.cpp @@ -27,7 +27,6 @@ #include "proc/assetmanager.hpp" //////?? #include "proc/asset/timeline.hpp" #include "proc/asset/sequence.hpp" -#include "lib/lumitime.hpp" #include "lib/util.hpp" #include <iostream> @@ -47,7 +46,6 @@ namespace test { using asset::PTimeline; using asset::PSequence; - using lumiera::Time; /******************************************************************************* diff --git a/tests/components/proc/mobject/session/testclip.hpp b/tests/components/proc/mobject/session/testclip.hpp index 5907f6750..97c6b8554 100644 --- a/tests/components/proc/mobject/session/testclip.hpp +++ b/tests/components/proc/mobject/session/testclip.hpp @@ -26,7 +26,7 @@ #include "proc/mobject/session/clip.hpp" -#include "lib/lumitime.hpp" +#include "lib/time/timevalue.hpp" @@ -78,7 +78,7 @@ namespace test { /* == some test data to check == */ - extern const lumiera::Time LENGTH_TestClip; + extern const lib::time::Time LENGTH_TestClip; }}} // namespace mobject::session::test diff --git a/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp b/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp index a1eb84a34..92efe65f5 100644 --- a/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp +++ b/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp @@ -30,7 +30,6 @@ #include "proc/asset/timeline.hpp" #include "proc/asset/sequence.hpp" #include "proc/asset/pipe.hpp" -#include "lib/lumitime.hpp" #include "lib/query.hpp" #include "lib/util.hpp" @@ -56,7 +55,6 @@ namespace test { using asset::Pipe; using lumiera::Query; - using lumiera::Time; /******************************************************************************** diff --git a/tests/lib/meta/iterable-classification-test.cpp b/tests/lib/meta/iterable-classification-test.cpp index 8c5b8b770..bda746175 100644 --- a/tests/lib/meta/iterable-classification-test.cpp +++ b/tests/lib/meta/iterable-classification-test.cpp @@ -28,7 +28,6 @@ #include "lib/meta/duck-detector.hpp" #include "lib/util-foreach.hpp" #include "lib/itertools.hpp" -#include "lib/lumitime.hpp" /////////////////////////////////////////////////////////////TODO draft #include <boost/type_traits/is_convertible.hpp> @@ -45,7 +44,6 @@ namespace lib { namespace meta{ namespace test{ - using lumiera::Time; using mobject::session::Effect; using mobject::session::ScopeQuery; From 481875a78aa539d9165265e982a5b83e6d5cd716 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 16 May 2011 04:02:26 +0200 Subject: [PATCH 075/496] add check for time point contained in TimeSpan --- src/lib/time/timevalue.hpp | 10 ++++++++-- tests/lib/time/time-value-test.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index aa08c00ca..4087a9f5f 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -409,8 +409,14 @@ namespace time { Time end() const { - TimeVar startPoint (*this); - return (startPoint + dur_); + return TimeVar(*this) += dur_; + } + + bool + contains (TimeValue const& tp) const + { + return *this <= tp + && tp < end(); } /** may change start / duration */ diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index 27c4c1524..9a5a6fc4c 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -75,6 +75,7 @@ namespace test{ buildDuration (ref); buildTimeSpan (ref); compareTimeSpan (ref); + relateTimeIntervals (ref); } @@ -381,6 +382,30 @@ namespace test{ CHECK (span3y.end() < span3z.end()); CHECK (Time(span3) < Time(span3z)); } + + + void + relateTimeIntervals (TimeValue org) + { + TimeSpan span1 (org, FSecs(2)); + TimeSpan span2 (org+Time(FSecs(1)), FSecs(2)); + + TimeVar probe(org); + CHECK ( span1.contains(probe)); + CHECK (!span2.contains(probe)); + + probe = span2; + CHECK ( span1.contains(probe)); + CHECK ( span2.contains(probe)); + + probe = span1.end(); + CHECK (!span1.contains(probe)); // Note: end is always exclusive + CHECK ( span2.contains(probe)); + + probe = span2.end(); + CHECK (!span1.contains(probe)); + CHECK (!span2.contains(probe)); + } }; From ef126283eb62f376970baa11fc9b2b06f1582191 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 16 May 2011 04:37:03 +0200 Subject: [PATCH 076/496] add symbolic constant for "neutral" time. Note: it's rather arbitrary choosen, because any time grid can define its own origin. You must not assume that the origin of any time axis is located at Time::ZERO !! --- .../timeline/{basic-draw-strategy.cpp => draw-strategy.cpp} | 0 src/lib/time/lumitime.cpp | 1 + src/lib/time/timevalue.hpp | 1 + 3 files changed, 2 insertions(+) rename src/gui/widgets/timeline/{basic-draw-strategy.cpp => draw-strategy.cpp} (100%) diff --git a/src/gui/widgets/timeline/basic-draw-strategy.cpp b/src/gui/widgets/timeline/draw-strategy.cpp similarity index 100% rename from src/gui/widgets/timeline/basic-draw-strategy.cpp rename to src/gui/widgets/timeline/draw-strategy.cpp diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index cc40b26c7..de79b7f38 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -39,6 +39,7 @@ namespace time { /** @note the allowed time range is explicitly limited to help overflow protection */ const Time Time::MAX ( TimeValue::buildRaw_(+std::numeric_limits<gavl_time_t>::max() / 30) ); const Time Time::MIN ( TimeValue::buildRaw_(-_raw(Time::MAX) ) ); + const Time Time::ZERO; /** convenience constructor to build an diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 4087a9f5f..fcc287941 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -273,6 +273,7 @@ namespace time { public: static const Time MAX ; static const Time MIN ; + static const Time ZERO; explicit Time (TimeValue const& val =TimeValue(0)) From 042598f04ca7759ca6c7323dbccddddd6c0caaf5 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 16 May 2011 08:37:36 +0200 Subject: [PATCH 077/496] adapt Core + Proc to use the new Time framework --- src/lib/test/test-helper.hpp | 5 +++-- src/proc/asset/media.cpp | 14 ++++++++------ src/proc/asset/media.hpp | 9 +++++---- src/proc/asset/unknown.cpp | 2 +- src/proc/asset/unknown.hpp | 7 +++++-- src/proc/engine/rendergraph.hpp | 17 +++++++++++++---- src/proc/mobject/mobject-ref.hpp | 2 +- src/proc/mobject/mobject.cpp | 4 +++- src/proc/mobject/mobject.hpp | 8 ++++---- src/proc/mobject/placement.hpp | 3 ++- src/proc/mobject/session/abstractmo.hpp | 2 +- src/proc/mobject/session/clip.cpp | 14 +++++++++----- src/proc/mobject/session/clip.hpp | 5 ++++- src/proc/mobject/session/locatingpin.cpp | 2 +- src/proc/mobject/session/locatingpin.hpp | 11 +++++++---- src/proc/mobject/session/relativelocation.hpp | 6 ++++-- src/proc/mobject/session/segment.hpp | 7 +++---- src/proc/mobject/session/track.cpp | 2 +- src/proc/mobject/session/track.hpp | 7 ++++++- 19 files changed, 81 insertions(+), 46 deletions(-) diff --git a/src/lib/test/test-helper.hpp b/src/lib/test/test-helper.hpp index e124055b1..8a778840f 100644 --- a/src/lib/test/test-helper.hpp +++ b/src/lib/test/test-helper.hpp @@ -26,6 +26,7 @@ #include "lib/symbol.hpp" +#include "lib/time/timevalue.hpp" #include <typeinfo> #include <string> @@ -102,10 +103,10 @@ namespace test{ /** create a random but not insane Time value */ - inline Time + inline lib::time::Time randTime () { - return Time (500 * (rand() % 2), (rand() % 600)); + return lib::time::Time (500 * (rand() % 2), (rand() % 600)); } /** create garbage string of given length diff --git a/src/proc/asset/media.cpp b/src/proc/asset/media.cpp index 53cd1f1fd..d67231e89 100644 --- a/src/proc/asset/media.cpp +++ b/src/proc/asset/media.cpp @@ -28,6 +28,7 @@ #include "proc/asset/unknown.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/session/mobjectfactory.hpp" +#include "lib/time/timevalue.hpp" #include "lib/util.hpp" #include "include/logging.h" @@ -36,6 +37,8 @@ using util::isnil; +using lib::time::FSecs; +using lib::time::Duration; using boost::format; using boost::regex; @@ -44,11 +47,10 @@ using boost::regex_search; using std::tr1::dynamic_pointer_cast; -namespace asset - { +namespace asset { - namespace // Implementation details - { + namespace { // Implementation details + /** helper: extract a name token out of a given path/filename * @return sanitised token based on the name (minus extension), * empty string if not the common filename pattern. @@ -116,7 +118,7 @@ namespace asset } - lumiera::Time + Duration Media::getLength() const { return len_; @@ -157,7 +159,7 @@ namespace asset if (isnil (key.name)) key.name=extractName(file); TODO ("file exists?"); TODO ("extract media file properties"); - Time length(25); + Duration length(FSecs(5)); TODO ("detecting and wiring multichannel compound media!"); pM = new Media (key,file,length); } diff --git a/src/proc/asset/media.hpp b/src/proc/asset/media.hpp index d3b2c9024..bdd37da18 100644 --- a/src/proc/asset/media.hpp +++ b/src/proc/asset/media.hpp @@ -39,6 +39,7 @@ #include "proc/asset.hpp" #include "lib/factory.hpp" +#include "lib/time/timevalue.hpp" #include "proc/mobject/mobject.hpp" #include "proc/mobject/session/clip.hpp" @@ -52,7 +53,7 @@ namespace asset { class ProcPatt; using lumiera::P; - using lumiera::Time; + using lib::time::Duration; template<> @@ -71,7 +72,7 @@ namespace asset { class Media : public Asset { string filename_; - const Time len_; + const Duration len_; public: typedef P<Media> PMedia; @@ -105,11 +106,11 @@ namespace asset { PClipMO createClip (); /** @return the overall length of the media represented by this asset */ - virtual Time getLength () const; + virtual Duration getLength () const; protected: - Media (const Asset::Ident& idi, const string& file, Time length) + Media (const Asset::Ident& idi, const string& file, Duration length) : Asset(idi), filename_(file), len_(length) {} friend class MediaFactory; diff --git a/src/proc/asset/unknown.cpp b/src/proc/asset/unknown.cpp index ca25adf74..f8d3b81f1 100644 --- a/src/proc/asset/unknown.cpp +++ b/src/proc/asset/unknown.cpp @@ -38,7 +38,7 @@ namespace asset { * on using the Unknown::getOrg() function. * @note we don't depend on the refered media... */ - Unknown::Unknown (const Asset::Ident& idi, string name, Time length) + Unknown::Unknown (const Asset::Ident& idi, string name, Duration length) : Media (idi, name, length) { TODO ("implement some sensible behaviour for the »unknown media« placeholder..."); diff --git a/src/proc/asset/unknown.hpp b/src/proc/asset/unknown.hpp index fa3f5a291..31f3df6b0 100644 --- a/src/proc/asset/unknown.hpp +++ b/src/proc/asset/unknown.hpp @@ -25,13 +25,16 @@ #define ASSET_UNKNOWN_H #include "proc/asset/media.hpp" +#include "lib/time/timevalue.hpp" namespace asset { + using lib::time::FSecs; + using lib::time::Duration; - const lumiera::Time DUMMY_TIME (25); ///< @todo solve config management + const Duration DUMMY_TIME (FSecs(5)); ///< @todo solve config management /** * Placeholder Asset for unknown or unavailable media source. @@ -42,7 +45,7 @@ namespace asset { class Unknown : public Media { protected: - Unknown (const Asset::Ident& idi, string name="", Time length=DUMMY_TIME); + Unknown (const Asset::Ident& idi, string name="", Duration length=DUMMY_TIME); friend class MediaFactory; public: diff --git a/src/proc/engine/rendergraph.hpp b/src/proc/engine/rendergraph.hpp index 718d3dfdb..a77eac68b 100644 --- a/src/proc/engine/rendergraph.hpp +++ b/src/proc/engine/rendergraph.hpp @@ -26,11 +26,16 @@ #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; /** @@ -41,11 +46,15 @@ namespace engine { protected: ExitNode * output; - /** begin of the timerange covered by this RenderGraph */ - lumiera::Time start; + /** timerange covered by this RenderGraph */ + TimeSpan segment_; - /**end (exclusive) of the timerange */ - lumiera::Time end; + public: + RenderGraph() + : segment_(Time::ZERO, FSecs(5)) + { + UNIMPLEMENTED ("anything regarding the Fixture datastructure"); + } }; diff --git a/src/proc/mobject/mobject-ref.hpp b/src/proc/mobject/mobject-ref.hpp index e8c7cad5c..e4e5b3935 100644 --- a/src/proc/mobject/mobject-ref.hpp +++ b/src/proc/mobject/mobject-ref.hpp @@ -147,7 +147,7 @@ namespace mobject { /** resolves the referred placement to an * ExplicitPlacement and returns the found start time */ - lumiera::Time const& + lib::time::Time const& getStartTime() { return pRef_.resolve().time; diff --git a/src/proc/mobject/mobject.cpp b/src/proc/mobject/mobject.cpp index f55c588aa..f844510fa 100644 --- a/src/proc/mobject/mobject.cpp +++ b/src/proc/mobject/mobject.cpp @@ -23,8 +23,10 @@ #include "proc/mobject/mobject.hpp" #include "proc/mobject/session/mobjectfactory.hpp" +#include "lib/time/timevalue.hpp" #include "lib/util.hpp" +using lib::time::Time; using util::isnil; namespace mobject { @@ -39,7 +41,7 @@ namespace mobject { MObject::MObject() - : length_() + : length_(Time::ZERO) , shortID_() { } diff --git a/src/proc/mobject/mobject.hpp b/src/proc/mobject/mobject.hpp index a8ea73313..c20bd64dd 100644 --- a/src/proc/mobject/mobject.hpp +++ b/src/proc/mobject/mobject.hpp @@ -31,6 +31,7 @@ #include "proc/mobject/builder/buildertool.hpp" #include "proc/mobject/placement.hpp" #include "proc/asset.hpp" //TODO finally not needed? +#include "lib/time/timevalue.hpp" #include <boost/noncopyable.hpp> #include <boost/operators.hpp> @@ -68,10 +69,9 @@ namespace mobject { boost::equality_comparable< MObject > { protected: - typedef lumiera::Time Time; + typedef lib::time::Duration Duration; - // TODO: how to represent time intervals best? - Time length_; + Duration length_; mutable string shortID_; @@ -93,7 +93,7 @@ namespace mobject { /** MObject self-test (usable for asserting) */ virtual bool isValid() const =0; - virtual Time& getLength() =0; ///< @todo how to deal with the time/length field?? ////TICKET #448 + virtual Duration& getLength() =0; ////////////////////TICKET #448 virtual bool operator== (const MObject& oo) const =0; ///< needed for handling by lumiera::P diff --git a/src/proc/mobject/placement.hpp b/src/proc/mobject/placement.hpp index 5cd8206dc..94f6b2cca 100644 --- a/src/proc/mobject/placement.hpp +++ b/src/proc/mobject/placement.hpp @@ -69,6 +69,7 @@ #include "pre.hpp" #include "lib/hash-indexed.hpp" +#include "lib/time/timevalue.hpp" #include "proc/mobject/session/locatingpin.hpp" #include "proc/asset/pipe.hpp" //////////////TICKET #109 : get rid of this @@ -123,7 +124,7 @@ namespace mobject { typedef HashIndexed<Placement<MObject>, lib::hash::LuidH> HashInd; typedef shared_ptr<MObject> _SmartPtr; typedef void (*Deleter)(MObject*); - typedef lumiera::Time Time; + typedef lib::time::Time Time; typedef asset::shared_ptr<asset::Pipe> Pipe; ////TICKET #109 : get rid of this diff --git a/src/proc/mobject/session/abstractmo.hpp b/src/proc/mobject/session/abstractmo.hpp index 53abf0466..1140f3c39 100644 --- a/src/proc/mobject/session/abstractmo.hpp +++ b/src/proc/mobject/session/abstractmo.hpp @@ -54,7 +54,7 @@ namespace session { public: - Time& + Duration& getLength() { return length_; diff --git a/src/proc/mobject/session/clip.cpp b/src/proc/mobject/session/clip.cpp index af388fd9b..426cd8e57 100644 --- a/src/proc/mobject/session/clip.cpp +++ b/src/proc/mobject/session/clip.cpp @@ -25,6 +25,11 @@ #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 { @@ -33,9 +38,8 @@ namespace session { * Initially, this clip will cover the whole source media length. */ Clip::Clip (const asset::Clip& clipDef, const Media& mediaDef) - : start_(0), - mediaDef_(mediaDef), - clipDef_(clipDef) + : mediaDef_(mediaDef) + , clipDef_(clipDef) { setupLength(); throwIfInvalid(); @@ -50,7 +54,7 @@ namespace session { Clip::isValid () const { TODO ("check consistency of clip length def, implies accessing the underlying media def"); - return length_ > Time(0); + return !isnil(length_); } @@ -58,7 +62,7 @@ namespace session { Clip::setupLength() { TODO ("really calculate the length of a clip and set length field"); - this->length_ = mediaDef_.getLength(); + this->length_.accept (Mutation::changeDuration(mediaDef_.getLength())); } diff --git a/src/proc/mobject/session/clip.hpp b/src/proc/mobject/session/clip.hpp index 6a6251103..52e134926 100644 --- a/src/proc/mobject/session/clip.hpp +++ b/src/proc/mobject/session/clip.hpp @@ -25,6 +25,7 @@ #define MOBJECT_SESSION_CLIP_H #include "proc/mobject/session/abstractmo.hpp" +#include "lib/time/timevalue.hpp" namespace asset { @@ -36,6 +37,8 @@ namespace mobject { namespace session { using asset::Media; + using lib::time::TimeVar; + typedef P<Media> PMedia; typedef P<asset::Clip> PClipAsset; @@ -67,7 +70,7 @@ namespace session { protected: /** start position in source */ - Time start_; + 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 diff --git a/src/proc/mobject/session/locatingpin.cpp b/src/proc/mobject/session/locatingpin.cpp index 1c98c3df1..059e38290 100644 --- a/src/proc/mobject/session/locatingpin.cpp +++ b/src/proc/mobject/session/locatingpin.cpp @@ -189,7 +189,7 @@ namespace session { RelativeLocation& - LocatingPin::operator() (PlaRef& refObj, Time offset) + LocatingPin::operator() (PlaRef& refObj, Offset const& offset) { return static_cast<RelativeLocation&> (addChain (new RelativeLocation (refObj, offset))); diff --git a/src/proc/mobject/session/locatingpin.hpp b/src/proc/mobject/session/locatingpin.hpp index 04feae332..dc58ac944 100644 --- a/src/proc/mobject/session/locatingpin.hpp +++ b/src/proc/mobject/session/locatingpin.hpp @@ -48,6 +48,7 @@ #include "proc/common.hpp" +#include "lib/time/timevalue.hpp" #include <utility> #include <tr1/memory> @@ -96,7 +97,9 @@ namespace mobject { class LocatingPin { protected: - typedef lumiera::Time Time; + typedef lib::time::Time Time; + typedef lib::time::TimeVar TimeVar; + typedef lib::time::Offset Offset; typedef Time* Track; //TODO dummy declaration; we don't use Tracks as first-class entity any longer typedef std::tr1::shared_ptr<asset::Pipe> Pipe; typedef std::pair<Time,Pipe> SolutionData; //TICKET #100 (ichthyo considers better passing of solution by subclass) @@ -119,7 +122,7 @@ namespace mobject { /* Factory functions for adding LocatingPins */ FixedLocation& operator() (Time start, Track track=0); - RelativeLocation& operator() (PlacementRef<MObject>& refObj, Time offset=Time(0)); //////////TODO: warning, just a dummy placeholder for now!! + RelativeLocation& operator() (PlacementRef<MObject>& refObj, Offset const& offset=Offset(Time::ZERO)); //////////TODO: warning, just a dummy placeholder for now!! LocatingPin (const LocatingPin&); LocatingPin& operator= (const LocatingPin&); @@ -145,8 +148,8 @@ protected: */ struct LocatingSolution { - Time minTime; - Time maxTime; + TimeVar minTime; + TimeVar maxTime; Track minTrack; // TODO don't use Tracks Track maxTrack; bool impo; diff --git a/src/proc/mobject/session/relativelocation.hpp b/src/proc/mobject/session/relativelocation.hpp index 46b2a6d4b..bea3e134b 100644 --- a/src/proc/mobject/session/relativelocation.hpp +++ b/src/proc/mobject/session/relativelocation.hpp @@ -33,6 +33,8 @@ namespace mobject { namespace session { + using lib::time::Offset; + /** * @todo just a design sketch, nothing finished yet. @@ -56,14 +58,14 @@ namespace session { }; protected: - RelativeLocation (PlaRef const& a, Time ofs) : anchor_(a), offset_(ofs) { } + RelativeLocation (PlaRef const& a, Offset const& ofs) : anchor_(a), offset_(ofs) { } friend class LocatingPin; /** the kind of relation denoted by this Placement */ RelType relType; /** Offset the actual position by this (time) value relative to the anchor point. */ - Time offset_; + Offset offset_; //TODO: suitable representation? diff --git a/src/proc/mobject/session/segment.hpp b/src/proc/mobject/session/segment.hpp index 3a0835c87..9c39533e2 100644 --- a/src/proc/mobject/session/segment.hpp +++ b/src/proc/mobject/session/segment.hpp @@ -28,6 +28,7 @@ #include "proc/common.hpp" #include "proc/mobject/explicitplacement.hpp" +#include "lib/time/timevalue.hpp" using std::list; @@ -46,12 +47,10 @@ namespace mobject { class Segment { protected: - typedef lumiera::Time Time; + typedef lib::time::TimeSpan Span; /** begin of this timeline segment. */ - Time start; - /** duration (span) of this timeline segment. */ - Time length; + Span span_; /** relevant MObjects comprising this segment. */ list<ExplicitPlacement> elements; diff --git a/src/proc/mobject/session/track.cpp b/src/proc/mobject/session/track.cpp index 73e811b90..78ca7a52a 100644 --- a/src/proc/mobject/session/track.cpp +++ b/src/proc/mobject/session/track.cpp @@ -32,7 +32,7 @@ namespace session { * will be set to 0 */ Track::Track (TrackID const& trackID) - : start_(0) + : start_(Time::ZERO) //////////////////////////////////TODO something more inspired please , id_(trackID) { throwIfInvalid(); diff --git a/src/proc/mobject/session/track.hpp b/src/proc/mobject/session/track.hpp index 60fb8afa5..998c51899 100644 --- a/src/proc/mobject/session/track.hpp +++ b/src/proc/mobject/session/track.hpp @@ -26,6 +26,7 @@ #include "proc/mobject/session/meta.hpp" #include "proc/asset/entry-id.hpp" +#include "lib/time/timevalue.hpp" @@ -33,6 +34,7 @@ namespace mobject { namespace session { //////////////////////////////////////////////////////TICKET #637 using lumiera::P; + using lib::time::Time; class Track; @@ -52,6 +54,9 @@ namespace session { using asset::TrackID; + //////////////////////////////////////////////////////TICKET #646 + //////////////////////////////////////////////////////TICKET #715 + /** * A Track is grouping device within the Session. * The corresponding Placement by which this Track object is referred @@ -73,7 +78,7 @@ namespace session { */ class Track : public Meta { - Time start_; + Time start_; /////////////////////////////////TODO: this doesn't belong here. The start position is governed by the Placement!!! TrackID id_; From 7656e5d5000dbb57709cfb95bd38d5b0b9960c0f Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 16 May 2011 08:38:01 +0200 Subject: [PATCH 078/496] adapt GUI to use the new Time framework --- src/gui/model/clip.cpp | 54 ++---- src/gui/model/clip.hpp | 166 ++++++++---------- src/gui/panels/panel.cpp | 2 +- src/gui/panels/panel.hpp | 3 - src/gui/panels/timeline-panel.cpp | 86 ++++----- src/gui/panels/timeline-panel.hpp | 25 ++- src/gui/widgets/timecode-widget.cpp | 57 +++--- src/gui/widgets/timecode-widget.hpp | 16 +- src/gui/widgets/timeline-widget.cpp | 14 +- src/gui/widgets/timeline-widget.hpp | 32 ++-- .../widgets/timeline/basic-draw-strategy.hpp | 34 ++-- src/gui/widgets/timeline/draw-strategy.cpp | 26 +-- src/gui/widgets/timeline/draw-strategy.hpp | 52 +++--- .../widgets/timeline/timeline-arrow-tool.cpp | 10 +- .../widgets/timeline/timeline-arrow-tool.hpp | 16 +- src/gui/widgets/timeline/timeline-body.cpp | 39 ++-- src/gui/widgets/timeline/timeline-body.hpp | 14 +- .../widgets/timeline/timeline-clip-track.cpp | 8 +- .../widgets/timeline/timeline-clip-track.hpp | 16 +- src/gui/widgets/timeline/timeline-clip.cpp | 34 ++-- src/gui/widgets/timeline/timeline-clip.hpp | 76 ++++---- src/gui/widgets/timeline/timeline-entity.cpp | 6 +- src/gui/widgets/timeline/timeline-entity.hpp | 25 +-- .../widgets/timeline/timeline-ibeam-tool.cpp | 38 ++-- .../widgets/timeline/timeline-ibeam-tool.hpp | 13 +- src/gui/widgets/timeline/timeline-ruler.cpp | 105 +++++------ src/gui/widgets/timeline/timeline-ruler.hpp | 3 +- src/gui/widgets/timeline/timeline-state.cpp | 116 ++++-------- src/gui/widgets/timeline/timeline-state.hpp | 103 ++++------- src/gui/widgets/timeline/timeline-track.cpp | 7 +- src/gui/widgets/timeline/timeline-track.hpp | 9 +- .../widgets/timeline/timeline-view-window.cpp | 34 ++-- .../widgets/timeline/timeline-view-window.hpp | 41 +++-- 33 files changed, 623 insertions(+), 657 deletions(-) diff --git a/src/gui/model/clip.cpp b/src/gui/model/clip.cpp index 1dc0f2e00..2b7dc6cd6 100644 --- a/src/gui/model/clip.cpp +++ b/src/gui/model/clip.cpp @@ -20,65 +20,47 @@ * *****************************************************/ -#include "clip.hpp" + +#include "gui/model/clip.hpp" +#include "lib/time/mutation.hpp" + +using lib::time::FSecs; +using lib::time::Mutation; + namespace gui { namespace model { Clip::Clip() - : begin(1000000), - end(2000000) + : timeCoord_(Time(FSecs(1)), FSecs(2)) { } - - gavl_time_t - Clip::getBegin() const - { - return begin; - } - - gavl_time_t - Clip::getEnd() const - { - return end; - } - - const std::string - Clip::getName() const - { - return name; - } - - bool - Clip::isPlayingAt(lumiera::Time position) const - { - return (begin <= position && end >= position); - } - + + void - Clip::setBegin(gavl_time_t begin) + Clip::setBegin (Time newStartTime) { - this->begin = begin; + timeCoord_.accept (Mutation::changeTime (newStartTime)); // TODO: emit signal } void - Clip::setEnd(gavl_time_t end) + Clip::setDuration (Duration newLength) { - this->end = end; + timeCoord_.accept (Mutation::changeDuration(newLength)); // TODO: emit signal } void - Clip::setName(const std::string &name) + Clip::setName (string const& name) { - this->name = name; - nameChangedSignal.emit(name); + this->name_ = name; + nameChangedSignal_.emit (name); } sigc::signal<void, std::string> Clip::signalNameChanged() const { - return nameChangedSignal; + return nameChangedSignal_; } } // namespace model diff --git a/src/gui/model/clip.hpp b/src/gui/model/clip.hpp index 2da8a6056..134680e95 100644 --- a/src/gui/model/clip.hpp +++ b/src/gui/model/clip.hpp @@ -19,109 +19,87 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /** @file model/clip.hpp - ** This file contains the definition of the Clip object + ** This file defines a Proxy Clip object to base the GUI implementation on. + ** Later this Clip object will be connected to the underlying model in Proc-Layer. */ -#include <string> +#ifndef GUI_MODEL_CLIP_H +#define GUI_MODEL_CLIP_H + #include "gui/gtk-lumiera.hpp" +#include "lib/time/timevalue.hpp" -//#include "lib/lumitime.hpp"//////////////////////////////TODO +#include <string> -// TODO: Remove once we get real measure of duration. -// This is here *only* for purposes of testing the GUI. -//extern "C" { -//#include <stdint.h> -//#include <gavl/gavltime.h> -//} - -using Cairo::Pattern; - -#ifndef CLIP_HPP -#define CLIP_HPP namespace gui { namespace model { - + + using std::string; + using lib::time::Time; + using lib::time::TimeSpan; + using lib::time::Duration; + using Cairo::Pattern; + + class Clip - { - public: - /** - * Constructor - */ - Clip(); - - /** - * Gets the begin time of this clip. - */ - gavl_time_t - getBegin() const; - - /** - * Gets the end time of this clip. - */ - gavl_time_t - getEnd() const; - - /** - * Gets the name of this clip. - */ - const std::string - getName() const; - - /** - * Check whether or not the clip will be playing during the given time. - */ - bool - isPlayingAt(lumiera::Time position) const; - - /** - * Sets the begin time of this clip. - * @param[in] begin The new begin time to set this clip to. - */ - void - setBegin(gavl_time_t begin); - - /** - * Sets the end time of this clip. - * @param[in] end The new end time to set this clip to. - */ - void - setEnd(gavl_time_t end); - - /** - * Sets the name of this clip. - * @param[in] name The new name to set this clip to. - */ - void - setName(const std::string &name); - - /** - * A signal which fires when the name changes. - * @return Returns the signal. The signal sends the new name for the clip. - */ - sigc::signal<void, std::string> - signalNameChanged() const; - - private: - - /** - * The name of this clip. - */ - std::string name; - - /** - * A signal which fires when the name changes. - */ - sigc::signal<void, std::string> nameChangedSignal; - - // TODO: Use a good measure of duration, probably TimeSpan. - // These are here *only* for purposes of testing the GUI. - gavl_time_t begin; - gavl_time_t end; - }; - -} // namespace model -} // namespace gui + { + TimeSpan timeCoord_; + string name_; + + /** fires when the name changes. */ + sigc::signal<void, string> nameChangedSignal_; + + + public: + + Clip(); + + Time getBegin() const { return timeCoord_.start();} + Time getEnd() const { return timeCoord_.end(); } + + string const& getName() const { return name_; } + + + /** + * Check whether or not the clip will be playing during the given time. + */ + bool + isPlayingAt (Time const& position) const + { + return timeCoord_.contains (position); + } + + /** + * Sets the begin time of this clip. + * @param[in] begin The new begin time to set this clip to. + */ + void setBegin (Time); + + /** + * Sets the end time of this clip. + * @param[in] end The new end time to set this clip to. + */ + void setDuration (Duration); + + /** + * Sets the name of this clip. + * @param[in] name The new name to set this clip to. + */ + void setName (string const&); + + /** + * A signal which fires when the name changes. + * @return Returns the signal. The signal sends the new name for the clip. + */ + sigc::signal<void, std::string> + signalNameChanged() const; + + }; + + +}} // namespace gui::model #endif // CLIP_HPP diff --git a/src/gui/panels/panel.cpp b/src/gui/panels/panel.cpp index 4a72e82ff..4606c0ab3 100644 --- a/src/gui/panels/panel.cpp +++ b/src/gui/panels/panel.cpp @@ -26,7 +26,7 @@ #include "gui/workspace/panel-manager.hpp" #include "gui/workspace/workspace-window.hpp" -#include "panel.hpp" +#include "gui/panels/panel.hpp" using namespace Gtk; diff --git a/src/gui/panels/panel.hpp b/src/gui/panels/panel.hpp index ef287d8f6..1a9801250 100644 --- a/src/gui/panels/panel.hpp +++ b/src/gui/panels/panel.hpp @@ -59,9 +59,6 @@ protected: const gchar *stock_id); public: - /** - * Destructor - */ ~Panel(); /** diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index 9441e8969..07dce7b1d 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -20,47 +20,48 @@ * *****************************************************/ -#include <boost/foreach.hpp> #include "gui/gtk-lumiera.hpp" -#include "timeline-panel.hpp" +#include "gui/panels/timeline-panel.hpp" #include "gui/workspace/workspace-window.hpp" #include "gui/model/project.hpp" #include "gui/controller/controller.hpp" +#include "lib/util.hpp" -#include "lib/time.h" - +#include <boost/foreach.hpp> using namespace Gtk; using namespace sigc; -using namespace std; -using namespace boost; -using namespace util; using namespace gui::widgets; using namespace gui::model; +using boost::shared_ptr; ///////////////////////////////TICKET #796 +using boost::weak_ptr; ///////////////////////////////TICKET #796 +using util::contains; + namespace gui { namespace panels { const int TimelinePanel::ZoomToolSteps = 2; // 2 seems comfortable -TimelinePanel::TimelinePanel(workspace::PanelManager &panel_manager, - GdlDockItem *dock_item) : - Panel(panel_manager, dock_item, get_title(), get_stock_id()), - timeCode("sequence_clock", "timecode_widget", true), - previousButton(Stock::MEDIA_PREVIOUS), - rewindButton(Stock::MEDIA_REWIND), - playPauseButton(Stock::MEDIA_PLAY), - stopButton(Stock::MEDIA_STOP), - forwardButton(Stock::MEDIA_FORWARD), - nextButton(Stock::MEDIA_NEXT), - arrowTool(Gtk::StockID("tool_arrow")), - iBeamTool(Gtk::StockID("tool_i_beam")), - zoomIn(Stock::ZOOM_IN), - zoomOut(Stock::ZOOM_OUT), - updatingToolbar(false), - currentTool(timeline::Arrow) + +TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager, + GdlDockItem *dock_item) + : Panel(panel_manager, dock_item, get_title(), get_stock_id()) + , timeCode("sequence_clock", "timecode_widget", true) + , previousButton(Stock::MEDIA_PREVIOUS) + , rewindButton(Stock::MEDIA_REWIND) + , playPauseButton(Stock::MEDIA_PLAY) + , stopButton(Stock::MEDIA_STOP) + , forwardButton(Stock::MEDIA_FORWARD) + , nextButton(Stock::MEDIA_NEXT) + , arrowTool(Gtk::StockID("tool_arrow")) + , iBeamTool(Gtk::StockID("tool_i_beam")) + , zoomIn(Stock::ZOOM_IN) + , zoomOut(Stock::ZOOM_OUT) + , updatingToolbar(false) + , currentTool(timeline::Arrow) { // Hook up notifications get_project().get_sequences().signal_changed().connect(mem_fun(this, @@ -123,7 +124,7 @@ TimelinePanel::TimelinePanel(workspace::PanelManager &panel_manager, zoomOut .set_tooltip_text(_("Zoom out")); // Setup the timeline widget - shared_ptr<Sequence> sequence + shared_ptr<Sequence> sequence ///////////////////////////////TICKET #796 = *get_project().get_sequences().begin(); timelineWidget.reset(new TimelineWidget(load_state(sequence))); pack_start(*timelineWidget, PACK_EXPAND_WIDGET); @@ -132,13 +133,9 @@ TimelinePanel::TimelinePanel(workspace::PanelManager &panel_manager, update_sequence_chooser(); update_tool_buttons(); update_zoom_buttons(); - show_time(0); + show_time (Time::ZERO); } -TimelinePanel::~TimelinePanel() -{ - -} const char* TimelinePanel::get_title() @@ -203,9 +200,9 @@ TimelinePanel::on_zoom_out() } void -TimelinePanel::on_mouse_hover(gavl_time_t time) +TimelinePanel::on_mouse_hover(Time) { - (void)time; + /* do nothing */ } void @@ -216,8 +213,8 @@ TimelinePanel::on_playback_period_drag_released() REQUIRE(timelineWidget); - timelineWidget->get_state()->set_playback_point( - timelineWidget->get_state()->get_playback_period_start()); + timelineWidget->get_state()->setPlaybackPoint( + timelineWidget->get_state()->getPlaybackPeriodStart()); //----- END TEST CODE play(); @@ -365,27 +362,17 @@ TimelinePanel::set_tool(timeline::ToolType tool) } void -TimelinePanel::show_time(gavl_time_t time) +TimelinePanel::show_time(Time time) { - // timeIndicator.set_text(lumiera_tmpbuf_print_time(time)); + ////////////TODO integrate the Timecode Widget + + // timeIndicator.set_text(string(time)); } bool TimelinePanel::on_frame() { - // TEST CODE! - /*const gavl_time_t point = timelineWidget.get_playback_point() - + GAVL_TIME_SCALE / 25; - if(point < timelineWidget.get_playback_period_end()) - { - show_time(point); - timelineWidget.set_playback_point(point); - - - } - else - on_stop();*/ - + /////////TODO what happens here?? return true; } @@ -407,5 +394,4 @@ TimelinePanel::load_state(weak_ptr<Sequence> sequence) return shared_ptr< timeline::TimelineState >(); } -} // namespace panels -} // namespace gui +}} // namespace gui::panels diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp index 435599759..b65469bc9 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -29,9 +29,10 @@ #ifndef TIMELINE_PANEL_HPP #define TIMELINE_PANEL_HPP -#include "panel.hpp" +#include "gui/panels/panel.hpp" #include "gui/widgets/timecode-widget.hpp" #include "gui/widgets/timeline-widget.hpp" +#include "lib/time/timevalue.hpp" #include <boost/scoped_ptr.hpp> #include <boost/weak_ptr.hpp> @@ -47,6 +48,9 @@ class Sequence; namespace panels { +using lib::time::Time; + + /** * The definition of the timeline panel class, which holds timeline * widgets. @@ -55,17 +59,12 @@ class TimelinePanel : public Panel { public: /** - * Constructor. * @param panel_manager The owner panel manager widget. * @param dock_item The GdlDockItem that will host this panel. */ TimelinePanel(workspace::PanelManager &panel_manager, - GdlDockItem *dock_item); + GdlDockItem *dock_item); - /** - * Destructor - */ - ~TimelinePanel(); /** * Get the title of the panel. @@ -93,7 +92,7 @@ private: void on_time_pressed(); - void on_mouse_hover(gavl_time_t time); + void on_mouse_hover(Time); void on_playback_period_drag_released(); /** @@ -121,12 +120,12 @@ private: bool is_playing(); - void set_tool(gui::widgets::timeline::ToolType tool); + void set_tool (gui::widgets::timeline::ToolType tool); - void show_time(gavl_time_t time); + void show_time (Time); - boost::shared_ptr<widgets::timeline::TimelineState> load_state( - boost::weak_ptr<model::Sequence> sequence); + boost::shared_ptr<widgets::timeline::TimelineState> ///////////////////////////////TICKET #796 + load_state (boost::weak_ptr<model::Sequence> sequence); private: @@ -172,7 +171,7 @@ private: boost::scoped_ptr<TimelineWidget> timelineWidget; std::map< boost::weak_ptr<model::Sequence>, - boost::shared_ptr<widgets::timeline::TimelineState> > + boost::shared_ptr<widgets::timeline::TimelineState> > ///////////////////////////////TICKET #796 timelineStates; // Toolbar Widgets diff --git a/src/gui/widgets/timecode-widget.cpp b/src/gui/widgets/timecode-widget.cpp index fbf9c1eee..b46907e76 100644 --- a/src/gui/widgets/timecode-widget.cpp +++ b/src/gui/widgets/timecode-widget.cpp @@ -19,7 +19,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ +* *****************************************************/ + + +#include "timecode-widget.hpp" +#include "gui/util/convert.hpp" +#include "lib/time/diagnostics.hpp" ////////////TODO: temporary solution to get H:M:S components. Use TimeCode instead! #include <cmath> #include <stdint.h> @@ -30,9 +35,6 @@ #include <gavl/gavl.h> #include <sigc++/bind.h> -#include "timecode-widget.hpp" -#include "gui/util/convert.hpp" - using namespace sigc; using namespace Gtk; using namespace std; @@ -69,7 +71,7 @@ TimeCode::TimeCode(std::string clock_name, std::string widget_name, bool allow_e colon4(":"), colon5(":") { - last_when = Time(0); + last_when = Time::ZERO; last_pdelta = 0; last_sdelta = 0; key_entry_state = 0; @@ -423,8 +425,10 @@ TimeCode::set(Time when, bool force) void TimeCode::set_frames(Time when, bool force) { + ///////////////////////////TICKET #750 : integrate Timecode formats, let Digxel class do the formatting + char buf[32]; - snprintf(buf, sizeof(buf), "%u", (unsigned int)when); + snprintf(buf, sizeof(buf), "%u", uint(123)); audio_frames_label.set_text(buf); } @@ -432,10 +436,10 @@ void TimeCode::set_minsec(Time when, bool force) { char buf[32]; - - int hrs = when.getHours(); - int mins = when.getMins(); - float secs = when.getSecs(); + ////////////TICKET #750 : temporary solution to get H:M:S components. Use TimeCode instead! + int hrs = getHours(when); + int mins = getMins (when); + float secs = getSecs (when); if (force || hrs != ms_last_hrs) { @@ -463,11 +467,11 @@ void TimeCode::set_smpte(Time when, bool force) { char buf[32]; - + ////////////TICKET #750 : temporary solution to get H:M:S components. Use TimeCode instead! int smpte_negative = 0; // FIXME: when < 0; - int smpte_hours = when.getHours(); - int smpte_minutes = when.getMins(); - int smpte_seconds = when.getSecs(); + int smpte_hours = getHours(when); + int smpte_minutes = getMins(when); + int smpte_seconds = getSecs(when); int smpte_frames = 0; //when.getFrames(framerate); // if (is_duration) { @@ -1043,20 +1047,22 @@ TimeCode::field_motion_notify_event (GdkEventMotion *ev, Field field) if (trunc(drag_accum) != 0) { int frames; - Time pos; + TimeVar pos(current_time()); int dir; dir = (drag_accum < 0 ? 1:-1); - pos = current_time(); frames = get_frames(field,pos,dir); - - if (frames != 0 && frames * drag_accum < ((gavl_time_t)current_time())) + /////////////////////////////////////////////////////////////TICKET #750 : factor out all timecode calculations + ////////////// and concentrate them in lib/time/timecode.cpp + + if (frames != 0 && frames * drag_accum < (_raw(current_time()))) { // minus because up is negative in computer-land - set ((Time) floor (pos - drag_accum * frames), false); + pos = TimeValue (floor (pos - drag_accum * frames)); + set (pos, false); } else { - set (Time(0) , false); + set (Time::ZERO, false); } drag_accum = 0; @@ -1106,7 +1112,7 @@ TimeCode::get_frames(Field field, Time pos, int dir) Time TimeCode::current_time(Time pos) const { - Time ret = Time(0); + TimeVar ret; switch (_mode) { @@ -1132,7 +1138,7 @@ TimeCode::current_time(Time pos) const Time TimeCode::current_duration(Time pos) const { - Time ret = Time(0); + TimeVar ret; switch (_mode) { @@ -1202,7 +1208,7 @@ TimeCode::smpte_time_from_display() const // session->smpte_to_sample(smpte, sample, false /* use_offset */, false /* use_subframes */ ); - return Time(0); + return Time::ZERO; } Time @@ -1218,13 +1224,14 @@ TimeCode::minsec_time_from_display () const // return (Time) floor ((hrs * 60.0f * 60.0f * sr) + (mins * 60.0f * sr) + (secs * sr)); - return Time(0); + return Time::ZERO; } Time TimeCode::audio_time_from_display () const { - return Time((gavl_time_t) atoi (audio_frames_label.get_text())); + gavl_time_t parsedAudioFrames = atoi (audio_frames_label.get_text()); + return Time(TimeValue(parsedAudioFrames)); } void diff --git a/src/gui/widgets/timecode-widget.hpp b/src/gui/widgets/timecode-widget.hpp index beefa21ea..e5c15255e 100644 --- a/src/gui/widgets/timecode-widget.hpp +++ b/src/gui/widgets/timecode-widget.hpp @@ -25,6 +25,9 @@ #ifndef TIMECODE_WIDGET_HPP #define TIMECODE_WIDGET_HPP +#include "gui/gtk-base.hpp" +#include "lib/time/timevalue.hpp" + #include <string> #include <gtkmm/box.h> @@ -38,6 +41,11 @@ namespace gui { namespace widgets { +using lib::time::Time; +using lib::time::TimeVar; +using lib::time::TimeValue; + + class TimeCode : public Gtk::HBox { public: @@ -61,8 +69,8 @@ public: std::string name() const { return _name; } - Time current_time(Time position = Time(0)) const; - Time current_duration(Time position = Time(0)) const; + Time current_time(Time position = Time::ZERO) const; + Time current_duration(Time position = Time::ZERO) const; sigc::signal<void> ValueChanged; sigc::signal<void> ChangeAborted; @@ -129,7 +137,7 @@ private: Gtk::EventBox clock_base; Gtk::Frame clock_frame; - Time last_when; + TimeVar last_when; bool last_pdelta; bool last_sdelta; @@ -164,7 +172,7 @@ private: void set_minsec(Time, bool); void set_frames(Time, bool); - int get_frames(Field, Time pos = Time(0), int dir=1); + int get_frames(Field, Time pos = Time::ZERO, int dir=1); void smpte_sanitize_display(); Time smpte_time_from_display() const; diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index 0e1b975a9..a00882b7e 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -30,7 +30,9 @@ using namespace std; using namespace boost; using namespace util; using namespace gui::widgets::timeline; -using namespace lumiera; + +using lib::time::Time; +using lib::time::TimeValue; namespace gui { namespace widgets { @@ -158,7 +160,7 @@ TimelineWidget::get_hovering_track() const /* ===== Signals ===== */ -sigc::signal<void, lumiera::Time> +sigc::signal<void, Time> TimelineWidget::mouse_hover_signal() const { return mouseHoverSignal; @@ -188,8 +190,10 @@ void TimelineWidget::on_scroll() { if(state) - state->get_view_window().set_time_offset( - Time((gavl_time_t)horizontalAdjustment.get_value())); + { + TimeValue newStartOffset ((gavl_time_t)horizontalAdjustment.get_value()); + state->get_view_window().set_time_offset(Time(newStartOffset)); + } } void @@ -212,7 +216,7 @@ TimelineWidget::on_view_window_changed() horizontalAdjustment.set_page_size( window.get_time_scale() * view_width); - horizontalAdjustment.set_value(window.get_time_offset()); + horizontalAdjustment.set_value(_raw(window.get_time_offset())); } } diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index f27479d60..ca213c15b 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -23,30 +23,38 @@ ** This file contains the definition of timeline widget */ + #ifndef TIMELINE_WIDGET_HPP #define TIMELINE_WIDGET_HPP -#include "timeline/timeline-state.hpp" -#include "timeline/timeline-header-container.hpp" -#include "timeline/timeline-body.hpp" -#include "timeline/timeline-ruler.hpp" -#include "timeline/timeline-tool.hpp" -#include "timeline/timeline-arrow-tool.hpp" -#include "timeline/timeline-ibeam-tool.hpp" -#include "timeline/timeline-group-track.hpp" -#include "timeline/timeline-clip-track.hpp" -#include "timeline/timeline-layout-helper.hpp" +#include "gui/widgets/timeline/timeline-state.hpp" +#include "gui/widgets/timeline/timeline-header-container.hpp" +#include "gui/widgets/timeline/timeline-body.hpp" +#include "gui/widgets/timeline/timeline-ruler.hpp" +#include "gui/widgets/timeline/timeline-tool.hpp" +#include "gui/widgets/timeline/timeline-arrow-tool.hpp" +#include "gui/widgets/timeline/timeline-ibeam-tool.hpp" +#include "gui/widgets/timeline/timeline-group-track.hpp" +#include "gui/widgets/timeline/timeline-clip-track.hpp" +#include "gui/widgets/timeline/timeline-layout-helper.hpp" #include "gui/model/sequence.hpp" +#include "lib/time/timevalue.hpp" + + namespace gui { namespace widgets { + +using lib::time::Time; + /** * The namespace of all timeline widget helper classes. */ namespace timeline {} + /** * The timeline widget class. * @remarks This widget is a composite of several widgets contained @@ -107,7 +115,7 @@ public: public: /* ===== Signals ===== */ - sigc::signal<void, lumiera::Time> mouse_hover_signal() const; + sigc::signal<void, lib::time::Time> mouse_hover_signal() const; sigc::signal<void> playback_period_drag_released_signal() const; @@ -265,7 +273,7 @@ protected: Gtk::VScrollbar verticalScroll; // Signals - sigc::signal<void, lumiera::Time> mouseHoverSignal; + sigc::signal<void, Time> mouseHoverSignal; sigc::signal<void> playbackPeriodDragReleasedSignal; sigc::signal<void, boost::shared_ptr<timeline::Track> > hoveringTrackChangedSignal; diff --git a/src/gui/widgets/timeline/basic-draw-strategy.hpp b/src/gui/widgets/timeline/basic-draw-strategy.hpp index 511ae9808..165a0e3d3 100644 --- a/src/gui/widgets/timeline/basic-draw-strategy.hpp +++ b/src/gui/widgets/timeline/basic-draw-strategy.hpp @@ -19,32 +19,32 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *****************************************************/ + /** @file basic-draw-strategy.hpp ** Declares the Timeline Entity draw strategy class. */ -#ifndef TIMELINE_BASIC_DRAW_STRATEGY_HPP -#define TIMELINE_BASIC_DRAW_STRATEGY_HPP +#ifndef WIDGETS_TIMELINE_BASIC_DRAW_STRATEGY_H +#define WIDGETS_TIMELINE_BASIC_DRAW_STRATEGY_H -#include "draw-strategy.hpp" +#include "gui/widgets/timeline/draw-strategy.hpp" namespace gui { namespace widgets { namespace timeline { - class BasicDrawStrategy : public DrawStrategy - { - public: - - BasicDrawStrategy(); - - void draw(const Entity &entity, - Cairo::RefPtr<Cairo::Context> cr, - TimelineViewWindow* const window) const; - }; - -} // namespace timeline -} // namespace widgets -} // namespace gui + class BasicDrawStrategy + : public DrawStrategy + { + public: + + BasicDrawStrategy() { }; + + void + draw (const Entity &entity, + Cairo::RefPtr<Cairo::Context> cr, + TimelineViewWindow* const window) const; + }; +}}} // namespace gui::widgets::timeline #endif // TIMELINE_BASIC_DRAW_STRATEGY_HPP diff --git a/src/gui/widgets/timeline/draw-strategy.cpp b/src/gui/widgets/timeline/draw-strategy.cpp index 52967ced9..5fc505260 100644 --- a/src/gui/widgets/timeline/draw-strategy.cpp +++ b/src/gui/widgets/timeline/draw-strategy.cpp @@ -1,5 +1,5 @@ /* - basic-draw-strategy.cpp - Implementation of a basic draw strategy + DrawStrategy - Implementation of a basic draw strategy Copyright (C) Lumiera.org 2010, Stefan Kangas <skangas@skangas.se @@ -20,19 +20,20 @@ * *****************************************************/ -#include "basic-draw-strategy.hpp" + +#include "gui/widgets/timeline/basic-draw-strategy.hpp" namespace gui { namespace widgets { namespace timeline { - - BasicDrawStrategy::BasicDrawStrategy() - { } - + + DrawStrategy::~DrawStrategy() { } // emit VTable here + + void - BasicDrawStrategy::draw(const Entity &entity, - Cairo::RefPtr<Cairo::Context> cr, - TimelineViewWindow* const window) const + BasicDrawStrategy::draw (const Entity &entity, + Cairo::RefPtr<Cairo::Context> cr, + TimelineViewWindow* const window) const { REQUIRE (cr); REQUIRE (window); @@ -60,8 +61,7 @@ namespace timeline { cr->set_font_size (9); cr->show_text (entity.getName()); } - -} // namespace timeline -} // namespace widgets -} // namespace gui + + +}}} // namespace gui::widgets::timeline diff --git a/src/gui/widgets/timeline/draw-strategy.hpp b/src/gui/widgets/timeline/draw-strategy.hpp index 72eaa8828..612558230 100644 --- a/src/gui/widgets/timeline/draw-strategy.hpp +++ b/src/gui/widgets/timeline/draw-strategy.hpp @@ -19,40 +19,46 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *****************************************************/ + /** @file draw-strategy.hpp ** Declares the timeline entity drawing strategy interface. */ -#ifndef TIMELINE_DRAW_STRATEGY_HPP -#define TIMELINE_DRAW_STRATEGY_HPP +#ifndef WIDGETS_TIMELINE_DRAW_STRATEGY_HPP +#define WIDGETS_TIMELINE_DRAW_STRATEGY_HPP -#include "timeline-entity.hpp" -#include "timeline-view-window.hpp" +#include "gui/gtk-base.hpp" +#include "gui/widgets/timeline/timeline-entity.hpp" +#include "gui/widgets/timeline/timeline-view-window.hpp" namespace gui { namespace widgets { namespace timeline { - + + /////////TODO some questions: + ///////// 1) who is allowed to destroy DrawStrategy objects + ///////// 2) shouldn't DrawStragegy be boost::noncopyable? + + /** * An interface for drawing strategies for timeline entities. */ class DrawStrategy - { - protected: - - DrawStrategy() { } - - virtual ~DrawStrategy() { } - - public: - - virtual void draw(const Entity &entity, - Cairo::RefPtr<Cairo::Context> cr, - TimelineViewWindow* const window) const = 0; - }; - -} // namespace timeline -} // namespace widgets -} // namespace gui - + { + protected: + + DrawStrategy() { } + + virtual ~DrawStrategy(); + + public: + + virtual void + draw (const Entity &entity, + Cairo::RefPtr<Cairo::Context> cr, + TimelineViewWindow* const window) const = 0; + }; + + +}}} // namespace gui::widgets::timeline #endif // TIMELINE_DRAW_STRATEGY_HPP diff --git a/src/gui/widgets/timeline/timeline-arrow-tool.cpp b/src/gui/widgets/timeline/timeline-arrow-tool.cpp index f0c109189..dab5539e4 100644 --- a/src/gui/widgets/timeline/timeline-arrow-tool.cpp +++ b/src/gui/widgets/timeline/timeline-arrow-tool.cpp @@ -20,11 +20,15 @@ * *****************************************************/ -#include "timeline-arrow-tool.hpp" + +#include "gui/widgets/timeline/timeline-arrow-tool.hpp" namespace gui { namespace widgets { namespace timeline { + + using lib::time::Time; + ArrowTool::ArrowTool(TimelineBody &timelineBody) : Tool(timelineBody) @@ -53,8 +57,8 @@ namespace timeline { // Convert the mouse click position to a Time boost::shared_ptr<TimelineState> state = timelineBody.getTimelineWidget().get_state(); REQUIRE(state); - const TimelineViewWindow &window = state->get_view_window(); - lumiera::Time tpoint = window.x_to_time(mousePoint.get_x()); + TimelineViewWindow const& window = state->get_view_window(); + Time tpoint = window.x_to_time(mousePoint.get_x()); // Get the clip, if any boost::shared_ptr<timeline::Track> track = getHoveringTrack(); diff --git a/src/gui/widgets/timeline/timeline-arrow-tool.hpp b/src/gui/widgets/timeline/timeline-arrow-tool.hpp index c9b7decab..83abd77e7 100644 --- a/src/gui/widgets/timeline/timeline-arrow-tool.hpp +++ b/src/gui/widgets/timeline/timeline-arrow-tool.hpp @@ -19,20 +19,22 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /** @file timeline-arrow-tool.hpp ** This file contains the definition of the arrow tool class */ -#ifndef TIMELINE_ARROW_TOOL_HPP -#define TIMELINE_ARROW_TOOL_HPP +#ifndef WIDGETS_TIMELINE_ARROW_TOOL_H +#define WIDGETS_TIMELINE_ARROW_TOOL_H -#include <gtkmm.h> - -#include "timeline-tool.hpp" +#include "gui/gtk-base.hpp" #include "gui/widgets/timeline-widget.hpp" -#include "timeline-body.hpp" -#include "timeline-track.hpp" +#include "gui/widgets/timeline/timeline-tool.hpp" +#include "gui/widgets/timeline/timeline-body.hpp" +#include "gui/widgets/timeline/timeline-track.hpp" + +#include "lib/time/timevalue.hpp" namespace gui { namespace widgets { diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index 431c7d44b..7f78d0929 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -43,15 +43,15 @@ namespace gui { namespace widgets { namespace timeline { -TimelineBody::TimelineBody(TimelineWidget &timelineWidget) : - Glib::ObjectBase("TimelineBody"), - tool(NULL), - mouseDownX(0), - mouseDownY(0), - dragType(None), - beginShiftTimeOffset(0), - selectionAlpha(0.5), - timelineWidget(timelineWidget) +TimelineBody::TimelineBody (TimelineWidget &timelineWidget) + : Glib::ObjectBase("TimelineBody") + , tool(NULL) + , mouseDownX(0) + , mouseDownY(0) + , dragType(None) + , beginShiftTimeOffset() + , selectionAlpha(0.5) + , timelineWidget(timelineWidget) { // Connect up some events timelineWidget.state_changed_signal().connect( @@ -264,10 +264,11 @@ 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! + ///////////////////////////// : either encapsulate this entirely here, or leave it to the timeline view! const int64_t scale = window.get_time_scale(); - Time offset(beginShiftTimeOffset + - (int64_t)(mouseDownX - event->x) * scale); - window.set_time_offset(offset); + window.set_time_offset(beginShiftTimeOffset + + TimeValue(scale * (mouseDownX - event->x))); set_vertical_offset((int)(mouseDownY - event->y) + beginShiftVerticalOffset); @@ -388,9 +389,9 @@ TimelineBody::draw_selection(Cairo::RefPtr<Cairo::Context> cr) shared_ptr<TimelineState> state = timelineWidget.get_state(); REQUIRE(state); - const TimelineViewWindow &window = state->get_view_window(); - const int start_x = window.time_to_x(state->get_selection_start()); - const int end_x = window.time_to_x(state->get_selection_end()); + 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()); // Draw the cover if(end_x > 0 && start_x < allocation.get_width()) @@ -431,12 +432,10 @@ TimelineBody::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr) shared_ptr<TimelineState> state = timelineWidget.get_state(); if(state) { - const Allocation allocation = get_allocation(); - - const gavl_time_t point = state->get_playback_point(); - if(point == (gavl_time_t)GAVL_TIME_UNDEFINED) - return; + if (!state->isPlaying()) return; + const Allocation allocation = get_allocation(); + Time point = state->getPlaybackPoint(); const int x = view_window().time_to_x(point); // Set source diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index 72dc374b2..74b057738 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -19,15 +19,17 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /** @file timeline-body.hpp ** This file contains the definition of timeline body widget */ -#ifndef TIMELINE_BODY_HPP -#define TIMELINE_BODY_HPP +#ifndef WIDGETS_TIMELINE_BODY_H +#define WIDGETS_TIMELINE_BODY_H #include "gui/gtk-lumiera.hpp" -#include "timeline-tool.hpp" +#include "gui/widgets/timeline/timeline-tool.hpp" +#include "lib/time/timevalue.hpp" #include <boost/scoped_ptr.hpp> @@ -43,9 +45,13 @@ class TimelineWidget; namespace timeline { +using lib::time::TimeVar; + + class Track; class TimelineViewWindow; + /** * Implementation of the timeline body subwidget. This widget is * displayed in the centre of the timeline widget, and displays the @@ -186,7 +192,7 @@ private: // Scroll State DragType dragType; - gavl_time_t beginShiftTimeOffset; + TimeVar beginShiftTimeOffset; int beginShiftVerticalOffset; // Style properties diff --git a/src/gui/widgets/timeline/timeline-clip-track.cpp b/src/gui/widgets/timeline/timeline-clip-track.cpp index b07b3ee1c..39d254a48 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.cpp +++ b/src/gui/widgets/timeline/timeline-clip-track.cpp @@ -57,8 +57,8 @@ namespace timeline { REQUIRE (window); // Draw a rectangle to let us know it works? :-) - cairo->rectangle(window->time_to_x(0), 1, - window->time_to_x(500000) - window->time_to_x(0), + cairo->rectangle(window->time_to_x(Time::ZERO), 1, + window->time_to_x(Time(500,0)) - window->time_to_x(Time::ZERO), get_height() - 2); cairo->set_source_rgb(0.5, 0.5, 0.5); @@ -77,13 +77,13 @@ namespace timeline { } boost::shared_ptr<timeline::Clip> - ClipTrack::getClipAt(lumiera::Time position) const + ClipTrack::getClipAt(Time position) const { std::pair<shared_ptr<model::Clip>, shared_ptr<timeline::Clip> > pair; BOOST_FOREACH (pair, clipMap) { - if (pair.first->isPlayingAt(position)) + if (pair.first->isPlayingAt (position)) return pair.second; } diff --git a/src/gui/widgets/timeline/timeline-clip-track.hpp b/src/gui/widgets/timeline/timeline-clip-track.hpp index 461d71876..1265c913b 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.hpp +++ b/src/gui/widgets/timeline/timeline-clip-track.hpp @@ -28,19 +28,23 @@ #ifndef TIMELINE_CLIP_TRACK_HPP #define TIMELINE_CLIP_TRACK_HPP -#include <vector> - -#include "basic-draw-strategy.hpp" -#include "timeline-track.hpp" +#include "gui/widgets/timeline/basic-draw-strategy.hpp" +#include "gui/widgets/timeline/timeline-track.hpp" #include "gui/model/clip-track.hpp" +#include "lib/time/timevalue.hpp" + +#include <vector> namespace gui { namespace widgets { namespace timeline { - + + using lib::time::Time; + class Clip; class TimelineViewWindow; + class ClipTrack : public timeline::Track { public: @@ -63,7 +67,7 @@ namespace timeline { * @param the given time */ boost::shared_ptr<timeline::Clip> - getClipAt(lumiera::Time position) const; + getClipAt(Time position) const; private: diff --git a/src/gui/widgets/timeline/timeline-clip.cpp b/src/gui/widgets/timeline/timeline-clip.cpp index 001964ae1..f2328bdc5 100644 --- a/src/gui/widgets/timeline/timeline-clip.cpp +++ b/src/gui/widgets/timeline/timeline-clip.cpp @@ -20,17 +20,21 @@ * *****************************************************/ -#include "timeline-clip.hpp" + +#include "gui/widgets/timeline/timeline-clip.hpp" namespace gui { namespace widgets { namespace timeline { + + using boost::shared_ptr; //////////////////////TICKET #796 + - Clip::Clip(boost::shared_ptr<model::Clip> clip, - boost::shared_ptr<timeline::DrawStrategy> drawStrategy) - : Entity(drawStrategy), - modelClip(clip), - selected(false) + Clip::Clip (boost::shared_ptr<model::Clip> clip, + boost::shared_ptr<timeline::DrawStrategy> drawStrategy) + : Entity(drawStrategy) + , modelClip(clip) + , selected(false) { REQUIRE(modelClip); @@ -39,35 +43,33 @@ namespace timeline { // &Clip::onNameChanged); } - gavl_time_t - Clip::getBegin () const + Time + Clip::getBegin() const { REQUIRE (modelClip); return modelClip->getBegin(); } - gavl_time_t - Clip::getEnd () const + Time + Clip::getEnd() const { REQUIRE (modelClip); return modelClip->getEnd(); } - std::string - Clip::getName () const + string + Clip::getName() const { REQUIRE (modelClip); return modelClip->getName(); } void - Clip::setSelected(bool selected) + Clip::setSelected (bool selected) { this->selected = selected; } -} // namespace timeline -} // namespace widgets -} // namespace gui +}}} // namespace gui::widgets::timeline diff --git a/src/gui/widgets/timeline/timeline-clip.hpp b/src/gui/widgets/timeline/timeline-clip.hpp index f3b8c0151..2d6d83139 100644 --- a/src/gui/widgets/timeline/timeline-clip.hpp +++ b/src/gui/widgets/timeline/timeline-clip.hpp @@ -19,57 +19,57 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /** @file widgets/timeline/timeline-clip.hpp ** This file contains the definition of timeline clip object */ +#ifndef WIDGETS_TIMELINE_CLIP_H +#define WIDGETS_TIMELINE_CLIP_H + #include "gui/gtk-lumiera.hpp" #include "gui/model/clip.hpp" #include "include/logging.h" +#include "lib/time/timevalue.hpp" -#include "draw-strategy.hpp" -#include "timeline-entity.hpp" +#include "gui/widgets/timeline/draw-strategy.hpp" +#include "gui/widgets/timeline/timeline-entity.hpp" + +#include <string> -#ifndef TIMELINE_CLIP_HPP -#define TIMELINE_CLIP_HPP namespace gui { namespace widgets { namespace timeline { + + using std::string; + using lib::time::Time; + class Clip : public Entity - { - public: - Clip(boost::shared_ptr<model::Clip> clip, - boost::shared_ptr<timeline::DrawStrategy> drawStrategy); - - gavl_time_t - getBegin () const; - - gavl_time_t - getEnd () const; - - std::string - getName () const; - - /** - * Sets the selected status of the clip. - */ - void - setSelected (bool state); - - private: - - boost::shared_ptr<model::Clip> modelClip; - - /** - * True when this clip is selected in the GUI. - */ - bool selected; - }; - -} // namespace timeline -} // namespace widgets -} // namespace gui - + { + public: + Clip (boost::shared_ptr<model::Clip> clip, + boost::shared_ptr<timeline::DrawStrategy> drawStrategy); + + + Time getBegin() const; + Time getEnd() const; + string getName() const; + + /** Sets the selected status of the clip. */ + void setSelected (bool); + + private: + + boost::shared_ptr<model::Clip> modelClip; ////////////////////////TICKET #796 : should use std::tr1 + + /** + * True when this clip is selected in the GUI. + */ + bool selected; + }; + + +}}} // namespace gui::widgets::timeline #endif // TIMELINE_CLIP_HPP diff --git a/src/gui/widgets/timeline/timeline-entity.cpp b/src/gui/widgets/timeline/timeline-entity.cpp index 0514dafd4..13dc398aa 100644 --- a/src/gui/widgets/timeline/timeline-entity.cpp +++ b/src/gui/widgets/timeline/timeline-entity.cpp @@ -20,12 +20,10 @@ * *****************************************************/ -#include "timeline-entity.hpp" - -#include "draw-strategy.hpp" #include "gui/gtk-lumiera.hpp" -#include "include/logging.h" +#include "gui/widgets/timeline/timeline-entity.hpp" +#include "gui/widgets/timeline/draw-strategy.hpp" namespace gui { namespace widgets { diff --git a/src/gui/widgets/timeline/timeline-entity.hpp b/src/gui/widgets/timeline/timeline-entity.hpp index fc1e8e345..a6a03b5da 100644 --- a/src/gui/widgets/timeline/timeline-entity.hpp +++ b/src/gui/widgets/timeline/timeline-entity.hpp @@ -19,6 +19,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *****************************************************/ + /** @file timeline-entity.hpp ** Declares the Timeline Entity class. */ @@ -26,21 +27,20 @@ #ifndef TIMELINE_ENTITY_HPP #define TIMELINE_ENTITY_HPP +#include "gui/gtk-base.hpp" +#include "lib/time/timevalue.hpp" + #include <string> - -// TODO: Remove once we get better measure of duration. -extern "C" { -#include <stdint.h> -#include <gavl/gavltime.h> -} - -#include <boost/shared_ptr.hpp> #include <cairomm/cairomm.h> +#include <boost/shared_ptr.hpp> //////////////////////TICKET #796 namespace gui { namespace widgets { namespace timeline { - + + using lib::time::Time; + + class DrawStrategy; class TimelineViewWindow; @@ -57,8 +57,6 @@ namespace timeline { public: - virtual gavl_time_t - getBegin () const = 0; virtual void draw(Cairo::RefPtr<Cairo::Context> cairo, @@ -67,7 +65,10 @@ namespace timeline { bool getEnabled () const; - virtual gavl_time_t + virtual Time + getBegin () const = 0; + + virtual Time getEnd () const = 0; virtual std::string diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp index 99e263ed7..6a858aa18 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp @@ -20,12 +20,16 @@ * *****************************************************/ -#include "timeline-ibeam-tool.hpp" +#include "gui/widgets/timeline/timeline-ibeam-tool.hpp" #include "gui/widgets/timeline-widget.hpp" +#include "lib/time/mutation.hpp" + +#include <boost/shared_ptr.hpp> -using namespace boost; using namespace gui::widgets; -using namespace lumiera; + +using lib::time::Mutation; +using boost::shared_ptr; /////////////////////////TICKET #796 namespace gui { namespace widgets { @@ -42,7 +46,7 @@ const int IBeamTool::ScrollSlideEventInterval = 40; IBeamTool::IBeamTool(TimelineBody &timeline_body) : Tool(timeline_body), dragType(None), - pinnedDragTime(0), + pinnedDragTime(), scrollSlideRate(0) { @@ -103,20 +107,22 @@ IBeamTool::on_button_press_event(GdkEventButton* event) { // User began to drag the start of the selection dragType = GrabStart; - pinnedDragTime = state->get_selection_end(); + pinnedDragTime = state->getSelectionEnd(); } else if(is_mouse_in_end_drag_zone()) { // User began to drag the end of the selection dragType = GrabEnd; - pinnedDragTime = state->get_selection_start(); + pinnedDragTime = state->getSelectionStart(); } else { // User began the drag in clear space, begin a Select drag dragType = Selection; pinnedDragTime = time; - state->set_selection(time, time); + state->setSelection (Mutation::changeTime(time)); + state->setSelection (Mutation::changeDuration(Duration::NIL)); + //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all } } } @@ -188,11 +194,15 @@ IBeamTool::set_leading_x(const int x) shared_ptr<TimelineState> state = get_state(); const bool set_playback_period = dragType == Selection; - const Time time = state->get_view_window().x_to_time(x); - if(time > pinnedDragTime) - state->set_selection(pinnedDragTime, time, set_playback_period); - else - state->set_selection(time, pinnedDragTime, set_playback_period); + TimeVar newStartPoint (state->get_view_window().x_to_time(x)); + Offset selectionLength (pinnedDragTime, newStartPoint); + + if (newStartPoint > pinnedDragTime) + newStartPoint=pinnedDragTime; // use the smaller one as selection start + + state->setSelection (Mutation::changeTime(newStartPoint) , set_playback_period); + state->setSelection (Mutation::changeDuration(selectionLength), set_playback_period); + //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once } void @@ -217,7 +227,7 @@ bool IBeamTool::is_mouse_in_start_drag_zone() const { const int start_x = view_window().time_to_x( - get_state()->get_selection_start()); + get_state()->getSelectionStart()); return (mousePoint.get_x() <= start_x && mousePoint.get_x() > start_x - DragZoneWidth); @@ -227,7 +237,7 @@ bool IBeamTool::is_mouse_in_end_drag_zone() const { const int end_x = view_window().time_to_x( - get_state()->get_selection_end()); + get_state()->getSelectionEnd()); return (mousePoint.get_x() >= end_x && mousePoint.get_x() < end_x + DragZoneWidth); diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.hpp b/src/gui/widgets/timeline/timeline-ibeam-tool.hpp index 219a6dd49..291305cde 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.hpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.hpp @@ -19,21 +19,26 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /** @file timeline-ibeam-tool.hpp ** This file contains the definition of ibeam tool class ** tool objects */ -#ifndef TIMELINE_IBEAM_TOOL_HPP -#define TIMELINE_IBEAM_TOOL_HPP +#ifndef WIDGETS_TIMELINE_IBEAM_TOOL_H +#define WIDGETS_TIMELINE_IBEAM_TOOL_H + +#include "gui/widgets/timeline/timeline-tool.hpp" +#include "lib/time/timevalue.hpp" #include <gtkmm.h> -#include "timeline-tool.hpp" namespace gui { namespace widgets { namespace timeline { +using lib::time::TimeVar; + /** * A helper class to implement the timeline i-beam tool */ @@ -164,7 +169,7 @@ private: * the mouse, the other is pinned. pinnedDragTime specifies the time * of that point. */ - lumiera::Time pinnedDragTime; + TimeVar pinnedDragTime; /** * This connection is used to represent the timer which causes scroll diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp index 6c79d6af7..afc395710 100644 --- a/src/gui/widgets/timeline/timeline-ruler.cpp +++ b/src/gui/widgets/timeline/timeline-ruler.cpp @@ -1,5 +1,5 @@ /* - timeline-ruler.cpp - Implementation of the time ruler widget + TimelineRuler - Implementation of the time ruler widget Copyright (C) Lumiera.org 2008, Joel Holdsworth <joel@airwebreathe.org.uk> @@ -20,51 +20,52 @@ * *****************************************************/ -#include <cairomm/cairomm.h> -#include "timeline-ruler.hpp" +#include "gui/widgets/timeline/timeline-ruler.hpp" #include "gui/widgets/timeline-widget.hpp" #include "gui/window-manager.hpp" #include "gui/util/cairo-util.hpp" - +#include "lib/time/timevalue.hpp" #include "lib/time.h" +#include <cairomm/cairomm.h> using namespace Gtk; using namespace Cairo; -using namespace std; -using namespace boost; using namespace gui; using namespace gui::widgets; using namespace gui::widgets::timeline; -using namespace lumiera; +using boost::shared_ptr; ////////////////////TICKET #796 using gui::util::CairoUtil; +using lib::time::Time; +using lib::time::TimeVar; + namespace gui { namespace widgets { namespace timeline { -TimelineRuler::TimelineRuler( - gui::widgets::TimelineWidget &timeline_widget) : - Glib::ObjectBase("TimelineRuler"), - isDragging(false), - pinnedDragTime(0), - mouseChevronOffset(0), - annotationHorzMargin(0), - annotationVertMargin(0), - majorTickHeight(0), - minorLongTickHeight(0), - minorShortTickHeight(0), - minDivisionWidth(100), - mouseChevronSize(5), - selectionChevronSize(5), - playbackPointAlpha(0.5f), - playbackPointSize(12), - playbackPeriodArrowAlpha(0.5f), - playbackPeriodArrowSize(10), - playbackPeriodArrowStemSize(3), - timelineWidget(timeline_widget) + +TimelineRuler::TimelineRuler (TimelineWidget &timeline_widget) + : Glib::ObjectBase("TimelineRuler") + , isDragging(false) + , pinnedDragTime(Time::ZERO) + , mouseChevronOffset(0) + , annotationHorzMargin(0) + , annotationVertMargin(0) + , majorTickHeight(0) + , minorLongTickHeight(0) + , minorShortTickHeight(0) + , minDivisionWidth(100) + , mouseChevronSize(5) + , selectionChevronSize(5) + , playbackPointAlpha(0.5f) + , playbackPointSize(12) + , playbackPeriodArrowAlpha(0.5f) + , playbackPeriodArrowSize(10) + , playbackPeriodArrowStemSize(3) + , timelineWidget(timeline_widget) { // Connect up some events timeline_widget.state_changed_signal().connect( @@ -238,11 +239,16 @@ TimelineRuler::set_leading_x(const int x) if(state) { - const Time time = view_window().x_to_time(x); - if(time > pinnedDragTime) - state->set_playback_period(pinnedDragTime, time); - else - state->set_playback_period(time, pinnedDragTime); + TimeVar newStartPoint (view_window().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)); + //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once + ////////////////////TODO : code duplication with timeline-ibeam-tool 205 } } @@ -255,7 +261,7 @@ TimelineRuler::draw_ruler(Cairo::RefPtr<Cairo::Context> cr, REQUIRE(ruler_rect.get_height() > 0); const TimelineViewWindow &window = view_window(); - const gavl_time_t left_offset = window.get_time_offset(); + const gavl_time_t left_offset = _raw(window.get_time_offset()); const int64_t time_scale = window.get_time_scale(); // Preparation steps @@ -301,7 +307,7 @@ TimelineRuler::draw_ruler(Cairo::RefPtr<Cairo::Context> cr, cr->stroke(); // Draw the text - pango_layout->set_text(lumiera_tmpbuf_print_time(time_offset)); + pango_layout->set_text(lumiera_tmpbuf_print_time(time_offset)); ///////////////TICKET #750 : should delegate to a Timecode format here cr->move_to(annotationHorzMargin + x, annotationVertMargin); pango_layout->add_to_cairo_context(cr); cr->fill(); @@ -364,7 +370,7 @@ TimelineRuler::draw_selection(Cairo::RefPtr<Cairo::Context> cr, Gdk::Cairo::set_source_color(cr, style->get_fg(STATE_NORMAL)); // Draw the selection start chevron - const int a = window.time_to_x(state->get_selection_start()) + 1; + const int a = 1 + window.time_to_x(state->getSelectionStart()); if(a >= 0 && a < ruler_rect.get_width()) { cr->move_to(a, ruler_rect.get_height()); @@ -374,7 +380,7 @@ TimelineRuler::draw_selection(Cairo::RefPtr<Cairo::Context> cr, } // Draw the selection end chevron - const int b = window.time_to_x(state->get_selection_end()); + const int b = window.time_to_x(state->getSelectionEnd()); if(b >= 0 && b < ruler_rect.get_width()) { cr->move_to(b, ruler_rect.get_height()); @@ -399,22 +405,18 @@ TimelineRuler::draw_playback_period(Cairo::RefPtr<Cairo::Context> cr, // Calculate coordinates const float halfSize = playbackPeriodArrowSize / 2; - const float a = window.time_to_x( - state->get_playback_period_start()) + 1 + 0.5f; + const float a = 1.5f + window.time_to_x(state->getPlaybackPeriodStart()); + const float b = a + halfSize; - const float d = window.time_to_x( - state->get_playback_period_end()) + 0.5f; + const float d = 0.5f + window.time_to_x(state->getPlaybackPeriodEnd()); + const float c = d - halfSize; - const float e = ruler_rect.get_height() - playbackPeriodArrowSize - - 0.5f; - const float f = e + (playbackPeriodArrowSize - - playbackPeriodArrowStemSize) / 2; - const float g = ruler_rect.get_height() - playbackPeriodArrowSize / 2 - - 0.5f; + const float e = ruler_rect.get_height() - playbackPeriodArrowSize - 0.5f; + const float f = e + (playbackPeriodArrowSize - playbackPeriodArrowStemSize) / 2; + const float g = ruler_rect.get_height() - playbackPeriodArrowSize / 2 - 0.5f; const float i = ruler_rect.get_height() - 0.5f; - const float h = i - (playbackPeriodArrowSize - - playbackPeriodArrowStemSize) / 2; + const float h = i - (playbackPeriodArrowSize - playbackPeriodArrowStemSize) / 2; // Contruct the path if(d - a >= playbackPeriodArrowSize) @@ -466,11 +468,10 @@ TimelineRuler::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr, shared_ptr<TimelineState> state = timelineWidget.get_state(); REQUIRE(state); - const TimelineViewWindow &window = state->get_view_window(); + if (!state->isPlaying()) return; - const gavl_time_t point = state->get_playback_point(); - if(point == (gavl_time_t)GAVL_TIME_UNDEFINED) - return; + TimelineViewWindow const& window = state->get_view_window(); + Time point = state->getPlaybackPoint(); const int x = window.time_to_x(point); cr->move_to(x + 0.5, ruler_rect.get_height()); diff --git a/src/gui/widgets/timeline/timeline-ruler.hpp b/src/gui/widgets/timeline/timeline-ruler.hpp index fe50669b9..a7c9b0533 100644 --- a/src/gui/widgets/timeline/timeline-ruler.hpp +++ b/src/gui/widgets/timeline/timeline-ruler.hpp @@ -28,6 +28,7 @@ #define TIMELINE_RULER_HPP #include "gui/gtk-lumiera.hpp" +#include "lib/time/timevalue.hpp" namespace gui { namespace widgets { @@ -198,7 +199,7 @@ private: * the mouse, the other is pinned. pinnedDragTime specifies the time * of that point. */ - lumiera::Time pinnedDragTime; + lib::time::TimeVar pinnedDragTime; // Indicated values /** diff --git a/src/gui/widgets/timeline/timeline-state.cpp b/src/gui/widgets/timeline/timeline-state.cpp index f8e712169..2a8f311e4 100644 --- a/src/gui/widgets/timeline/timeline-state.cpp +++ b/src/gui/widgets/timeline/timeline-state.cpp @@ -20,31 +20,43 @@ * *****************************************************/ -#include "timeline-state.hpp" -using namespace boost; +#include "gui/widgets/timeline/timeline-state.hpp" +#include "lib/time/timevalue.hpp" +#include "lib/time/mutation.hpp" + using namespace Gtk; using namespace sigc; -using namespace lumiera; namespace gui { namespace widgets { namespace timeline { -TimelineState::TimelineState( - boost::shared_ptr<model::Sequence> source_sequence) : - sequence(source_sequence), - viewWindow(Time(0), 1), - selectionStart(0), - selectionEnd(0), - playbackPeriodStart(0), - playbackPeriodEnd(0), - playbackPoint(Time(GAVL_TIME_UNDEFINED)) +using lib::time::FSecs; +using lib::time::Offset; +using lib::time::Duration; +using lib::time::Mutation; +using boost::shared_ptr; /////////////////////////TICKET #796 + + + +TimelineState::TimelineState (boost::shared_ptr<model::Sequence> source_sequence) + : sequence(source_sequence) + , viewWindow(Offset(Time::ZERO), 1) + , selection_(Time::ZERO, Duration::NIL) + , playbackPeriod_(Time::ZERO, Duration::NIL) + , playbackPoint_(Time::ZERO) + , isPlayback_(false) { REQUIRE(sequence); + ////////////////////////////////////////////////////////////TICKET #798: how to handle GUI default state + viewWindow.set_time_scale(GAVL_TIME_SCALE / 200); - set_selection(Time(2000000), Time(4000000)); + + setSelection (Mutation::changeTime (Time(FSecs(2)))); + setSelection (Mutation::changeDuration(Duration(FSecs(2)))); + //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all } boost::shared_ptr<model::Sequence> @@ -59,87 +71,31 @@ TimelineState::get_view_window() return viewWindow; } -Time -TimelineState::get_selection_start() const -{ - return selectionStart; -} - -Time -TimelineState::get_selection_end() const -{ - return selectionEnd; -} - void -TimelineState::set_selection(Time start, Time end, - bool reset_playback_period) -{ - if(start < end) - { - selectionStart = start; - selectionEnd = end; - } - else - { - // The selection is back-to-front, flip it round - selectionStart = end; - selectionEnd = start; - } - - if(reset_playback_period) - { - playbackPeriodStart = selectionStart; - playbackPeriodEnd = selectionEnd; - } - +TimelineState::setSelection (Mutation const& change, + bool resetPlaybackPeriod) +{ + selection_.accept (change); + if (resetPlaybackPeriod) + setPlaybackPeriod(change); + selectionChangedSignal.emit(); } - -Time -TimelineState::get_playback_period_start() const -{ - return playbackPeriodStart; -} - -Time -TimelineState::get_playback_period_end() const -{ - return playbackPeriodEnd; -} void -TimelineState::set_playback_period(Time start, Time end) +TimelineState::setPlaybackPeriod (Mutation const& change) { - - if(start < end) - { - playbackPeriodStart = start; - playbackPeriodEnd = end; - } - else - { - // The period is back-to-front, flip it round - playbackPeriodStart = end; - playbackPeriodEnd = start; - } - + playbackPeriod_.accept (change); playbackChangedSignal.emit(); } void -TimelineState::set_playback_point(Time point) +TimelineState::setPlaybackPoint (Time newPosition) { - playbackPoint = point; + playbackPoint_ = newPosition; playbackChangedSignal.emit(); } -Time -TimelineState::get_playback_point() const -{ - return playbackPoint; -} - sigc::signal<void> TimelineState::selection_changed_signal() const { diff --git a/src/gui/widgets/timeline/timeline-state.hpp b/src/gui/widgets/timeline/timeline-state.hpp index 22fb12cfd..47fb583fb 100644 --- a/src/gui/widgets/timeline/timeline-state.hpp +++ b/src/gui/widgets/timeline/timeline-state.hpp @@ -26,7 +26,10 @@ #ifndef TIMELINE_STATE_HPP #define TIMELINE_STATE_HPP -#include "timeline-view-window.hpp" +#include "gui/widgets/timeline/timeline-view-window.hpp" +#include "lib/time/mutation.hpp" + +#include <boost/shared_ptr.hpp> /////////////////////////TICKET #796 namespace gui { @@ -36,6 +39,8 @@ class Sequence; namespace widgets { namespace timeline { + +using lib::time::Mutation; /** * TimelineState is a container for the state data for TimelineWidget. @@ -67,56 +72,37 @@ public: */ timeline::TimelineViewWindow& get_view_window(); - /** - * Gets the time at which the selection begins. - */ - lumiera::Time get_selection_start() const; - /** - * Gets the time at which the selection begins. - */ - lumiera::Time get_selection_end() const; + Time getSelectionStart() const { return selection_.start();} + Time getSelectionEnd() const { return selection_.end(); } + Time getPlaybackPeriodStart() const { return selection_.start();} + Time getPlaybackPeriodEnd() const { return selection_.end(); } + + Time getPlaybackPoint() const { return playbackPoint_; } + + /** is there currently any ongoing playback process? + * Otherwise the #getPlaybackPoint is meaningless */ + bool isPlaying() const { return isPlayback_; } + /** * Sets the period of the selection. - * @param start The start time. - * @param end The end time. - * @param reset_playback_period Specifies whether to set the playback - * period to the same as this new selection. + * @param resetPlaybackPeriod Specifies whether to set the + * playback period to the same as this new selection. */ - void set_selection(lumiera::Time start, lumiera::Time end, - bool reset_playback_period = true); + void setSelection(Mutation const& change, + bool resetPlaybackPeriod = true); + + void setPlaybackPeriod(Mutation const& change); /** - * Gets the time at which the playback period begins. + * Sets the time which is currently being played back. + * @param point The time index being played. + * @todo do we ever get the situation that we don't have such a position? + * @todo very likely to be handled differently, once + * the GUI is really connected to the Player */ - lumiera::Time get_playback_period_start() const; - - /** - * Gets the time at which the playback period ends. - */ - lumiera::Time get_playback_period_end() const; - - /** - * Sets the playback period. - * @param start The start time. - * @param end The end time. - */ - void set_playback_period(lumiera::Time start, lumiera::Time end); - - /** - * Sets the time which is currenty being played back. - * @param point The time index being played. This value may be - * GAVL_TIME_UNDEFINED, if there is no playback point. - */ - void set_playback_point(lumiera::Time point); - - /** - * Gets the current playback point. - * @return The time index of the playback point. This value may be - * GAVL_TIME_UNDEFINED, if there is no playback point. - */ - lumiera::Time get_playback_point() const; + void setPlaybackPoint(Time newPos); /** * A signal to notify when the selected period has changed. @@ -132,8 +118,7 @@ public: private: /** - * A pointer to the sequence object which this timeline_widget will - * represent. + * A pointer to the sequence object which this timeline_widget will represent. * @remarks This pointer is set by the constructor and is constant, so * will not change in value during the lifetime of the class. */ @@ -147,30 +132,20 @@ private: // Selection State - /** - * The start time of the selection period. - */ - lumiera::Time selectionStart; + /** currently selected time period. */ + TimeSpan selection_; - /** - * The end time of the selection period. - */ - lumiera::Time selectionEnd; + /** current playback period. */ + TimeSpan playbackPeriod_; - /** - * The start time of the playback period. + /** current playback position. + * @todo very likely to be handled differently + * when actually integrated with the Player */ - lumiera::Time playbackPeriodStart; + TimeVar playbackPoint_; - /** - * The end time of the playback period. - */ - lumiera::Time playbackPeriodEnd; + bool isPlayback_; - /** - * The time of the playback point. - */ - lumiera::Time playbackPoint; // Signals diff --git a/src/gui/widgets/timeline/timeline-track.cpp b/src/gui/widgets/timeline/timeline-track.cpp index 2ac324f1f..0a86daf2d 100644 --- a/src/gui/widgets/timeline/timeline-track.cpp +++ b/src/gui/widgets/timeline/timeline-track.cpp @@ -26,10 +26,11 @@ #include "gui/dialogs/name-chooser.hpp" #include "include/logging.h" -using namespace boost; using namespace Gtk; using namespace sigc; +using boost::shared_ptr; //////////////////////////////TICKET #796 : should use std::tr1 + namespace gui { namespace widgets { namespace timeline { @@ -120,8 +121,8 @@ Track::get_expanded() const return expanded; } -boost::shared_ptr<timeline::Clip> -Track::getClipAt(lumiera::Time) const +shared_ptr<timeline::Clip> +Track::getClipAt(Time) const { // Default implementation returns empty pointer return boost::shared_ptr<timeline::Clip>(); diff --git a/src/gui/widgets/timeline/timeline-track.hpp b/src/gui/widgets/timeline/timeline-track.hpp index a45bf5ab4..79b7dec98 100644 --- a/src/gui/widgets/timeline/timeline-track.hpp +++ b/src/gui/widgets/timeline/timeline-track.hpp @@ -38,6 +38,7 @@ #include "gui/widgets/timeline/timeline-clip.hpp" #include "gui/widgets/timeline/timeline-header-container.hpp" #include "gui/widgets/timeline/timeline-header-widget.hpp" +#include "lib/time/timevalue.hpp" #include <boost/scoped_ptr.hpp> @@ -45,6 +46,8 @@ namespace gui { namespace widgets { namespace timeline { + +using lib::time::Time; class TimelineViewWindow; @@ -82,7 +85,7 @@ public: Gtk::Widget& get_header_widget(); - boost::shared_ptr<model::Track> + boost::shared_ptr<model::Track> //////////////////////////////TICKET #796 : should use std::tr1 getModelTrack() const; /** @@ -152,8 +155,8 @@ public: * The default implementation simply returns an empty pointer. * @param the given time */ - virtual boost::shared_ptr<timeline::Clip> - getClipAt(lumiera::Time position) const; + virtual boost::shared_ptr<timeline::Clip> //////////////////////////////TICKET #796 : should use std::tr1 + getClipAt (Time position) const; private: diff --git a/src/gui/widgets/timeline/timeline-view-window.cpp b/src/gui/widgets/timeline/timeline-view-window.cpp index 2a62fcc90..d9b86597b 100644 --- a/src/gui/widgets/timeline/timeline-view-window.cpp +++ b/src/gui/widgets/timeline/timeline-view-window.cpp @@ -31,28 +31,29 @@ namespace gui { namespace widgets { namespace timeline { -TimelineViewWindow::TimelineViewWindow( - Time offset, int64_t scale) : - timeOffset(offset), - timeScale(scale) + + +TimelineViewWindow::TimelineViewWindow (Offset offset, int64_t scale) + : timeOffset(offset) + , timeScale(scale) { } -Time -TimelineViewWindow::get_time_offset() const +Offset +TimelineViewWindow::get_time_offset() const /////////////////////TODO: this function shouldn't be accessible from outside { - return timeOffset; + return Offset (timeOffset); } void -TimelineViewWindow::set_time_offset(Time offset) +TimelineViewWindow::set_time_offset(TimeValue const& offset) /////////TODO: this function shouldn't be accessible from outside { timeOffset = offset; changedSignal.emit(); } int64_t -TimelineViewWindow::get_time_scale() const +TimelineViewWindow::get_time_scale() const /////////////////////TODO: this function shouldn't be accessible from outside { return timeScale; } @@ -81,8 +82,9 @@ TimelineViewWindow::zoom_view(int point, int zoom_size) new_time_scale = TimelineWidget::MaxScale; // The view must be shifted so that the zoom is centred on the cursor - set_time_offset(Time((gavl_time_t)get_time_offset() + - (timeScale - new_time_scale) * point)); + TimeVar newStartPoint = get_time_offset(); + newStartPoint += TimeValue(point * (timeScale - new_time_scale)); + set_time_offset(newStartPoint); // Apply the new scale set_time_scale(new_time_scale); @@ -91,20 +93,20 @@ TimelineViewWindow::zoom_view(int point, int zoom_size) void TimelineViewWindow::shift_view(int view_width, int shift_size) { - set_time_offset(Time((gavl_time_t)get_time_offset() + - shift_size * timeScale * view_width / 256)); + set_time_offset(timeOffset + TimeValue(timeScale * shift_size * view_width / 256)); } int -TimelineViewWindow::time_to_x(gavl_time_t time) const +TimelineViewWindow::time_to_x(TimeValue const& time) const { - return (int)((time - timeOffset) / timeScale); + return int(_raw(time - timeOffset) / timeScale); //////TODO protect against values out-of range } Time TimelineViewWindow::x_to_time(int x) const { - return Time((gavl_time_t)((int64_t)x * timeScale + timeOffset)); + TimeValue time_in_view (timeScale * x); + return timeOffset + time_in_view; } sigc::signal<void> diff --git a/src/gui/widgets/timeline/timeline-view-window.hpp b/src/gui/widgets/timeline/timeline-view-window.hpp index ebd476b66..4f1733af8 100644 --- a/src/gui/widgets/timeline/timeline-view-window.hpp +++ b/src/gui/widgets/timeline/timeline-view-window.hpp @@ -28,14 +28,24 @@ #define TIMELINE_VIEW_WINDOW_HPP #include "gui/gtk-lumiera.hpp" +#include "lib/time/timevalue.hpp" namespace gui { namespace widgets { class TimelineWidget; - + + namespace timeline { +using lib::time::Time; +using lib::time::TimeVar; +using lib::time::TimeValue; +using lib::time::TimeSpan; +using lib::time::Duration; +using lib::time::Offset; + + /** * TimelineViewWindow is a helper class for TimelineWidget which manages * the view window of the timeline: the zoom and shift. The class also @@ -46,28 +56,34 @@ class TimelineViewWindow public: /** - * Constructor * @param offset The initial view offset. - * @param scale The initial scale. + * @param scale The initial view scale. */ - TimelineViewWindow(lumiera::Time offset, int64_t scale); + TimelineViewWindow(Offset offset, int64_t scale); /** - * Gets the time offset. This is the time value displaid at the + * Gets the time offset. This is the time value displayed at the * left-hand edge of the timeline body area. + * @todo obviously this must be switched to use the relevant time grid + * from the session / current timeline to be displayed. It doesn't + * make sense to display raw time values here, as each timeline might + * turn out to have a different origin; this is the result of resolving + * a placement, and only the session has the necessary informations... */ - lumiera::Time get_time_offset() const; + Offset get_time_offset() const; /** * Sets the time offset. This is the time value displaid at the * left-hand edge of the timeline body area. + * @todo this is private detail that shouldn't be exposed to the outside //////////////////TICKET# 795 */ - void set_time_offset(lumiera::Time time_offset); + void set_time_offset(TimeValue const&); /** * Gets the time scale value. * @return The scale factor, which is the number of microseconds per * screen pixel. + * @todo this is private detail that shouldn't be exposed to the outside //////////////////TICKET# 795 */ int64_t get_time_scale() const; @@ -100,15 +116,20 @@ public: * @return Returns the x-value as pixels from the left hand edge of * the timeline body. */ - int time_to_x(int64_t time) const; + int time_to_x(TimeValue const&) const; /** * Converts x coordinates in pixels to time values. * @param x The x coordinte (as pixels from the left hand edge of * the timeline body) to convert. * @return Returns the time at the coordinate. + * + * @todo 5/11 this is good for now, but on the long run I'm tinking + * we rather should treat that like a special frame grid + * (display coordinate system) and then use the same framework + * we use for timecodes and frame counts. */ - lumiera::Time x_to_time(int x) const; + Time x_to_time(int x) const; /** * A signal to indicate that the scale or offset have been changed. @@ -116,7 +137,7 @@ public: sigc::signal<void> changed_signal() const; private: - lumiera::Time timeOffset; + TimeVar timeOffset; int64_t timeScale; sigc::signal<void> changedSignal; From 46c9811184d464ff1f30c6442a43a5fd7e79506f Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 16 May 2011 08:38:27 +0200 Subject: [PATCH 079/496] adapt the unit-tests to use the new Time framework --- tests/components/proc/asset/makecliptest.cpp | 3 +- .../control/argument-tuple-accept-test.cpp | 6 ++-- .../proc/control/command-argument-test.cpp | 34 ++++++++++--------- .../proc/control/command-basic-test.cpp | 24 ++++++++----- .../proc/mobject/mobject-interface-test.cpp | 16 +++++---- .../proc/mobject/mobject-ref-test.cpp | 16 ++++++--- .../proc/mobject/placement-basic-test.cpp | 13 +++---- .../placement-object-identity-test.cpp | 5 +-- .../proc/mobject/placement-ref-test.cpp | 11 +++--- .../proc/mobject/session/addcliptest.cpp | 1 - .../proc/mobject/session/testclip.cpp | 5 +-- .../advice/advice-binding-pattern-test.cpp | 6 ++-- tests/lib/iter-source-test.cpp | 10 +++--- .../lib/meta/iterable-classification-test.cpp | 2 ++ tests/lib/time/time-value-test.cpp | 6 ++-- tests/lib/typed-counter-test.cpp | 5 +-- 16 files changed, 96 insertions(+), 67 deletions(-) diff --git a/tests/components/proc/asset/makecliptest.cpp b/tests/components/proc/asset/makecliptest.cpp index 4042053a7..0eeb060c7 100644 --- a/tests/components/proc/asset/makecliptest.cpp +++ b/tests/components/proc/asset/makecliptest.cpp @@ -25,6 +25,7 @@ #include "lib/test/run.hpp" #include "lib/util.hpp" +#include "lib/time/timevalue.hpp" #include "proc/assetmanager.hpp" #include "proc/asset/media.hpp" #include "proc/mobject/session/clip.hpp" @@ -61,7 +62,7 @@ namespace asset { PM cm = cc->getMedia(); CHECK (cm); - CHECK (Time(0) < cc->getLength()); + CHECK (!isnil (cc->getLength())); CHECK (cm->ident.category.hasKind (VIDEO)); CHECK (cm->getFilename() == mm->getFilename()); TODO ("implement Processing Pattern!!!"); diff --git a/tests/components/proc/control/argument-tuple-accept-test.cpp b/tests/components/proc/control/argument-tuple-accept-test.cpp index c264ca255..38618bcba 100644 --- a/tests/components/proc/control/argument-tuple-accept-test.cpp +++ b/tests/components/proc/control/argument-tuple-accept-test.cpp @@ -26,7 +26,7 @@ #include "proc/control/argument-tuple-accept.hpp" #include "lib/meta/function.hpp" #include "lib/meta/tuple.hpp" -#include "lib/lumitime-fmt.hpp" +#include "lib/time/diagnostics.hpp" #include <tr1/functional> #include <iostream> @@ -41,7 +41,7 @@ namespace test { using lib::test::showSizeof; using lib::test::randTime; - using lumiera::Time; + using lib::time::TimeVar; using std::tr1::function; using lumiera::typelist::FunctionSignature; using lumiera::typelist::Tuple; @@ -103,7 +103,7 @@ namespace test { run (Arg) { TestClass<void()> testVoid; - TestClass<int(Time,int)> testTime; + TestClass<int(TimeVar,int)> testTime; testVoid.bind(); testTime.bind(randTime(),23); diff --git a/tests/components/proc/control/command-argument-test.cpp b/tests/components/proc/control/command-argument-test.cpp index 8f5feb85f..094f63d2a 100644 --- a/tests/components/proc/control/command-argument-test.cpp +++ b/tests/components/proc/control/command-argument-test.cpp @@ -25,7 +25,7 @@ #include "lib/test/test-helper.hpp" #include "proc/control/command-argument-holder.hpp" #include "lib/scoped-ptrvect.hpp" -#include "lib/lumitime-fmt.hpp" +#include "lib/time/diagnostics.hpp" #include "lib/meta/tuple.hpp" #include "lib/util-foreach.hpp" #include "lib/util.hpp" @@ -39,7 +39,9 @@ using util::isnil; using util::for_each; -using lumiera::Time; +using lib::time::Time; +using lib::time::TimeVar; +using lib::time::TimeValue; using boost::format; using std::string; using std::ostream; @@ -123,7 +125,7 @@ namespace test { /* === functions to implement test-"operation" & UNDO === */ void - doIt (Tracker<Time> time, Tracker<string> str, int rand) + doIt (Tracker<TimeVar> time, Tracker<string> str, int rand) { static format fmt ("doIt( Time=%s \"%s\" rand=%2d )"); cout << "invoke operation..." << endl; @@ -131,14 +133,14 @@ namespace test { } Tracker<string> - captureState (Tracker<Time>, Tracker<string> xstr, int) + captureState (Tracker<TimeVar>, Tracker<string> xstr, int) { cout << "capture state..." << endl; return protocol.str() + *xstr; } void - undoIt (Tracker<Time> time, Tracker<string>, int, Tracker<string> memento) + undoIt (Tracker<TimeVar> time, Tracker<string>, int, Tracker<string> memento) { cout << "undo... memento=" << memento << endl; protocol << "undoIt(time="<<time<<")----memento-:"<< *memento; @@ -189,7 +191,7 @@ namespace test { run (Arg) { ArgTuples testTuples; - Tracker<Time>::instanceCnt = 0; + Tracker<TimeVar>::instanceCnt = 0; Tracker<string>::instanceCnt = 0; createTuples (testTuples); @@ -200,12 +202,12 @@ namespace test { simulateCmdLifecycle(); // verify all dtors properly called... - CHECK (0 == Tracker<Time>::instanceCnt); + CHECK (0 == Tracker<TimeVar>::instanceCnt); CHECK (0 == Tracker<string>::instanceCnt); } - typedef Tracker<Time> TTime; + typedef Tracker<TimeVar> TTime; typedef Tracker<string> Tstr; @@ -215,10 +217,10 @@ namespace test { void createTuples (ArgTuples& tup) { - typedef ArgumentHolder<void(), bool> A1; - typedef ArgumentHolder<void(int), void*> A2; - typedef ArgumentHolder<void(int,Time), int> A3; - typedef ArgumentHolder<void(int,Time), Sint5> A4; + typedef ArgumentHolder<void(), bool> A1; + typedef ArgumentHolder<void(int), void*> A2; + typedef ArgumentHolder<void(int,TimeVar), int> A3; + typedef ArgumentHolder<void(int,TimeVar), Sint5> A4; typedef ArgumentHolder<void(TTime,Tstr,int), Tstr> A5; @@ -239,8 +241,8 @@ namespace test { arg1->storeTuple (tuple::makeNullTuple()); arg2->storeTuple (tuple::make (rand() % 10)); - arg3->storeTuple (tuple::make (rand() % 10, randTime())); - arg4->storeTuple (tuple::make (rand() % 10, randTime())); + arg3->storeTuple (tuple::make (rand() % 10, TimeVar(randTime()))); + arg4->storeTuple (tuple::make (rand() % 10, TimeVar(randTime()))); arg5->storeTuple (tuple::make (TTime (randTime()), Tstr("glorious"), rand() % 25)); @@ -321,7 +323,7 @@ namespace test { void simulateCmdLifecycle() { - typedef void SIG_do(Tracker<Time>, Tracker<string>, int); + typedef void SIG_do(Tracker<TimeVar>, Tracker<string>, int); typedef ArgumentHolder<SIG_do, Tracker<string> > Args; typedef MementoTie<SIG_do, Tracker<string> > MemHolder; @@ -375,7 +377,7 @@ namespace test { protocol << "RESET..."; args.storeTuple ( - tuple::make (TTime(Time(123456)), Tstr("unbelievable"), rand() %100)); + tuple::make (TTime(TimeValue(123456)), Tstr("unbelievable"), rand() %100)); cout << "modified: " << args << endl; cout << "copied : " << argsCopy << endl; // holds still the old params & memento diff --git a/tests/components/proc/control/command-basic-test.cpp b/tests/components/proc/control/command-basic-test.cpp index c2413c95c..ef392906e 100644 --- a/tests/components/proc/control/command-basic-test.cpp +++ b/tests/components/proc/control/command-basic-test.cpp @@ -23,6 +23,7 @@ #include "lib/test/run.hpp" #include "proc/control/command-def.hpp" +#include "lib/time/timevalue.hpp" #include "lib/p.hpp" #include <cstdlib> @@ -34,6 +35,9 @@ namespace control { namespace test { using lumiera::P; + using lib::time::Time; + using lib::time::TimeVar; + using lib::time::TimeValue; @@ -42,19 +46,19 @@ namespace test { namespace { // functions to be invoked through the command system void - operate (P<Time> dummyObj, int randVal) + operate (P<TimeVar> dummyObj, int randVal) { - *dummyObj += Time(randVal); + *dummyObj += TimeValue(randVal); } - Time - capture (P<Time> dummyObj, int) + TimeVar + capture (P<TimeVar> dummyObj, int) { return *dummyObj; } void - undoIt (P<Time> dummyObj, int, Time oldVal) + undoIt (P<TimeVar> dummyObj, int, TimeVar oldVal) { *dummyObj = oldVal; } @@ -85,8 +89,10 @@ namespace test { virtual void run (Arg) { - P<Time> obj (new Time(5)); int randVal ((rand() % 10) - 5); + Time five(TimeValue(5)); + TimeValue randomTime(randVal); + P<TimeVar> obj (new TimeVar(five)); CommandDef ("test.command1") .operation (operate) @@ -99,13 +105,13 @@ namespace test { Command ourCmd = Command::get("test.command1"); // invoke the command - CHECK (*obj == Time(5)); + CHECK (*obj == five); ourCmd(); - CHECK (*obj == Time(5) + Time(randVal)); + CHECK (*obj == five + randomTime); // undo the effect of the command ourCmd.undo(); - CHECK (*obj == Time(5)); + CHECK (*obj == five); } }; diff --git a/tests/components/proc/mobject/mobject-interface-test.cpp b/tests/components/proc/mobject/mobject-interface-test.cpp index b3f3e83eb..3b5f64fd1 100644 --- a/tests/components/proc/mobject/mobject-interface-test.cpp +++ b/tests/components/proc/mobject/mobject-interface-test.cpp @@ -36,6 +36,7 @@ //#include "proc/mobject/explicitplacement.hpp" #include "proc/mobject/test-dummy-mobject.hpp" //#include "lib/test/test-helper.hpp" +#include "lib/time/timevalue.hpp" #include <iostream> @@ -50,6 +51,9 @@ namespace test { using std::endl; using lib::Symbol; + using lib::time::Duration; + using lib::time::FSecs; + using lib::time::Time; // using session::Clip; @@ -80,13 +84,13 @@ namespace test { PMO testClip2 = asset::Media::create("test-2", asset::VIDEO)->createClip(); // set up a tie to fixed start positions (i.e. "properties of placement") - testClip1.chain(Time(10)); - testClip2.chain(Time(20)); + testClip1.chain(Time(FSecs(10))); + testClip2.chain(Time(FSecs(20))); Symbol labelType ("dummyLabel"); PMO testLabel1 = MObject::create (labelType); - testLabel1.chain(Time(30)); + testLabel1.chain(Time(FSecs(30))); PDummy testDummy1(*new DummyMO); PDummy testDummy2(*new TestSubMO1); @@ -97,9 +101,9 @@ namespace test { CHECK (testDummy1->isValid()); CHECK (testDummy2->isValid()); - Time lenC1 = testClip1->getLength(); - Time lenC2 = testClip2->getLength(); - Time lenL1 = testLabel1->getLength(); + Duration lenC1 = testClip1->getLength(); + Duration lenC2 = testClip2->getLength(); + Duration lenL1 = testLabel1->getLength(); cout << testClip1->shortID() << endl; cout << testClip2->shortID() << endl; diff --git a/tests/components/proc/mobject/mobject-ref-test.cpp b/tests/components/proc/mobject/mobject-ref-test.cpp index 3447deb8e..91b9d9859 100644 --- a/tests/components/proc/mobject/mobject-ref-test.cpp +++ b/tests/components/proc/mobject/mobject-ref-test.cpp @@ -33,10 +33,16 @@ #include "proc/mobject/explicitplacement.hpp" #include "proc/mobject/test-dummy-mobject.hpp" #include "lib/test/test-helper.hpp" +#include "lib/time/timevalue.hpp" +#include "lib/util.hpp" #include <iostream> using lib::test::showSizeof; +using lib::time::Duration; +using lib::time::FSecs; +using lib::time::Time; +using util::isnil; using std::string; using std::cout; using std::endl; @@ -101,8 +107,8 @@ namespace test { PMObj testClip2 = asset::Media::create("test-2", asset::VIDEO)->createClip(); // set up a tie to fixed start positions (i.e. "properties of placement") - testClip1.chain(Time(10)); - testClip2.chain(Time(20)); + testClip1.chain (Time(FSecs(10))); + testClip2.chain (Time(FSecs(20))); CHECK (testClip1->isValid()); CHECK (testClip2->isValid()); @@ -188,13 +194,13 @@ namespace test { // cout << rMO->operator string() << endl; /////////////////////TICKET #428 PMedia media = rMO->getMedia(); cout << str(media) << endl; /////////////////////TICKET #520 - Time mediaLength = media->getLength(); - CHECK (Time(0) < mediaLength); + Duration mediaLength = media->getLength(); + CHECK (!isnil (mediaLength)); CHECK (rMO->isValid()); // access the Placement-API CHECK (checkUseCount(rMO, 1)); // now rMO shares ownership with the Placement --> use-count += 1 - CHECK (Time(0) < rMO.getStartTime()); // (internally, this resolves to an ExplicitPlacement) /////////TICKET #332 + CHECK (Time::ZERO < rMO.getStartTime()); // (internally, this resolves to an ExplicitPlacement) /////////TICKET #332 CHECK ( rMO.isCompatible<MObject>()); CHECK ( rMO.isCompatible<Clip>()); CHECK (!rMO.isCompatible<TestSubMO1>()); diff --git a/tests/components/proc/mobject/placement-basic-test.cpp b/tests/components/proc/mobject/placement-basic-test.cpp index 150907b62..5aeed4d15 100644 --- a/tests/components/proc/mobject/placement-basic-test.cpp +++ b/tests/components/proc/mobject/placement-basic-test.cpp @@ -27,12 +27,13 @@ #include "proc/mobject/session/testclip.hpp" #include "proc/mobject/placement.hpp" #include "proc/mobject/explicitplacement.hpp" -#include "lib/util.hpp" #include "proc/mobject/session/mobjectfactory.hpp" ////TODO: avoidable? +#include "lib/time/timevalue.hpp" +#include "lib/util.hpp" #include <iostream> -using lumiera::Time; +using lib::time::Time; using util::contains; using std::string; using std::cout; @@ -72,16 +73,16 @@ namespace test { // using the Placement interface // TODO: how to handle insufficiently determinated Placement? Throw? - FixedLocation & fixloc = pc.chain(Time(1)); // TODO: the track?? + FixedLocation & fixloc = pc.chain (Time(0,1)); // TODO: the track?? ExplicitPlacement expla = pc.resolve(); - CHECK (expla.time == Time(1)); + CHECK (expla.time == Time(0,1)); CHECK (!expla.chain.isOverdetermined()); // CHECK (*expla == *pc); ////////////////////////////////////////////TICKET #511 define equivalence of locating chains and solutions // now overconstraining with another Placement - pc.chain(Time(2)); + pc.chain (Time(0,2)); ExplicitPlacement expla2 = pc.resolve(); - CHECK (expla2.time == Time(2)); // the latest addition wins + CHECK (expla2.time == Time(0,2)); // the latest addition wins CHECK (expla2.chain.isOverdetermined()); } }; diff --git a/tests/components/proc/mobject/placement-object-identity-test.cpp b/tests/components/proc/mobject/placement-object-identity-test.cpp index 62309d07f..fd349ed5e 100644 --- a/tests/components/proc/mobject/placement-object-identity-test.cpp +++ b/tests/components/proc/mobject/placement-object-identity-test.cpp @@ -31,6 +31,7 @@ #include "proc/mobject/session/session-service-mock-index.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/explicitplacement.hpp" +#include "lib/time/timevalue.hpp" //#include "proc/mobject/test-dummy-mobject.hpp" //#include "lib/test/test-helper.hpp" @@ -79,8 +80,8 @@ namespace test { PMObj pClip2 = asset::Media::create("test-2", asset::VIDEO)->createClip(); // set up a tie to fixed start positions - pClip1.chain(Time(10)); - pClip2.chain(Time(20)); + pClip1.chain (lib::time::Time(0,10)); + pClip2.chain (lib::time::Time(0,20)); CHECK (pClip1->isValid()); CHECK (pClip2->isValid()); diff --git a/tests/components/proc/mobject/placement-ref-test.cpp b/tests/components/proc/mobject/placement-ref-test.cpp index c61036ade..8d91c660b 100644 --- a/tests/components/proc/mobject/placement-ref-test.cpp +++ b/tests/components/proc/mobject/placement-ref-test.cpp @@ -29,6 +29,7 @@ #include "proc/mobject/session/session-service-mock-index.hpp" #include "proc/mobject/explicitplacement.hpp" #include "proc/mobject/test-dummy-mobject.hpp" +#include "lib/time/timevalue.hpp" #include "lib/util.hpp" #include <iostream> @@ -69,7 +70,9 @@ namespace test { { PSub testPlacement1(*new TestSubMO21); PSub testPlacement2(*new TestSubMO21); - testPlacement2.chain(Time(2)); // define start time of Placement-2 to be at t=2 + + lib::time::Time twoSec(0,2); + testPlacement2.chain (twoSec); // define start time of Placement-2 to be at t=2sec // Prepare an (test)Index backing the PlacementRefs PPIdx index = SessionServiceMockIndex::install(); @@ -113,7 +116,7 @@ namespace test { CHECK (2 == ref1.use_count()); CHECK (2 == ref2.use_count()); ExplicitPlacement exPla = refX.resolve(); - CHECK (exPla.time == Time(2)); // indeed get back the time we set on p2 above + CHECK (exPla.time == twoSec); // indeed get back the time we set on p2 above CHECK (3 == ref2.use_count()); // exPla shares ownership with p2 CHECK (index->contains(ref1)); // ref can stand-in for a placement-ID @@ -151,9 +154,9 @@ namespace test { CHECK (ref2 != refX); // resolution is indeed "live", we see changes to the referred placement - CHECK (refX.resolve().time == Time::MIN); + CHECK (refX.resolve().time == lib::time::Time::MIN); p1.chain = p2.chain; // do a change on the placement within index.... - CHECK (refX.resolve().time == Time(2)); // now we get the time tie we originally set on p2 + CHECK (refX.resolve().time == twoSec); // now we get the time tie we originally set on p2 CHECK (p1.getID() != p2.getID()); // but the instance identities are still unaltered CHECK (2 == ref1.use_count()); diff --git a/tests/components/proc/mobject/session/addcliptest.cpp b/tests/components/proc/mobject/session/addcliptest.cpp index 34e83d2b5..35dd0a7d9 100644 --- a/tests/components/proc/mobject/session/addcliptest.cpp +++ b/tests/components/proc/mobject/session/addcliptest.cpp @@ -31,7 +31,6 @@ #include <iostream> //using boost::format; -using lumiera::Time; using util::contains; using std::string; using std::cout; diff --git a/tests/components/proc/mobject/session/testclip.cpp b/tests/components/proc/mobject/session/testclip.cpp index c8c916512..53296fab6 100644 --- a/tests/components/proc/mobject/session/testclip.cpp +++ b/tests/components/proc/mobject/session/testclip.cpp @@ -27,12 +27,13 @@ #include "proc/asset/media.hpp" #include "proc/asset/clip.hpp" #include "lib/singleton.hpp" +#include "lib/time/timevalue.hpp" namespace mobject { namespace session { namespace test { - using lumiera::Time; + using lib::time::Time; typedef shared_ptr<asset::Media> PM; typedef backend_interface::MediaAccessFacade MAF; using backend_interface::test::MediaAccessMock; @@ -85,7 +86,7 @@ namespace test { /* == define some data for verification in unit tests == */ - const Time LENGTH_TestClip = Time(25); //////TODO hard wired as of (1/10). See MediaFactory::operator() in media.cpp + const Time LENGTH_TestClip(0,25,0,0); //////TODO hard wired as of (1/10). See MediaFactory::operator() in media.cpp }}} // namespace mobject::session::test diff --git a/tests/lib/advice/advice-binding-pattern-test.cpp b/tests/lib/advice/advice-binding-pattern-test.cpp index 54c6741d5..dc649cb46 100644 --- a/tests/lib/advice/advice-binding-pattern-test.cpp +++ b/tests/lib/advice/advice-binding-pattern-test.cpp @@ -25,7 +25,7 @@ #include "lib/test/test-helper.hpp" #include "lib/advice.hpp" -#include "lib/time.h" +#include "lib/time/timevalue.hpp" #include <iostream> @@ -127,11 +127,11 @@ namespace test { b2.addPredicate("cat3(zzz)"); CHECK (b1 != b2); - b1.addTypeGuard<lumiera::Time>(); + b1.addTypeGuard<time::Time>(); CHECK (b1 != b2); b1.addPredicate(" cat3( zzz ) "); CHECK (b1 != b2); - b2.addTypeGuard<lumiera::Time>(); + b2.addTypeGuard<time::Time>(); CHECK (b1 == b2); cout << "b2==" << b2 << endl; diff --git a/tests/lib/iter-source-test.cpp b/tests/lib/iter-source-test.cpp index 694556be8..cd1ef447e 100644 --- a/tests/lib/iter-source-test.cpp +++ b/tests/lib/iter-source-test.cpp @@ -24,7 +24,7 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" -#include "lib/lumitime-fmt.hpp" +#include "lib/time/diagnostics.hpp" #include "lib/util.hpp" #include "lib/iter-source.hpp" @@ -44,9 +44,9 @@ namespace lib { namespace test{ using ::Test; - using lumiera::Time; using boost::lexical_cast; using boost::noncopyable; + using lib::time::TimeVar; using lib::test::randStr; using lib::test::randTime; using util::isnil; @@ -153,10 +153,10 @@ namespace test{ typedef IterSource<int>::iterator IntIter; typedef IterSource<CStr>::iterator StrIter; typedef IterSource<string>::iterator StringIter; - typedef IterSource<Time>::iterator TimeIter; + typedef IterSource<TimeVar>::iterator TimeIter; - typedef std::map<string,Time> TreeMap; - typedef std::tr1::unordered_map<string,Time> HashMap; + typedef std::map<string,TimeVar> TreeMap; + typedef std::tr1::unordered_map<string,TimeVar> HashMap; typedef std::multimap<int,int> TreeMultimap; typedef std::tr1::unordered_multimap<int,int>HashMultimap; diff --git a/tests/lib/meta/iterable-classification-test.cpp b/tests/lib/meta/iterable-classification-test.cpp index bda746175..0066c33de 100644 --- a/tests/lib/meta/iterable-classification-test.cpp +++ b/tests/lib/meta/iterable-classification-test.cpp @@ -26,6 +26,7 @@ #include "proc/mobject/session/scope-query.hpp" #include "proc/mobject/session/effect.hpp" #include "lib/meta/duck-detector.hpp" +#include "lib/time/timevalue.hpp" #include "lib/util-foreach.hpp" #include "lib/itertools.hpp" @@ -46,6 +47,7 @@ namespace test{ using mobject::session::Effect; using mobject::session::ScopeQuery; + typedef lib::time::TimeVar Time; using std::cout; using std::endl; diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index 9a5a6fc4c..08dfef586 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -387,8 +387,10 @@ namespace test{ void relateTimeIntervals (TimeValue org) { - TimeSpan span1 (org, FSecs(2)); - TimeSpan span2 (org+Time(FSecs(1)), FSecs(2)); + Time oneSec(FSecs(1)); + + TimeSpan span1 (org, FSecs(2)); + TimeSpan span2 (oneSec + org, FSecs(2)); TimeVar probe(org); CHECK ( span1.contains(probe)); diff --git a/tests/lib/typed-counter-test.cpp b/tests/lib/typed-counter-test.cpp index b012c1f5e..ad0735e44 100644 --- a/tests/lib/typed-counter-test.cpp +++ b/tests/lib/typed-counter-test.cpp @@ -50,8 +50,9 @@ #include "lib/sync.hpp" #include <tr1/functional> -#include <vector> #include <cstdlib> +#include <vector> +#include <time.h> namespace lib { @@ -347,7 +348,7 @@ namespace test{ void tortureTest () { - std::srand (time (NULL)); + std::srand (::time (NULL)); sum_TypedCounter_ = 0; sum_internal_ = 0; From a207a9f003ba9231a9ffa1e12b341a697ad6d764 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 18 May 2011 01:37:33 +0200 Subject: [PATCH 080/496] adapt Media-Access (+Mock) to include an overall Duration --- src/backend/mediaaccessfacade.cpp | 14 +- src/backend/mediaaccessfacade.hpp | 55 ++++++-- src/lib/singleton-preconfigure.hpp | 6 +- src/proc/asset/media.cpp | 14 +- tests/48proc-operate.tests | 14 +- tests/components/backend/mediaaccessmock.cpp | 87 +++++++++---- tests/components/backend/mediaaccessmock.hpp | 9 +- .../backend/mediaaccessmocktest.cpp | 123 ++++++++---------- .../proc/mobject/session/testclip.cpp | 5 +- .../proc/mobject/session/testclip.hpp | 2 +- 10 files changed, 195 insertions(+), 134 deletions(-) diff --git a/src/backend/mediaaccessfacade.cpp b/src/backend/mediaaccessfacade.cpp index e9feab29f..50daf47c1 100644 --- a/src/backend/mediaaccessfacade.cpp +++ b/src/backend/mediaaccessfacade.cpp @@ -28,30 +28,26 @@ using util::isnil; using lumiera::error::Invalid; -namespace backend_interface - { +namespace backend { /** storage for the SingletonFactory * (actually a lumiera::test::MockInjector) */ Singleton<MediaAccessFacade> MediaAccessFacade::instance; - typedef MediaAccessFacade::FileHandle FileHandle; - typedef MediaAccessFacade::ChanHandle ChanHandle; - FileHandle - MediaAccessFacade::queryFile (const char* name) throw(Invalid) + MediaDesc& + MediaAccessFacade::queryFile (string const& name) const { if (isnil (name)) throw Invalid ("empty filename passed to MediaAccessFacade."); UNIMPLEMENTED ("delegate to backend: query accessability of file"); - return 0; } ChanDesc - MediaAccessFacade::queryChannel (FileHandle fhandle, uint chanNo) throw() + MediaAccessFacade::queryChannel (MediaDesc& mHandle, uint chanNo) const { UNIMPLEMENTED ("delegate to backend: query channel information"); ChanDesc nix; @@ -60,4 +56,4 @@ namespace backend_interface -} // namespace backend_interface +} // namespace backend diff --git a/src/backend/mediaaccessfacade.hpp b/src/backend/mediaaccessfacade.hpp index 261e84a61..241143985 100644 --- a/src/backend/mediaaccessfacade.hpp +++ b/src/backend/mediaaccessfacade.hpp @@ -25,13 +25,20 @@ #define BACKEND_INTERFACE_MEDIAACCESSFACADE_H -#include "lib/singleton.hpp" #include "lib/error.hpp" +#include "lib/singleton.hpp" +#include "lib/time/timevalue.hpp" + +#include <string> -namespace backend_interface { +namespace backend { + using lib::time::Duration; + using std::string; + + struct MediaDesc; struct ChanDesc; @@ -42,24 +49,26 @@ namespace backend_interface { * Implementation delegating to the actual backend functions. * * convention: data passed by pointer is owned by the originator; - * it should be copied if needed byond the control flow + * it should be copied if needed beyond the control flow * of the invoked function. */ - struct MediaAccessFacade + class MediaAccessFacade { - typedef void* FileHandle; + public: typedef void* ChanHandle; static Singleton<MediaAccessFacade> instance; /** request for testing the denoted files accessibility * @param name path and filename of the media file. - * @throw invalid when passing empty filename + * @throw error::Invalid when passing empty filename, + * or in case the media file is inaccessible + * or otherwise inappropriate. * @return opaque handle usable for querying channel * information from this file, NULL if the * file is not accessible. */ - virtual FileHandle queryFile (const char* name) throw(lumiera::error::Invalid); + virtual MediaDesc& queryFile (string const& name) const; /** request for information about the n-th channel * of the file referred by FileHandle. @@ -67,12 +76,31 @@ namespace backend_interface { * the file doesn't contain this much channels. * @todo throw or return NULL-ChanDesc if Filehandle is invalid? */ - virtual ChanDesc queryChannel (FileHandle, uint chanNo) throw(); + virtual ChanDesc queryChannel (MediaDesc&, uint chanNo) const; virtual ~MediaAccessFacade () {} }; - + + /** + * Descriptor holding the global informations, + * needed for further handling this media within Lumiera. + */ + struct MediaDesc + { + /** effectively usable duration. + * A clip created from this media will have this + * maximum duration. We expect to get media stream data + * from all channels within this limit. + */ + Duration length; + + MediaDesc() : length(Duration::NIL) { } + }; + + + + /** * Description of one channel found in a * media file; result of querying the channel. @@ -105,8 +133,15 @@ namespace backend_interface { handle(h) { } }; - +} // namespace backend + + +namespace backend_interface { + + using backend::MediaAccessFacade; + using backend::MediaDesc; + using backend::ChanDesc; } // namespace backend_interface #endif diff --git a/src/lib/singleton-preconfigure.hpp b/src/lib/singleton-preconfigure.hpp index 5101c634e..70c911a52 100644 --- a/src/lib/singleton-preconfigure.hpp +++ b/src/lib/singleton-preconfigure.hpp @@ -71,7 +71,7 @@ namespace lib { } // namespace test } // namespace lumiera -namespace backend_interface { +namespace backend { class MediaAccessFacade; using lib::Singleton; @@ -97,8 +97,8 @@ namespace lib { template<> - class Singleton<backend_interface::MediaAccessFacade> - : public MockInjector<backend_interface::MediaAccessFacade> + class Singleton<backend::MediaAccessFacade> + : public MockInjector<backend::MediaAccessFacade> { }; } // namespace lib diff --git a/src/proc/asset/media.cpp b/src/proc/asset/media.cpp index d67231e89..c357338e5 100644 --- a/src/proc/asset/media.cpp +++ b/src/proc/asset/media.cpp @@ -22,12 +22,14 @@ #include "pre.hpp" +#include "lib/error.hpp" #include "proc/assetmanager.hpp" #include "proc/asset/media.hpp" #include "proc/asset/clip.hpp" #include "proc/asset/unknown.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/session/mobjectfactory.hpp" +#include "backend/mediaaccessfacade.hpp" #include "lib/time/timevalue.hpp" #include "lib/util.hpp" #include "include/logging.h" @@ -39,6 +41,8 @@ using util::isnil; using lib::time::FSecs; using lib::time::Duration; +using backend_interface::MediaDesc; +using backend_interface::MediaAccessFacade; using boost::format; using boost::regex; @@ -46,6 +50,7 @@ using boost::smatch; using boost::regex_search; using std::tr1::dynamic_pointer_cast; +namespace error = lumiera::error; namespace asset { @@ -134,6 +139,7 @@ namespace asset { * either a asset::Media object or an "Unknown" placeholder will be provided. If * the given Category already contains an "Unkown", we just get the * corresponding smart-ptr. Otherwise a new asset::Unknown is created. + * @throw error::Invalid when media file is inaccessible or inappropriate * @return an Media smart ptr linked to the internally registered smart ptr * created as a side effect of calling the concrete Media subclass ctor. */ @@ -157,9 +163,11 @@ namespace asset { else { if (isnil (key.name)) key.name=extractName(file); - TODO ("file exists?"); - TODO ("extract media file properties"); - Duration length(FSecs(5)); + + MediaAccessFacade& maf = MediaAccessFacade::instance(); + MediaDesc& handle = maf.queryFile(file); + Duration length = handle.length; + TODO ("detecting and wiring multichannel compound media!"); pM = new Media (key,file,length); } diff --git a/tests/48proc-operate.tests b/tests/48proc-operate.tests index e36aa3066..1889a8c49 100644 --- a/tests/48proc-operate.tests +++ b/tests/48proc-operate.tests @@ -2,13 +2,13 @@ TESTING "Proc Layer combined operations Test Suite" ./test-components --group=op -TEST "MediaAccessMock_test" MediaAccessMock_test <<END -out: accessing "test-1" ... -out: Channel-0: nameID=video codecID=ID -out: accessing "test-2" ... -out: Channel-0: nameID=video codecID=H264 -out: Channel-1: nameID=audio-L codecID=PCM -out: Channel-2: nameID=audio-R codecID=PCM +TEST "Mock media access" MediaAccessMock_test <<END +out-lit: accessing "test-1" (dur=0:00:25.000)... +out-lit: Channel-0: nameID=video codecID=ID +out-lit: accessing "test-2" (dur=0:00:25.000)... +out-lit: Channel-0: nameID=video codecID=H264 +out-lit: Channel-1: nameID=audio-L codecID=PCM +out-lit: Channel-2: nameID=audio-R codecID=PCM END diff --git a/tests/components/backend/mediaaccessmock.cpp b/tests/components/backend/mediaaccessmock.cpp index e15a2213c..46c8d07dc 100644 --- a/tests/components/backend/mediaaccessmock.cpp +++ b/tests/components/backend/mediaaccessmock.cpp @@ -34,15 +34,21 @@ #include "backend/mediaaccessmock.hpp" +#include "proc/mobject/session/testclip.hpp" #include "lib/util.hpp" #include "lib/util-foreach.hpp" +#include "lib/time/mutation.hpp" +#include "lib/symbol.hpp" #include <iostream> #include <vector> #include <map> using lumiera::error::Invalid; +using lib::time::Mutation; +using lib::time::Duration; +using lib::Literal; using util::for_each; using util::isnil; using std::cout; @@ -51,29 +57,56 @@ using std::vector; using std::map; -namespace backend_interface { +namespace backend { namespace test { - typedef MediaAccessFacade::FileHandle FileHandle; typedef MediaAccessFacade::ChanHandle ChanHandle; namespace { // implementation details - - typedef vector<ChanDesc> Response; + + struct Response + { + MediaDesc globalDesc; + vector<ChanDesc> channels; + + Response& + globalLength (Duration length) + { + globalDesc.length.accept (Mutation::changeDuration(length)); + return *this; + } + + Response& + channel (Literal name, Literal id) + { + channels.push_back (ChanDesc (name, id, genH())); + return *this; + } + + private: + static int _i_; + ChanHandle genH() + { + return reinterpret_cast<ChanHandle> (++_i_); + } + }; + int Response::_i_(0); const ChanDesc NULLResponse; + using mobject::session::test::LENGTH_TestClip; + struct TestCases : map<string,Response> { TestCases () { - // ------------------------------------------------------------------TESTCASES - (*this)["test-1"].push_back (ChanDesc ("video","ID", genH())); + // ----------------------------------------------------------------------TESTCASES + (*this)["test-1"].globalLength(LENGTH_TestClip).channel("video","ID"); - (*this)["test-2"].push_back (ChanDesc ("video","H264", genH())); - (*this)["test-2"].push_back (ChanDesc ("audio-L","PCM", genH())); - (*this)["test-2"].push_back (ChanDesc ("audio-R","PCM", genH())); - // ------------------------------------------------------------------TESTCASES + (*this)["test-2"].globalLength(LENGTH_TestClip).channel("video","H264") + .channel("audio-L","PCM") + .channel("audio-R","PCM"); + // ----------------------------------------------------------------------TESTCASES } bool known (string key) @@ -81,12 +114,6 @@ namespace test { const_iterator i = find (key); return (i != end()); } - private: - int _i_; - ChanHandle genH() - { - return reinterpret_cast<ChanHandle> (++_i_); - } }; // instantiate TestCasses table @@ -95,28 +122,32 @@ namespace test { } // (end) implementation namespace - FileHandle - MediaAccessMock::queryFile (const char* name) throw(Invalid) + + MediaDesc& + MediaAccessMock::queryFile (string const& name) const { - if (isnil (name)) - throw Invalid ("empty filename passed to MediaAccessFacade."); + if (isnil (name)) + throw Invalid ("empty filename passed to MediaAccessFacade."); if (!testCases.known(name)) - return 0; - else - return reinterpret_cast<void*> (&testCases[name]); + throw Invalid ("unable to use media file \""+name+"\"." + "Hint: you're using a test-mock file access, " + "which responds only to some magical names."); + + return testCases[name].globalDesc; } + ChanDesc - MediaAccessMock::queryChannel (FileHandle h, uint chanNo) throw() + MediaAccessMock::queryChannel (MediaDesc& h, uint chanNo) const { - const Response* res (reinterpret_cast<Response*> (h)); + Response const& res (*reinterpret_cast<Response*> (&h)); - if (!res || res->size() <= chanNo) + if (res.channels.size() <= chanNo) return NULLResponse; else - return (*res)[chanNo]; + return res.channels[chanNo]; } -}} // namespace backend_interface::test +}} // namespace backend::test diff --git a/tests/components/backend/mediaaccessmock.hpp b/tests/components/backend/mediaaccessmock.hpp index e48733bc1..dce886aa9 100644 --- a/tests/components/backend/mediaaccessmock.hpp +++ b/tests/components/backend/mediaaccessmock.hpp @@ -30,7 +30,7 @@ -namespace backend_interface { +namespace backend { namespace test { /** @@ -39,11 +39,10 @@ namespace test { */ class MediaAccessMock : public MediaAccessFacade { - public: - FileHandle queryFile (const char* name) throw(lumiera::error::Invalid); - ChanDesc queryChannel (FileHandle, uint chanNo) throw(); + MediaDesc& queryFile (string const& name) const; + ChanDesc queryChannel (MediaDesc&, uint chanNo) const; }; -}} // namespace backend_interface::test +}} // namespace backend::test #endif diff --git a/tests/components/backend/mediaaccessmocktest.cpp b/tests/components/backend/mediaaccessmocktest.cpp index 60d770b89..8bd3ae026 100644 --- a/tests/components/backend/mediaaccessmocktest.cpp +++ b/tests/components/backend/mediaaccessmocktest.cpp @@ -25,78 +25,69 @@ #include "backend/mediaaccessmock.hpp" #include "lib/test/run.hpp" -//#include "lib/util.hpp" +#include "lib/time/diagnostics.hpp" +#include "lib/symbol.hpp" -//#include <boost/format.hpp> #include <iostream> -//using boost::format; -//using util::isnil; +using lib::Literal; using std::string; using std::cout; -namespace backend_interface - { - namespace test +namespace backend { +namespace test { + + + + + + /******************************************************************************** + * @test inject a Mock object replacing the backend_interface::MediaAccessFacade. + * Verify if the Mock object behaves as expected when calling the Facade. + */ + class MediaAccessMock_test : public Test { - - - - - - /******************************************************************************** - * @test inject a Mock object replacing the backend_interface::MediaAccessFacade. - * Verify if the Mock object behaves as expected when calling the Facade. - */ - class MediaAccessMock_test : public Test - { - typedef MediaAccessFacade MAF; - - virtual void run(Arg) - { - MAF::instance.injectSubclass (new MediaAccessMock); - - queryScenario ("test-1"); - queryScenario ("test-2"); - - MAF::instance.injectSubclass (0); - } - - - /** perform the test: query for an (alledged) file - * and retrieve the mock answer. - */ - void queryScenario (string filename) - { - MAF& maf (MAF::instance()); - MAF::FileHandle fhandle = maf.queryFile (filename.c_str()); - if (!fhandle) - cout << "File \""<<filename<<"\" not accessible\n"; - else - { - cout << "accessing \""<<filename<<"\" ...\n"; - for (int i=0; true; ++i) - { - ChanDesc chan = maf.queryChannel (fhandle, i); - if (!chan.handle) break; - - cout << " Channel-" << i - << ": nameID=" << chan.chanID - << " codecID=" << chan.codecID - << "\n"; - } } } - - - }; - + typedef MediaAccessFacade MAF; - - /** Register this test class... */ - LAUNCHER (MediaAccessMock_test, "unit operate"); - - - - } // namespace test - -} // namespace backend_interface + virtual void run(Arg) + { + MAF::instance.injectSubclass (new MediaAccessMock); + + queryScenario ("test-1"); + queryScenario ("test-2"); + + MAF::instance.injectSubclass (0); + } + + + /** perform the test: query for an (alleged) file + * and retrieve the mock answer. + */ + void queryScenario (string const& filename) + { + MAF& maf (MAF::instance()); + MediaDesc& mhandle = maf.queryFile (filename); + cout << "accessing \""<<filename<<"\" (dur="<<mhandle.length<<")...\n"; + for (int i=0; true; ++i) + { + ChanDesc chan = maf.queryChannel (mhandle, i); + if (!chan.handle) break; + + cout << " Channel-" << i + << ": nameID=" << chan.chanID + << " codecID=" << chan.codecID + << "\n"; + } } + + + }; + + + + /** Register this test class... */ + LAUNCHER (MediaAccessMock_test, "unit operate"); + + + +}} // namespace backend::test diff --git a/tests/components/proc/mobject/session/testclip.cpp b/tests/components/proc/mobject/session/testclip.cpp index 53296fab6..ec8eba4b6 100644 --- a/tests/components/proc/mobject/session/testclip.cpp +++ b/tests/components/proc/mobject/session/testclip.cpp @@ -34,9 +34,10 @@ namespace session { namespace test { using lib::time::Time; + using lib::time::Duration; typedef shared_ptr<asset::Media> PM; typedef backend_interface::MediaAccessFacade MAF; - using backend_interface::test::MediaAccessMock; + using backend::test::MediaAccessMock; using asset::VIDEO; @@ -86,7 +87,7 @@ namespace test { /* == define some data for verification in unit tests == */ - const Time LENGTH_TestClip(0,25,0,0); //////TODO hard wired as of (1/10). See MediaFactory::operator() in media.cpp + const Duration LENGTH_TestClip(Time(0,25,0,0)); //////TODO hard wired as of (1/10). See MediaFactory::operator() in media.cpp / mediaaccessmock.cpp }}} // namespace mobject::session::test diff --git a/tests/components/proc/mobject/session/testclip.hpp b/tests/components/proc/mobject/session/testclip.hpp index 97c6b8554..c62034250 100644 --- a/tests/components/proc/mobject/session/testclip.hpp +++ b/tests/components/proc/mobject/session/testclip.hpp @@ -78,7 +78,7 @@ namespace test { /* == some test data to check == */ - extern const lib::time::Time LENGTH_TestClip; + extern const lib::time::Duration LENGTH_TestClip; }}} // namespace mobject::session::test From e5e62644930e8e5c8ae23881f3db23b8b584e835 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 19 May 2011 23:21:50 +0200 Subject: [PATCH 081/496] technique to install a mock service temporarily --- src/lib/test/mockinjector.hpp | 86 +++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/lib/test/mockinjector.hpp b/src/lib/test/mockinjector.hpp index dbc47d3ed..cc39ba0bd 100644 --- a/src/lib/test/mockinjector.hpp +++ b/src/lib/test/mockinjector.hpp @@ -27,8 +27,11 @@ #include "lib/singleton-factory.hpp" +#include "lib/meta/duck-detector.hpp" #include <boost/scoped_ptr.hpp> +#include <boost/noncopyable.hpp> +#include <boost/utility/enable_if.hpp> namespace lib { @@ -87,5 +90,88 @@ namespace test{ + /** + * Details of creating and accessing mock instances, + * especially for services implemented as Singleton. + */ + namespace mock { + + using boost::enable_if; + using lumiera::Yes_t; + using lumiera::No_t; + + /** + * A Metafunction to find out, if a type in question + * is used as Singleton and especially has been configured + * to inject Mock implementations defined as Subclasses. + */ + template<class MOCK> + class is_Singleton_with_MockInjector + { + META_DETECT_NESTED (ServiceInterface); + META_DETECT_FUNCTION(void, injectSubclass, (TY*) const); + + typedef typename MOCK::ServiceInterface ServiceInterface; + + public: + typedef lib::SingletonFactory<ServiceInterface> SingletonFactory; + + enum{ value = HasNested_ServiceInterface<MOCK>::value + && HasFunSig_injectSubclass<SingletonFactory>::value + }; + }; + + + /** + * Policy-Trait: How to access and inject + * a mock service implementation? + */ + template<class MOCK, class YES =void> + struct AccessPoint; + + template<class MOCK> + struct AccessPoint<MOCK, typename enable_if< is_Singleton_with_MockInjector<MOCK> >::type> + { + typedef typename is_Singleton_with_MockInjector<MOCK>::SingletonFactory ServiceAccessPoint; + + void + activateMock() + { + ServiceAccessPoint().injectSubclass (new MOCK()); + } + void + deactivateMock() + { + ServiceAccessPoint().injectSubclass (0); + } + }; + }//(End) details of injecting the mock implementation + + + + + /** + * Scoped object for installing/deinstalling a mocked service automatically. + * Placing a suitably specialised instance of this template into a local scope + * will inject the corresponding mock installation and remove it when the + * control flow leaves this scope. + */ + template<class SI> + struct Use4Test + : boost::noncopyable + { + Use4Test() + { + mock::AccessPoint<SI>::activateMock(); + } + + ~Use4Test() + { + mock::AccessPoint<SI>::deactivateMock(); + } + }; + + + }} // namespace lib::test #endif From 4b4fcb0c806c55f2fdcfe57cb6050d48c8a2b2ad Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 20 May 2011 02:02:14 +0200 Subject: [PATCH 082/496] use (and verify) this with the MediaAccessMock_test --- src/lib/test/mockinjector.hpp | 135 +++++++++++++----- tests/components/backend/mediaaccessmock.hpp | 3 + .../backend/mediaaccessmocktest.cpp | 5 +- 3 files changed, 108 insertions(+), 35 deletions(-) diff --git a/src/lib/test/mockinjector.hpp b/src/lib/test/mockinjector.hpp index cc39ba0bd..0b308ec19 100644 --- a/src/lib/test/mockinjector.hpp +++ b/src/lib/test/mockinjector.hpp @@ -20,6 +20,40 @@ */ +/** @file mockinjector.hpp + ** Support for unit-testing with a mock implementation of some singleton service. + ** Using a Singleton to access a global service (instead of using a dependency injection + ** framework) has the downside of making unit-tests hard to write. When the code to be + ** tested accesses the global service, usually, several further services must be available. + ** Usually this (and the lack of flexibility) counts as the main arguments against using + ** Singletons. Yet still our current analysis (as of 2011) for Lumiera is that this + ** drawback doesn't justify all the overhead of writing our own dependency-injection + ** container (given the lack of pre-existing suitable C++ implementations). + ** + ** lib::test::MockInjector provides a mechanism to overcome this problem. Given + ** a Singleton service, it allows to install a subclass of the service interface + ** temporarily as a mock implementation. As an additional convenience shortcut, + ** the Use4Test template can be used as a scoped variable to automate this process + ** of installing and removing the mock service within the call scope of a single test + ** method or test object. + ** + ** \par prerequisites + ** The Singleton needs to be managed and accessed through Lumiera's lib::Singleton + ** factory. Moreover, an explicit template specialisation to use MockInjector instead + ** of the plain lib::SingletonFactory needs to be given globally, for the service interface + ** to use. For this template specialisation, a forward declaration of the service is + ** sufficient. See singleton-preconfigure.hpp for examples. + ** + ** The Mock implementation of the service needs to be default constructible and + ** assignment compatible to the service interface. Moreover, the mock implementation class + ** must expose a typedef \c ServiceInterface pointing to this interface. The instance of + ** the mock service implementation will be created in heap memory and managed by a static + ** smart-ptr variable; it will be destroyed when leaving the scope marked by Use4Test. + ** + ** @see SingletonFactory + ** @see SingletonTestMock_test + ** @see MediaAccessMock_test usage example + */ #ifndef LUMIERA_TEST_MOCKINJECTOR_H @@ -31,10 +65,15 @@ #include <boost/scoped_ptr.hpp> #include <boost/noncopyable.hpp> +#include <boost/static_assert.hpp> #include <boost/utility/enable_if.hpp> namespace lib { + template <class SI> + class Singleton; // forward decl; see singleton-preconfigure + + namespace test{ using boost::scoped_ptr; @@ -49,7 +88,7 @@ namespace test{ template<class SI> class MockInjector : public SingletonFactory<SI> { - scoped_ptr<SI> mock_; + static scoped_ptr<SI> mock_; public: /** Overwriting the normal Singleton creation Interface @@ -74,32 +113,59 @@ namespace test{ MockInjector () {}; - /** @note MockInjector singleton factory objects can be copied, - * but the copy will start out with clean internal state, - * i.e. exhibiting normal SingletonFactory behaviour - * without mock object. - */ - MockInjector (const MockInjector& other) - : SingletonFactory<SI>(other), mock_(0) { } - - MockInjector<SI>& operator= (const MockInjector<SI>& other) - { - return SingletonFactory<SI>::operator= (other); - } + // MockInjector objects may be copied freely (no object fields) }; + /** embedded static var to hold onto a configured mock implementation */ + template<class SI> + scoped_ptr<SI> MockInjector<SI>::mock_; - /** - * Details of creating and accessing mock instances, - * especially for services implemented as Singleton. - */ + + + + + /** Details of creating and accessing mock instances, * + * especially for services implemented as Singleton. */ namespace mock { using boost::enable_if; using lumiera::Yes_t; using lumiera::No_t; + /** + * Metafunction: does the Type in question + * give us a clue about what service interface to use? + */ + template<class MOCK> + class defines_ServiceInterface + { + META_DETECT_NESTED (ServiceInterface); + + public: + enum{ value = HasNested_ServiceInterface<MOCK>::value + }; + }; + + /** + * Trait template: getting the types of the + * Service interface and SingletonFactory + */ + template<class IMP, class YES =void> + struct SingletonAccess + { + typedef No_t ServiceInterface; + typedef No_t Factory; + }; + + template<class IMP> + struct SingletonAccess<IMP, typename enable_if< defines_ServiceInterface<IMP> >::type> + { + typedef typename IMP::ServiceInterface ServiceInterface; + typedef lib::Singleton<ServiceInterface> Factory; + }; + + /** * A Metafunction to find out, if a type in question * is used as Singleton and especially has been configured @@ -108,16 +174,14 @@ namespace test{ template<class MOCK> class is_Singleton_with_MockInjector { - META_DETECT_NESTED (ServiceInterface); - META_DETECT_FUNCTION(void, injectSubclass, (TY*) const); - - typedef typename MOCK::ServiceInterface ServiceInterface; + typedef typename SingletonAccess<MOCK>::ServiceInterface Interface; + typedef typename SingletonAccess<MOCK>::Factory SingletonFactory; + META_DETECT_MEMBER (injectSubclass); public: - typedef lib::SingletonFactory<ServiceInterface> SingletonFactory; - enum{ value = HasNested_ServiceInterface<MOCK>::value - && HasFunSig_injectSubclass<SingletonFactory>::value + enum{ value = defines_ServiceInterface<MOCK>::value + && HasMember_injectSubclass<SingletonFactory>::value }; }; @@ -127,19 +191,24 @@ namespace test{ * a mock service implementation? */ template<class MOCK, class YES =void> - struct AccessPoint; - + struct AccessPoint + { + BOOST_STATIC_ASSERT (is_Singleton_with_MockInjector<MOCK>::value ); + }; // Hint: (1) the mock needs a typedef pointing to the ServiceInterface + // (2) prepare the Singleton-Factory for injecting a Mock implementation... + // look into singleton-preconfigure.hpp for examples! + template<class MOCK> struct AccessPoint<MOCK, typename enable_if< is_Singleton_with_MockInjector<MOCK> >::type> { - typedef typename is_Singleton_with_MockInjector<MOCK>::SingletonFactory ServiceAccessPoint; + typedef typename SingletonAccess<MOCK>::Factory ServiceAccessPoint; - void + static void activateMock() { ServiceAccessPoint().injectSubclass (new MOCK()); } - void + static void deactivateMock() { ServiceAccessPoint().injectSubclass (0); @@ -155,19 +224,21 @@ namespace test{ * Placing a suitably specialised instance of this template into a local scope * will inject the corresponding mock installation and remove it when the * control flow leaves this scope. + * @param MOCK the concrete mock implementation to inject. It needs to + * subclass the service interface and expose a typedef \c ServiceInterace */ - template<class SI> + template<class MOCK> struct Use4Test : boost::noncopyable { Use4Test() { - mock::AccessPoint<SI>::activateMock(); + mock::AccessPoint<MOCK>::activateMock(); } ~Use4Test() { - mock::AccessPoint<SI>::deactivateMock(); + mock::AccessPoint<MOCK>::deactivateMock(); } }; diff --git a/tests/components/backend/mediaaccessmock.hpp b/tests/components/backend/mediaaccessmock.hpp index dce886aa9..ad22aaabb 100644 --- a/tests/components/backend/mediaaccessmock.hpp +++ b/tests/components/backend/mediaaccessmock.hpp @@ -41,6 +41,9 @@ namespace test { { MediaDesc& queryFile (string const& name) const; ChanDesc queryChannel (MediaDesc&, uint chanNo) const; + + public: + typedef MediaAccessFacade ServiceInterface; }; diff --git a/tests/components/backend/mediaaccessmocktest.cpp b/tests/components/backend/mediaaccessmocktest.cpp index 8bd3ae026..b3dfbcd84 100644 --- a/tests/components/backend/mediaaccessmocktest.cpp +++ b/tests/components/backend/mediaaccessmocktest.cpp @@ -30,6 +30,7 @@ #include <iostream> +using lib::test::Use4Test; using lib::Literal; using std::string; using std::cout; @@ -52,12 +53,10 @@ namespace test { virtual void run(Arg) { - MAF::instance.injectSubclass (new MediaAccessMock); + Use4Test<MediaAccessMock> within_this_scope; queryScenario ("test-1"); queryScenario ("test-2"); - - MAF::instance.injectSubclass (0); } From 12f43078a2b80491a0280b8ac89cc8783d3ce3d9 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 20 May 2011 02:17:20 +0200 Subject: [PATCH 083/496] fix some names --- .../{mediaaccessfacade.cpp => media-access-facade.cpp} | 2 +- .../{mediaaccessfacade.hpp => media-access-facade.hpp} | 6 +++--- src/lib/singleton-preconfigure.hpp | 2 +- src/lib/test/{mockinjector.hpp => mock-injector.hpp} | 8 ++++---- src/proc/asset/media.cpp | 2 +- ...mediaaccessmocktest.cpp => media-access-mock-test.cpp} | 4 ++-- .../{mediaaccessmock.cpp => media-access-mock.cpp} | 4 ++-- .../{mediaaccessmock.hpp => media-access-mock.hpp} | 8 ++++---- tests/components/proc/mobject/session/testclip.cpp | 4 ++-- 9 files changed, 20 insertions(+), 20 deletions(-) rename src/backend/{mediaaccessfacade.cpp => media-access-facade.cpp} (97%) rename src/backend/{mediaaccessfacade.hpp => media-access-facade.hpp} (96%) rename src/lib/test/{mockinjector.hpp => mock-injector.hpp} (97%) rename tests/components/backend/{mediaaccessmocktest.cpp => media-access-mock-test.cpp} (96%) rename tests/components/backend/{mediaaccessmock.cpp => media-access-mock.cpp} (97%) rename tests/components/backend/{mediaaccessmock.hpp => media-access-mock.hpp} (85%) diff --git a/src/backend/mediaaccessfacade.cpp b/src/backend/media-access-facade.cpp similarity index 97% rename from src/backend/mediaaccessfacade.cpp rename to src/backend/media-access-facade.cpp index 50daf47c1..a04cd76f7 100644 --- a/src/backend/mediaaccessfacade.cpp +++ b/src/backend/media-access-facade.cpp @@ -21,7 +21,7 @@ * *****************************************************/ -#include "backend/mediaaccessfacade.hpp" +#include "backend/media-access-facade.hpp" #include "lib/util.hpp" diff --git a/src/backend/mediaaccessfacade.hpp b/src/backend/media-access-facade.hpp similarity index 96% rename from src/backend/mediaaccessfacade.hpp rename to src/backend/media-access-facade.hpp index 241143985..3e51d96fa 100644 --- a/src/backend/mediaaccessfacade.hpp +++ b/src/backend/media-access-facade.hpp @@ -1,5 +1,5 @@ /* - MEDIAACCESSFACADE.hpp - functions for querying media file and channels. + MEDIA-ACCESS-FACADE.hpp - functions for querying media file and channels. Copyright (C) Lumiera.org 2008, Hermann Vosseler <Ichthyostega@web.de> @@ -21,8 +21,8 @@ */ -#ifndef BACKEND_INTERFACE_MEDIAACCESSFACADE_H -#define BACKEND_INTERFACE_MEDIAACCESSFACADE_H +#ifndef BACKEND_INTERFACE_MEDIA_ACCESS_FACADE_H +#define BACKEND_INTERFACE_MEDIA_ACCESS_FACADE_H #include "lib/error.hpp" diff --git a/src/lib/singleton-preconfigure.hpp b/src/lib/singleton-preconfigure.hpp index 70c911a52..faca11e86 100644 --- a/src/lib/singleton-preconfigure.hpp +++ b/src/lib/singleton-preconfigure.hpp @@ -44,7 +44,7 @@ #ifndef LIB_SINGLETON_PRECONFIGURE_H #define LIB_SINGLETON_PRECONFIGURE_H -#include "lib/test/mockinjector.hpp" +#include "lib/test/mock-injector.hpp" namespace lib { diff --git a/src/lib/test/mockinjector.hpp b/src/lib/test/mock-injector.hpp similarity index 97% rename from src/lib/test/mockinjector.hpp rename to src/lib/test/mock-injector.hpp index 0b308ec19..116711966 100644 --- a/src/lib/test/mockinjector.hpp +++ b/src/lib/test/mock-injector.hpp @@ -1,5 +1,5 @@ /* - MOCKINJECTOR.hpp - replacement singleton factory for injecting Test-Mock objects + MOCK-INJECTOR.hpp - replacement singleton factory for injecting Test-Mock objects Copyright (C) Lumiera.org 2008, Hermann Vosseler <Ichthyostega@web.de> @@ -20,7 +20,7 @@ */ -/** @file mockinjector.hpp +/** @file mock-injector.hpp ** Support for unit-testing with a mock implementation of some singleton service. ** Using a Singleton to access a global service (instead of using a dependency injection ** framework) has the downside of making unit-tests hard to write. When the code to be @@ -56,8 +56,8 @@ */ -#ifndef LUMIERA_TEST_MOCKINJECTOR_H -#define LUMIERA_TEST_MOCKINJECTOR_H +#ifndef LUMIERA_TEST_MOCK_INJECTOR_H +#define LUMIERA_TEST_MOCK_INJECTOR_H #include "lib/singleton-factory.hpp" diff --git a/src/proc/asset/media.cpp b/src/proc/asset/media.cpp index c357338e5..40034bd42 100644 --- a/src/proc/asset/media.cpp +++ b/src/proc/asset/media.cpp @@ -29,7 +29,7 @@ #include "proc/asset/unknown.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/session/mobjectfactory.hpp" -#include "backend/mediaaccessfacade.hpp" +#include "backend/media-access-facade.hpp" #include "lib/time/timevalue.hpp" #include "lib/util.hpp" #include "include/logging.h" diff --git a/tests/components/backend/mediaaccessmocktest.cpp b/tests/components/backend/media-access-mock-test.cpp similarity index 96% rename from tests/components/backend/mediaaccessmocktest.cpp rename to tests/components/backend/media-access-mock-test.cpp index b3dfbcd84..d79525396 100644 --- a/tests/components/backend/mediaaccessmocktest.cpp +++ b/tests/components/backend/media-access-mock-test.cpp @@ -21,8 +21,8 @@ * *****************************************************/ -#include "backend/mediaaccessfacade.hpp" -#include "backend/mediaaccessmock.hpp" +#include "backend/media-access-facade.hpp" +#include "backend/media-access-mock.hpp" #include "lib/test/run.hpp" #include "lib/time/diagnostics.hpp" diff --git a/tests/components/backend/mediaaccessmock.cpp b/tests/components/backend/media-access-mock.cpp similarity index 97% rename from tests/components/backend/mediaaccessmock.cpp rename to tests/components/backend/media-access-mock.cpp index 46c8d07dc..f0b7b36d4 100644 --- a/tests/components/backend/mediaaccessmock.cpp +++ b/tests/components/backend/media-access-mock.cpp @@ -1,5 +1,5 @@ /* - MediaAccessMock.hpp - a test (stub) target object for testing the factories + MediaAccessMock - a test (stub) target object for testing the factories Copyright (C) Lumiera.org 2008, Hermann Vosseler <Ichthyostega@web.de> @@ -33,7 +33,7 @@ */ -#include "backend/mediaaccessmock.hpp" +#include "backend/media-access-mock.hpp" #include "proc/mobject/session/testclip.hpp" #include "lib/util.hpp" diff --git a/tests/components/backend/mediaaccessmock.hpp b/tests/components/backend/media-access-mock.hpp similarity index 85% rename from tests/components/backend/mediaaccessmock.hpp rename to tests/components/backend/media-access-mock.hpp index ad22aaabb..c44e61b3c 100644 --- a/tests/components/backend/mediaaccessmock.hpp +++ b/tests/components/backend/media-access-mock.hpp @@ -1,5 +1,5 @@ /* - MEDIAACCESSMOCK.hpp - a test (stub) target object for testing the factories + MEDIA-ACCESS-MOCK.hpp - a test (stub) target object for testing the factories Copyright (C) Lumiera.org 2008, Hermann Vosseler <Ichthyostega@web.de> @@ -21,11 +21,11 @@ */ -#ifndef BACKEND_TEST_MEDIAACCESSMOCK_H -#define BACKEND_TEST_MEDIAACCESSMOCK_H +#ifndef BACKEND_TEST_MEDIA_ACCESS_MOCK_H +#define BACKEND_TEST_MEDIA_ACCESS_MOCK_H -#include "backend/mediaaccessfacade.hpp" +#include "backend/media-access-facade.hpp" diff --git a/tests/components/proc/mobject/session/testclip.cpp b/tests/components/proc/mobject/session/testclip.cpp index ec8eba4b6..74ee791c5 100644 --- a/tests/components/proc/mobject/session/testclip.cpp +++ b/tests/components/proc/mobject/session/testclip.cpp @@ -22,8 +22,8 @@ #include "proc/mobject/session/testclip.hpp" -#include "backend/mediaaccessfacade.hpp" -#include "backend/mediaaccessmock.hpp" +#include "backend/media-access-facade.hpp" +#include "backend/media-access-mock.hpp" #include "proc/asset/media.hpp" #include "proc/asset/clip.hpp" #include "lib/singleton.hpp" From 51fa9fe73564f7fbc7282a4c7722453715a3c889 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 20 May 2011 02:59:29 +0200 Subject: [PATCH 084/496] MediaFactory now using MediaAccessFacade to determine media Duration --- src/proc/asset/media.cpp | 2 +- .../components/backend/media-access-mock.cpp | 5 +++ .../components/backend/media-access-mock.hpp | 1 + .../proc/asset/create-asset-test.cpp | 44 ++++++++++--------- .../proc/asset/dependent-assets-test.cpp | 6 ++- .../proc/asset/identityofassetstest.cpp | 16 ++++--- tests/components/proc/asset/makecliptest.cpp | 4 ++ .../proc/asset/orderingofassetstest.cpp | 15 ++++--- .../proc/mobject/builder/buildertooltest.cpp | 9 +++- .../proc/mobject/mobject-interface-test.cpp | 6 +++ .../proc/mobject/mobject-ref-test.cpp | 4 ++ .../proc/mobject/placement-hierarchy-test.cpp | 5 +++ 12 files changed, 82 insertions(+), 35 deletions(-) diff --git a/src/proc/asset/media.cpp b/src/proc/asset/media.cpp index 40034bd42..eecc9d4fb 100644 --- a/src/proc/asset/media.cpp +++ b/src/proc/asset/media.cpp @@ -165,7 +165,7 @@ namespace asset { if (isnil (key.name)) key.name=extractName(file); MediaAccessFacade& maf = MediaAccessFacade::instance(); - MediaDesc& handle = maf.queryFile(file); + MediaDesc& handle = maf.queryFile(key.name); Duration length = handle.length; TODO ("detecting and wiring multichannel compound media!"); diff --git a/tests/components/backend/media-access-mock.cpp b/tests/components/backend/media-access-mock.cpp index f0b7b36d4..01839dfae 100644 --- a/tests/components/backend/media-access-mock.cpp +++ b/tests/components/backend/media-access-mock.cpp @@ -106,6 +106,11 @@ namespace test { (*this)["test-2"].globalLength(LENGTH_TestClip).channel("video","H264") .channel("audio-L","PCM") .channel("audio-R","PCM"); + (*this)["test-3"].globalLength(LENGTH_TestClip).channel("audio","PCM"); + (*this)["test-4"].globalLength(LENGTH_TestClip).channel("audio-W","PCM") + .channel("audio-X","PCM") + .channel("audio-Y","PCM") + .channel("audio-Z","PCM"); // ----------------------------------------------------------------------TESTCASES } diff --git a/tests/components/backend/media-access-mock.hpp b/tests/components/backend/media-access-mock.hpp index c44e61b3c..3c2fdb354 100644 --- a/tests/components/backend/media-access-mock.hpp +++ b/tests/components/backend/media-access-mock.hpp @@ -26,6 +26,7 @@ #include "backend/media-access-facade.hpp" +#include "lib/test/mock-injector.hpp" diff --git a/tests/components/proc/asset/create-asset-test.cpp b/tests/components/proc/asset/create-asset-test.cpp index 716155f7d..f03893198 100644 --- a/tests/components/proc/asset/create-asset-test.cpp +++ b/tests/components/proc/asset/create-asset-test.cpp @@ -30,7 +30,9 @@ #include "proc/asset/proc.hpp" #include "proc/asset/asset-diagnostics.hpp" +#include "backend/media-access-mock.hpp" +using lib::test::Use4Test; using util::isnil; using std::string; @@ -49,6 +51,8 @@ namespace test { { virtual void run(Arg arg) { + Use4Test<backend::test::MediaAccessMock> within_this_scope; + createMedia(); factoryVariants(); @@ -70,10 +74,10 @@ namespace test { void createMedia() { Category cat(VIDEO,"bin1"); - Asset::Ident key("Name-1", cat, "ichthyo", 5); + Asset::Ident key("test-1", cat, "ichthyo", 5); PM mm1 = asset::Media::create(key,"testfile.mov"); - PM mm2 = asset::Media::create("testfile1.mov", cat); - PM mm3 = asset::Media::create("testfile2.mov", VIDEO); + PM mm2 = asset::Media::create("test-1.mov", cat); + PM mm3 = asset::Media::create("test-2.mov", VIDEO); // Assets have been registered and can be retrieved by ID AssetManager& aMang = AssetManager::instance(); @@ -124,9 +128,9 @@ namespace test { // checking the Ident-Fields - CHECK (mm1->ident.name == "Name-1"); - CHECK (mm2->ident.name == "testfile1"); - CHECK (mm3->ident.name == "testfile2"); + CHECK (mm1->ident.name == "test-1"); + CHECK (mm2->ident.name == "test-1"); + CHECK (mm3->ident.name == "test-2"); CHECK (cat == Category (VIDEO,"bin1")); CHECK (mm1->ident.category == Category (VIDEO,"bin1")); @@ -142,8 +146,8 @@ namespace test { CHECK (mm3->ident.version == 1); CHECK (mm1->getFilename() == "testfile.mov"); - CHECK (mm2->getFilename() == "testfile1.mov"); - CHECK (mm3->getFilename() == "testfile2.mov"); + CHECK (mm2->getFilename() == "test-1.mov"); + CHECK (mm3->getFilename() == "test-2.mov"); TRACE (asset_mem, "leaving test method scope"); @@ -159,25 +163,25 @@ namespace test { { PM candi; - Asset::Ident key1("Au-1", Category(AUDIO), "ichthyo", 5); + Asset::Ident key1("test-1", Category(AUDIO), "ichthyo", 5); candi = asset::Media::create(key1); CHECK ( checkProperties (candi, key1, "")); - candi = asset::Media::create(key1, string("testfile.wav")); - CHECK ( checkProperties (candi, key1, "testfile.wav")); + candi = asset::Media::create(key1, string("test-1.wav")); + CHECK ( checkProperties (candi, key1, "test-1.wav")); Asset::Ident key2("", Category(AUDIO), "ichthyo", 5); - candi = asset::Media::create(key2, string("testfile2.wav")); - CHECK ( checkProperties (candi, key2, "testfile2.wav")); - CHECK (key2.name == "testfile2"); // name filled in automatically + candi = asset::Media::create(key2, string("test-2.wav")); + CHECK ( checkProperties (candi, key2, "test-2.wav")); + CHECK (key2.name == "test-2"); // name filled in automatically - candi = asset::Media::create(string("testfile3.wav"), Category(AUDIO)); - CHECK ( checkProperties (candi, Asset::Ident("testfile3", Category(AUDIO), "lumi", 1) - , "testfile3.wav")); + candi = asset::Media::create(string("test-3.wav"), Category(AUDIO)); + CHECK ( checkProperties (candi, Asset::Ident("test-3", Category(AUDIO), "lumi", 1) + , "test-3.wav")); - candi = asset::Media::create("some/path/testfile4.wav", Category(AUDIO)); - CHECK ( checkProperties (candi, Asset::Ident("testfile4", Category(AUDIO), "lumi", 1) - , "some/path/testfile4.wav")); + candi = asset::Media::create("some/path/test-4.wav", Category(AUDIO)); + CHECK ( checkProperties (candi, Asset::Ident("test-4", Category(AUDIO), "lumi", 1) + , "some/path/test-4.wav")); candi = asset::Media::create("", Category(AUDIO,"sub/bin")); CHECK ( checkProperties (candi, Asset::Ident("nil", Category(AUDIO,"sub/bin"), "lumi", 1) diff --git a/tests/components/proc/asset/dependent-assets-test.cpp b/tests/components/proc/asset/dependent-assets-test.cpp index 65783b20c..0644dbcda 100644 --- a/tests/components/proc/asset/dependent-assets-test.cpp +++ b/tests/components/proc/asset/dependent-assets-test.cpp @@ -24,10 +24,12 @@ #include "lib/test/run.hpp" #include "proc/asset/testasset.hpp" #include "proc/asset/asset-diagnostics.hpp" +#include "backend/media-access-mock.hpp" #include "proc/asset/media.hpp" #include "proc/asset/clip.hpp" #include "lib/util.hpp" +using lib::test::Use4Test; using util::contains; using util::isnil; @@ -186,11 +188,13 @@ namespace test { /** @test each real world asset subclass has to care * for registering and deregistering any additional * dependencies. Here we collect some more prominent - * examples (and hopfully don't fail to cover any + * examples (and hopefully don't fail to cover any * important special cases...) */ void checkRealAssetDependencyRegistration () { + Use4Test<backend::test::MediaAccessMock> within_this_scope; + // -----Media and Clip-------------------------------- typedef P<Media> PM; typedef P<Clip> PC; diff --git a/tests/components/proc/asset/identityofassetstest.cpp b/tests/components/proc/asset/identityofassetstest.cpp index 6f13cb7fc..2e1b4739c 100644 --- a/tests/components/proc/asset/identityofassetstest.cpp +++ b/tests/components/proc/asset/identityofassetstest.cpp @@ -30,7 +30,9 @@ #include "proc/asset/proc.hpp" #include "proc/asset/asset-diagnostics.hpp" +#include "backend/media-access-mock.hpp" +using lib::test::Use4Test; using util::isnil; using std::string; @@ -68,13 +70,15 @@ namespace test { */ void createDuplicate() { - PM mm1 = asset::Media::create ("testfile1.mov", VIDEO); + Use4Test<backend::test::MediaAccessMock> within_this_scope; + + PM mm1 = asset::Media::create ("test-1.mov", VIDEO); Asset::Ident idi (mm1->ident); // duplicate Ident record PM mm1X = asset::Media::create (idi); // note: we actually don't call any ctor CHECK (mm1 == mm1X); // instead, we got mm1 back. - PM mm2 = asset::Media::create (idi,"testfile2.mov"); + PM mm2 = asset::Media::create (idi,"test-2.mov"); CHECK (mm1->getID() == mm2->getID()); // different object, same hash @@ -84,10 +88,10 @@ namespace test { CHECK (aMang.known (mm1->getID())); CHECK (aMang.known (mm2->getID())); - CHECK (mm1->ident.name == "testfile1"); - CHECK (mm2->ident.name == "testfile1"); - CHECK (mm1->getFilename() == "testfile1.mov"); - CHECK (mm2->getFilename() == "testfile2.mov"); + CHECK (mm1->ident.name == "test-1"); + CHECK (mm2->ident.name == "test-1"); + CHECK (mm1->getFilename() == "test-1.mov"); + CHECK (mm2->getFilename() == "test-2.mov"); TRACE (asset_mem, "leaving test method scope"); diff --git a/tests/components/proc/asset/makecliptest.cpp b/tests/components/proc/asset/makecliptest.cpp index 0eeb060c7..99a7a3812 100644 --- a/tests/components/proc/asset/makecliptest.cpp +++ b/tests/components/proc/asset/makecliptest.cpp @@ -30,7 +30,9 @@ #include "proc/asset/media.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/asset/asset-diagnostics.hpp" +#include "backend/media-access-mock.hpp" +using lib::test::Use4Test; using util::contains; using util::isnil; using std::string; @@ -56,6 +58,8 @@ namespace asset { virtual void run (Arg) { + Use4Test<backend::test::MediaAccessMock> within_this_scope; + PM mm = asset::Media::create("test-1", VIDEO); PC cc = mm->createClip(); diff --git a/tests/components/proc/asset/orderingofassetstest.cpp b/tests/components/proc/asset/orderingofassetstest.cpp index cd4009e7f..cff898acd 100644 --- a/tests/components/proc/asset/orderingofassetstest.cpp +++ b/tests/components/proc/asset/orderingofassetstest.cpp @@ -29,7 +29,9 @@ #include "proc/asset/proc.hpp" #include "proc/asset/asset-diagnostics.hpp" +#include "backend/media-access-mock.hpp" +using lib::test::Use4Test; using util::isnil; using std::string; @@ -57,19 +59,22 @@ namespace asset { { virtual void run(Arg) { - Asset::Ident key1("Au-1", Category(AUDIO), "ichthyo", 5); + Use4Test<backend::test::MediaAccessMock> within_this_scope; + + + Asset::Ident key1("test-1", Category(AUDIO), "ichthyo", 5); PAsset mm1 = asset::Media::create(key1, "Name-1"); - Asset::Ident key2("Au-1", Category(AUDIO), "ichthyo", 7); + Asset::Ident key2("test-1", Category(AUDIO), "ichthyo", 7); PAsset mm2 = asset::Media::create(key2, "Name-2"); - Asset::Ident key3("Au-2", Category(AUDIO), "ichthyo", 5); + Asset::Ident key3("test-2", Category(AUDIO), "ichthyo", 5); PAsset mm3 = asset::Media::create(key3, "Name-3"); - Asset::Ident key4("Au-2", Category(AUDIO), "stega", 5); + Asset::Ident key4("test-2", Category(AUDIO), "stega", 5); PAsset mm4 = asset::Media::create(key4, "Name-4"); - Asset::Ident key5("Au-1", Category(VIDEO), "ichthyo", 5); + Asset::Ident key5("test-1", Category(VIDEO), "ichthyo", 5); PAsset mm5 = asset::Media::create(key5, "Name-5"); diff --git a/tests/components/proc/mobject/builder/buildertooltest.cpp b/tests/components/proc/mobject/builder/buildertooltest.cpp index 52a85f0af..21d4cba66 100644 --- a/tests/components/proc/mobject/builder/buildertooltest.cpp +++ b/tests/components/proc/mobject/builder/buildertooltest.cpp @@ -29,6 +29,7 @@ #include "proc/asset/media.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/test-dummy-mobject.hpp" +#include "backend/media-access-mock.hpp" #include "lib/util.hpp" #include <iostream> @@ -45,6 +46,7 @@ namespace test { using session::Clip; using session::AbstractMO; + using lib::test::Use4Test; using namespace mobject::test; @@ -106,9 +108,12 @@ namespace test { */ class BuilderTool_test : public Test { - virtual void run(Arg) + virtual void + run(Arg) { - + Use4Test<backend::test::MediaAccessMock> within_this_scope; + + TestTool t1; BuilderTool& tool = t1; diff --git a/tests/components/proc/mobject/mobject-interface-test.cpp b/tests/components/proc/mobject/mobject-interface-test.cpp index 3b5f64fd1..26e893648 100644 --- a/tests/components/proc/mobject/mobject-interface-test.cpp +++ b/tests/components/proc/mobject/mobject-interface-test.cpp @@ -35,6 +35,7 @@ //#include "proc/mobject/session/clip.hpp" //#include "proc/mobject/explicitplacement.hpp" #include "proc/mobject/test-dummy-mobject.hpp" +#include "backend/media-access-mock.hpp" //#include "lib/test/test-helper.hpp" #include "lib/time/timevalue.hpp" @@ -42,6 +43,7 @@ + namespace mobject { namespace test { @@ -51,6 +53,7 @@ namespace test { using std::endl; using lib::Symbol; + using lib::test::Use4Test; using lib::time::Duration; using lib::time::FSecs; using lib::time::Time; @@ -80,6 +83,9 @@ namespace test { virtual void run (Arg) { + Use4Test<backend::test::MediaAccessMock> within_this_scope; + + PMO testClip1 = asset::Media::create("test-1", asset::VIDEO)->createClip(); PMO testClip2 = asset::Media::create("test-2", asset::VIDEO)->createClip(); diff --git a/tests/components/proc/mobject/mobject-ref-test.cpp b/tests/components/proc/mobject/mobject-ref-test.cpp index 91b9d9859..3aa6fe976 100644 --- a/tests/components/proc/mobject/mobject-ref-test.cpp +++ b/tests/components/proc/mobject/mobject-ref-test.cpp @@ -32,12 +32,14 @@ #include "proc/mobject/session/clip.hpp" #include "proc/mobject/explicitplacement.hpp" #include "proc/mobject/test-dummy-mobject.hpp" +#include "backend/media-access-mock.hpp" #include "lib/test/test-helper.hpp" #include "lib/time/timevalue.hpp" #include "lib/util.hpp" #include <iostream> +using lib::test::Use4Test; using lib::test::showSizeof; using lib::time::Duration; using lib::time::FSecs; @@ -101,6 +103,8 @@ namespace test { virtual void run (Arg) { + Use4Test<backend::test::MediaAccessMock> within_this_scope; + // create data simulating a "Session" PMObj testClip1 = asset::Media::create("test-1", asset::VIDEO)->createClip(); diff --git a/tests/components/proc/mobject/placement-hierarchy-test.cpp b/tests/components/proc/mobject/placement-hierarchy-test.cpp index 12196b603..e3657229c 100644 --- a/tests/components/proc/mobject/placement-hierarchy-test.cpp +++ b/tests/components/proc/mobject/placement-hierarchy-test.cpp @@ -23,6 +23,7 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" +#include "backend/media-access-mock.hpp" #include "proc/mobject/test-dummy-mobject.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/placement.hpp" @@ -34,6 +35,7 @@ #include <iostream> using lib::HashIndexed; +using lib::test::Use4Test; using std::tr1::shared_ptr; using std::string; @@ -65,6 +67,9 @@ namespace test { virtual void run (Arg) { + Use4Test<backend::test::MediaAccessMock> within_this_scope; + + typedef Placement<MObject> PMObj; typedef TestPlacement<> PDummy; typedef TestPlacement<TestSubMO1> PSub1; From 1aafb07a438736de402fdb309c24f7a9c0a0a5a0 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 20 May 2011 03:39:07 +0200 Subject: [PATCH 085/496] fix remaining issues turned up by switching the time handling --- src/proc/mobject/mobject-ref.hpp | 2 +- tests/40components.tests | 2 +- tests/45controller.tests | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/proc/mobject/mobject-ref.hpp b/src/proc/mobject/mobject-ref.hpp index e4e5b3935..36cbdd102 100644 --- a/src/proc/mobject/mobject-ref.hpp +++ b/src/proc/mobject/mobject-ref.hpp @@ -147,7 +147,7 @@ namespace mobject { /** resolves the referred placement to an * ExplicitPlacement and returns the found start time */ - lib::time::Time const& + lib::time::Time getStartTime() { return pRef_.resolve().time; diff --git a/tests/40components.tests b/tests/40components.tests index ca4203a44..22defbc85 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -52,7 +52,7 @@ out-lit: Binding[advice.type.n3lib6advice4test12_GLOBAL__N_111DummyAdviceE/0(), out-lit: b0==Binding[] out-lit: b1==Binding[cat1/0(), cat2/0()] out-lit: b2==Binding[cat1/0(), cat2/0()] -out-lit: b2==Binding[advice.type.n7lumiera4TimeE/0(), cat1/0(), cat2/0(), cat3/1(zzz)] +out-lit: b2==Binding[advice.type.n3lib4time4TimeE/0(), cat1/0(), cat2/0(), cat3/1(zzz)] return: 0 END diff --git a/tests/45controller.tests b/tests/45controller.tests index 20deba21c..81a23996a 100644 --- a/tests/45controller.tests +++ b/tests/45controller.tests @@ -37,7 +37,7 @@ END TEST "build argument accepting function" ArgumentTupleAccept_test <<END out: sizeof\( .+control.+TestClass.+ \) = 1 -out: sizeof\( .+control.+TestClass.+lumiera.Time.+ \) = (12|16) +out: sizeof\( .+control.+TestClass.+lib.time.TimeVar.+ \) = (12|16) out: 0:..:..\.000 return: 0 END From 631b4472688a392e0fec5ad1c081a2f766301345 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 20 May 2011 17:15:08 +0200 Subject: [PATCH 086/496] SCons: check for official GDL prior to searching our gdl-lum ...most current systems provide a suitable GDL package, thus our curstom package is rarely necessary. --- SConstruct | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/SConstruct b/SConstruct index 9665854f4..a2f6750cf 100644 --- a/SConstruct +++ b/SConstruct @@ -284,11 +284,11 @@ def configurePlatform(env): problems.append('Unable to configure Cairo--') verGDL = '2.27.1' - if not conf.CheckPkgConfig('gdl-lum', verGDL, alias='gdl'): - print 'Custom package "gdl-lum" not found. Trying official GDL release >=%s...' % verGDL - if not conf.CheckPkgConfig('gdl-1.0', verGDL, alias='gdl'): - problems.append('GNOME Docking Library not found. We either need a very recent GDL ' - 'version (>=%s), or the custom package "gdl-lum".' % verGDL) + 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.') From 2dde33ec42d2b254e9af2b9550fb1bee58246cdf Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 20 May 2011 18:25:55 +0200 Subject: [PATCH 087/496] remove unecessary double calls to lumiera_config_destroy() config system is initialised and closed automatically --- src/common/config.c | 2 +- tests/common/test-config.c | 38 ++++++-------------------------------- 2 files changed, 7 insertions(+), 33 deletions(-) diff --git a/src/common/config.c b/src/common/config.c index a767cce96..8fba63e36 100644 --- a/src/common/config.c +++ b/src/common/config.c @@ -141,7 +141,7 @@ lumiera_config_destroy () lumiera_global_config = NULL; } else - WARN (config_dbg, "Tried to destroy non initialized config subsystem"); + WARN (config_dbg, "Config system not initialised or already destroyed"); } diff --git a/tests/common/test-config.c b/tests/common/test-config.c index e1a72636d..8ac469fe3 100644 --- a/tests/common/test-config.c +++ b/tests/common/test-config.c @@ -30,10 +30,15 @@ TESTS_BEGIN TEST (init) { -/* lumiera_config_init ("./"); */ +/* Note: lumiera_config_init and lumiera_config_destroy are + * invoked automatically from ConfigFacade + */ printf ("initialized\n"); lumiera_config_destroy (); printf ("destroyed\n"); + /* there will be a warning about destroying the already + * destroyed or uninitialised config system.... + */ } @@ -56,8 +61,6 @@ TEST (configitem_simple) } lumiera_configitem_delete (item, NULL); - - lumiera_config_destroy (); } @@ -77,7 +80,6 @@ TEST (lookup) CHECK (!lumiera_config_lookup_item_find (&lookup, "foo.bar")); lumiera_config_lookup_destroy (&lookup); - lumiera_config_destroy (); } @@ -104,8 +106,6 @@ TEST (change_value) printf ("%s\n", value); else printf ("failure retrieving '%s': %s\n", argv[2], lumiera_error ()); - - lumiera_config_destroy (); } @@ -126,8 +126,6 @@ TEST (basic_set_get) printf ("%s\n", value); else printf ("failure retrieving '%s': %s\n", argv[2], lumiera_error ()); - - lumiera_config_destroy (); } @@ -146,8 +144,6 @@ TEST (number_get) printf ("%lld\n", number); else printf ("%s, %lld\n", lumiera_error (), number); - - lumiera_config_destroy (); } @@ -161,8 +157,6 @@ TEST (number_get_nodefault) printf ("%lld\n", number); else printf ("%s\n", lumiera_error ()); - - lumiera_config_destroy (); } @@ -180,8 +174,6 @@ TEST (number_set) printf ("'%lld'\n", number); else printf ("%s\n", lumiera_error ()); - - lumiera_config_destroy (); } @@ -198,8 +190,6 @@ TEST (string_get) printf ("'%s'\n", string); else printf ("%s, '%s'\n", lumiera_error (), string); - - lumiera_config_destroy (); } @@ -216,8 +206,6 @@ TEST (string_set) printf ("'%s'\n", string); else printf ("%s\n", lumiera_error ()); - - lumiera_config_destroy (); } @@ -252,8 +240,6 @@ TEST (word_set) printf ("'%s'\n", word); else printf ("%s\n", lumiera_error ()); - - lumiera_config_destroy (); } @@ -264,8 +250,6 @@ TEST (configitem_simple_ctor_dtor) LumieraConfigitem item; item = lumiera_configitem_new (argv[2]); - - lumiera_config_destroy (); } @@ -293,8 +277,6 @@ TEST (configitem_simple_content_check) { printf("item->delim = '%s'\n", item->delim); } - - lumiera_config_destroy (); } @@ -310,8 +292,6 @@ TEST (wordlist_get_nth) const char* word = lumiera_config_wordlist_get_nth (argv[2], atoi (argv[4]), " \t,;"); printf ("'%s'\n", word?word:"NULL"); - - lumiera_config_destroy (); } @@ -327,8 +307,6 @@ TEST (wordlist_find) int n = lumiera_config_wordlist_find (argv[2], argv[4], " \t,;"); printf ("'%d'\n", n); - - lumiera_config_destroy (); } @@ -349,8 +327,6 @@ TEST (wordlist_replace) printf ("'%s'\n", wordlist); else printf ("%s\n", lumiera_error ()); - - lumiera_config_destroy (); } @@ -375,8 +351,6 @@ TEST (wordlist_add) printf ("'%s'\n", wordlist); else printf ("%s\n", lumiera_error ()); - - lumiera_config_destroy (); } From 36cfaaa14df5aca139a4aa17b375809bde4a45e1 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 20 May 2011 18:51:15 +0200 Subject: [PATCH 088/496] REVERT tmpbuf implementation to the state of July.2010 (bc989dab) The new implementation provides differentiated buffers, but is still buggy and causes double free alarms on shutdown See Ticket #683 --- src/lib/tmpbuf.c | 48 +++++++++++++++++------------------------------- src/lib/tmpbuf.h | 11 ++++------- 2 files changed, 21 insertions(+), 38 deletions(-) diff --git a/src/lib/tmpbuf.c b/src/lib/tmpbuf.c index d2ef81a7c..46e42174a 100644 --- a/src/lib/tmpbuf.c +++ b/src/lib/tmpbuf.c @@ -29,18 +29,17 @@ #include <nobug.h> +/*!! WARNING + *!! + *!! this is the restored old version from bc989dab7a97fc69c (July 2010) + *!! (the improved version is still buggy as of 5/2011) + */ + struct lumiera_tmpbuf_struct { - /* tiny buffers are not aligned, anything which is smaller than sizeof(void*) */ - char tiny_buffers[LUMIERA_TMPBUF_NUM*(sizeof(void*)-1)]; - unsigned tiny_idx; - /* first 16 static LUMIERA_TMPBUF_SMALL byte arrays */ - char small_buffers[LUMIERA_TMPBUF_NUM][LUMIERA_TMPBUF_SMALL]; - unsigned small_idx; - /* next 16 dynamic arrays for biggier buffers */ - void* big_buffers[LUMIERA_TMPBUF_NUM]; - size_t big_sizes[LUMIERA_TMPBUF_NUM]; - unsigned big_idx; + void* buffers[64]; + size_t sizes[64]; + unsigned idx; }; static pthread_once_t lumiera_tmpbuf_tls_once = PTHREAD_ONCE_INIT; @@ -74,7 +73,7 @@ lumiera_tmpbuf_freeall (void) { pthread_setspecific (lumiera_tmpbuf_tls_key, NULL); for (int idx = 0; idx < LUMIERA_TMPBUF_NUM; ++idx) - lumiera_free (buf->big_buffers[idx]); + lumiera_free (buf->buffers[idx]); lumiera_free (buf); } } @@ -90,28 +89,15 @@ lumiera_tmpbuf_provide (size_t size) pthread_setspecific (lumiera_tmpbuf_tls_key, buf = lumiera_calloc (1, sizeof (struct lumiera_tmpbuf_struct))); - if (size < sizeof(void*)) - { - buf->tiny_idx = (buf->tiny_idx + size) & ~(LUMIERA_TMPBUF_NUM*sizeof(void*)-1); - return &buf->tiny_buffers[buf->tiny_idx-size]; - } - else if (size <= LUMIERA_TMPBUF_SMALL) - { - buf->small_idx = (buf->small_idx + 1) % LUMIERA_TMPBUF_NUM; - return buf->small_buffers[buf->small_idx]; - } - else - { - buf->big_idx = (buf->big_idx + 1) & ~(LUMIERA_TMPBUF_NUM-1); + buf->idx = (buf->idx + 1) & 0x3f; - if (buf->big_sizes[buf->big_idx] < size || buf->big_sizes[buf->big_idx] > 8*size) - { - lumiera_free (buf->big_buffers[buf->big_idx]); - buf->big_sizes[buf->big_idx] = (size + LUMIERA_TMPBUF_SMALL-1) & ~(LUMIERA_TMPBUF_SMALL-1); - buf->big_buffers[buf->big_idx] = lumiera_malloc (buf->big_sizes[buf->big_idx]); - } - return buf->big_buffers[buf->big_idx]; + if (buf->sizes[buf->idx] < size || buf->sizes[buf->idx] > 8*size) + { + lumiera_free (buf->buffers[buf->idx]); + buf->sizes[buf->idx] = (size+4*sizeof(long)) & ~(4*sizeof(long)-1); + buf->buffers[buf->idx] = lumiera_malloc (buf->sizes[buf->idx]); } + return buf->buffers[buf->idx]; } diff --git a/src/lib/tmpbuf.h b/src/lib/tmpbuf.h index 95fc62564..76b3ceaec 100644 --- a/src/lib/tmpbuf.h +++ b/src/lib/tmpbuf.h @@ -29,10 +29,13 @@ * Round robin temporary buffers. * This provides some buffers per thread which are round-robin recycled with each use. * The idea is to have fast buffers for temporal data without need for explicit heap management. + * + * @warning this is the restored old version from bc989dab7a97fc69c (July 2010) + * (the improved version is still buggy as of 5/2011) */ -/* following 2 values must be exponent of 2 */ +/* following value must be exponent of 2 */ /** * Number of buffers in the ring * This also defines how many concurent buffers can be in use in one thread (including nested calls) @@ -40,12 +43,6 @@ */ #define LUMIERA_TMPBUF_NUM 16 -/** - * Size of a small block - * tmpbuf's knows small and big blocks, anything bigger than LUMIERA_TMPBUF_SMALL will be - * allocated dynamically as 'big' buffer while for anything smaller static buffers are allocated. - */ -#define LUMIERA_TMPBUF_SMALL 256 /** From ad59049ed0305cd2f8037a2a8c3f74dcf5eff107 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 20 May 2011 19:42:29 +0200 Subject: [PATCH 089/496] disable broken Thread-joining test. See Ticket #803 --- tests/30backend-threadpool.tests | 2 +- tests/backend/test-threadpool.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/30backend-threadpool.tests b/tests/30backend-threadpool.tests index f4afa4e0d..30c52e30f 100644 --- a/tests/30backend-threadpool.tests +++ b/tests/30backend-threadpool.tests @@ -39,7 +39,7 @@ END TEST "joinable thread" joinable-thread <<END END -TEST "sync joinable" sync-joinable <<END +PLANNED "sync joinable" sync-joinable <<END END PLANNED "error cleared on join" diff --git a/tests/backend/test-threadpool.c b/tests/backend/test-threadpool.c index ed3204a9b..4c0b1846b 100644 --- a/tests/backend/test-threadpool.c +++ b/tests/backend/test-threadpool.c @@ -349,7 +349,7 @@ TEST (sync-joinable) lumiera_thread_sync_other (master); value = 7732; - lumiera_thread_join (master); + lumiera_thread_join (master); //////////////////////////////TICKET #803 deadlock here CHECK (value == 42*2-13, "result is not 42*2-12=71, but %d", value); lumiera_threadpool_destroy (); From b6f81d1e1e160803596d36daff750d37599f0988 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 20 May 2011 20:31:16 +0200 Subject: [PATCH 090/496] Fix problems discovered by compiling with gcc 4.4 --- tests/components/backend/media-access-mock.cpp | 11 ++++------- tests/lib/sync-locking-test.cpp | 2 +- tests/lib/time/time-value-test.cpp | 18 +++++++++--------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/tests/components/backend/media-access-mock.cpp b/tests/components/backend/media-access-mock.cpp index 01839dfae..096ecf197 100644 --- a/tests/components/backend/media-access-mock.cpp +++ b/tests/components/backend/media-access-mock.cpp @@ -37,11 +37,10 @@ #include "proc/mobject/session/testclip.hpp" #include "lib/util.hpp" -#include "lib/util-foreach.hpp" +#include "lib/singleton.hpp" #include "lib/time/mutation.hpp" #include "lib/symbol.hpp" -#include <iostream> #include <vector> #include <map> @@ -49,9 +48,7 @@ using lumiera::error::Invalid; using lib::time::Mutation; using lib::time::Duration; using lib::Literal; -using util::for_each; using util::isnil; -using std::cout; using std::string; using std::vector; using std::map; @@ -122,7 +119,7 @@ namespace test { }; // instantiate TestCasses table - TestCases testCases; + lib::Singleton<TestCases> testCases; } // (end) implementation namespace @@ -134,12 +131,12 @@ namespace test { if (isnil (name)) throw Invalid ("empty filename passed to MediaAccessFacade."); - if (!testCases.known(name)) + if (!testCases().known(name)) throw Invalid ("unable to use media file \""+name+"\"." "Hint: you're using a test-mock file access, " "which responds only to some magical names."); - return testCases[name].globalDesc; + return testCases()[name].globalDesc; } diff --git a/tests/lib/sync-locking-test.cpp b/tests/lib/sync-locking-test.cpp index 9fa27cbb9..6bc9d7614 100644 --- a/tests/lib/sync-locking-test.cpp +++ b/tests/lib/sync-locking-test.cpp @@ -52,7 +52,7 @@ namespace test{ class Victim : public Sync<RecursiveLock_NoWait> { - volatile long cnt_[NUM_COUNTERS]; + volatile uint cnt_[NUM_COUNTERS]; volatile uint step_; ///< @note stored as instance variable void diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index 08dfef586..e2316f240 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -74,7 +74,7 @@ namespace test{ createOffsets (ref); buildDuration (ref); buildTimeSpan (ref); - compareTimeSpan (ref); + compareTimeSpan (Time(ref)); relateTimeIntervals (ref); } @@ -309,12 +309,12 @@ namespace test{ void - compareTimeSpan (TimeValue org) + compareTimeSpan (Time const& org) { - TimeSpan span1 (Time(org), Time(org)+Time(org)); // using the distance between start and end point - TimeSpan span2 (Time(org), Offset(org, TimeValue(0))); // note: the offset is taken absolute, as Duration - TimeSpan span3 (Time(org), FSecs(5,2)); // Duration given explicitly, in seconds - TimeSpan span4 (Time(org), FSecs(5,-2)); // again: the Duration is taken absolute + TimeSpan span1 (org, org+org); // using the distance between start and end point + TimeSpan span2 (org, Offset(org, Time::ZERO)); // note: the offset is taken absolute, as Duration + TimeSpan span3 (org, FSecs(5,2)); // Duration given explicitly, in seconds + TimeSpan span4 (org, FSecs(5,-2)); // again: the Duration is taken absolute CHECK (span1 == span2); CHECK (span2 == span1); @@ -336,9 +336,9 @@ namespace test{ CHECK (span3.end() == span4.end()); // Verify the extended order on time intervals - TimeSpan span1x (Time(org)+Time(org), Duration(Time(org))); // starting later than span1 - TimeSpan span3y (Time(org), FSecs(2)); // shorter than span3 - TimeSpan span3z (Time(org)+Time(org), FSecs(2)); // starting later and shorter than span3 + TimeSpan span1x (org+org, Duration(org)); // starting later than span1 + TimeSpan span3y (org, FSecs(2)); // shorter than span3 + TimeSpan span3z (org+org, FSecs(2)); // starting later and shorter than span3 CHECK (span1 != span1x); CHECK (span3 != span3y); From 49d5a9592a8cab6c6c1b2a3b56ceadb804c66611 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 21 May 2011 06:58:15 +0200 Subject: [PATCH 091/496] Fix drop-frame: remove float framerate --- src/lib/time.cpp | 135 +++++++++++++++++++++++------------ src/lib/time.h | 37 ++++------ tests/library/test-time.c | 143 ++++++++++++++++++++------------------ 3 files changed, 177 insertions(+), 138 deletions(-) diff --git a/src/lib/time.cpp b/src/lib/time.cpp index 9a760ac4d..ff9815f95 100644 --- a/src/lib/time.cpp +++ b/src/lib/time.cpp @@ -28,7 +28,7 @@ extern "C" { #include "lib/tmpbuf.h" } -#include <limits.h> +#include <limits> #include <math.h> using util::floordiv; @@ -102,25 +102,55 @@ lumiera_frame_duration (FrameRate const& fps) } -namespace { // implementation helper +namespace { // implementation: basic frame quantisation.... - inline long long + inline int64_t calculate_quantisation (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) { time -= origin; return floordiv (time,grid); } + + inline int64_t + calculate_quantisation (gavl_time_t time, gavl_time_t origin, uint framerate, uint framerate_divisor=1) + { + REQUIRE (framerate); + REQUIRE (framerate_divisor); + + const int64_t limit_num = std::numeric_limits<gavl_time_t>::max() / framerate; + const int64_t limit_den = std::numeric_limits<gavl_time_t>::max() / framerate_divisor; + const int64_t microScale(GAVL_TIME_SCALE); + + // protect against numeric overflow + if (abs(time) < limit_num && microScale < limit_den) + { + // safe to calculate "time * framerate" + time -= origin; + return floordiv (time*framerate, microScale*framerate_divisor); + } + else + { + // direct calculation will overflow. + // use the less precise method instead... + gavl_time_t frameDuration = microScale / framerate; // truncated to µs + return calculate_quantisation (time,origin, frameDuration); + } + } } int64_t lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) { - ENSURE (sizeof(int64_t) <= sizeof(long long)); - return calculate_quantisation (time, origin, grid); } +int64_t +lumiera_quantise_frames_fps (gavl_time_t time, gavl_time_t origin, uint framerate) +{ + return calculate_quantisation (time,origin,framerate); +} + gavl_time_t lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) { @@ -149,9 +179,9 @@ lumiera_build_time(long millis, uint secs, uint mins, uint hours) } gavl_time_t -lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours) +lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours) { - gavl_time_t time = frames * (1000.0 / fps) + gavl_time_t time = 1000LL * frames/fps + 1000 * secs + 1000 * 60 * mins + 1000 * 60 * 60 * hours; @@ -159,19 +189,6 @@ lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours return time; } -gavl_time_t -lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours) -{ - uint64_t total_mins = 60 * hours + mins; - uint64_t total_frames = 108000 * hours - + 1800 * mins - + 30 * secs - + frames - - 2 * (total_mins - total_mins / 10); - - return lumiera_framecount_to_time (total_frames, FrameRate::NTSC); -} - int lumiera_time_hours (gavl_time_t time) { @@ -197,56 +214,84 @@ lumiera_time_millis (gavl_time_t time) } int -lumiera_time_frames (gavl_time_t time, float fps) +lumiera_time_frames (gavl_time_t time, uint fps) { - return (fps * (lumiera_time_millis(time))) / 1000; + REQUIRE (fps < uint(std::numeric_limits<int>::max())); + return floordiv (lumiera_time_millis(time) * int(fps), GAVL_TIME_SCALE_MS); } -int -lumiera_time_frame_count (gavl_time_t time, float fps) -{ - REQUIRE (fps > 0); - - return roundf((time / GAVL_TIME_SCALE_MS / 1000.0f) * fps); -} -/** - * This function is used in the NTSC drop-frame functions to avoid code - * repetition. - * @return the frame number for given time - */ -static int -ntsc_drop_get_frame_number (gavl_time_t time) -{ - int frame = lumiera_time_frame_count (time, NTSC_DROP_FRAME_FPS); + + +/* ===== NTSC drop-frame conversions ===== */ + + +namespace { // implementation helper - int d = frame / 17982; // 17982 = 600 * 29.97 - int m = frame % 17982; // 17982 = 600 * 29.97 + const uint FRAMES_PER_10min = 10*60 * 30000/1001; + const uint FRAMES_PER_1min = 1*60 * 30000/1001; + const uint DISCREPANCY = (1*60 * 30) - FRAMES_PER_1min; + - return frame + 18*d + 2*((m - 2) / 1798); // 1798 = 60 * 29.97 + /** reverse the drop-frame calculation + * @param time absolute time value in micro ticks + * @return the absolute frame number using NTSC drop-frame encoding + * @todo I doubt this works correct for negative times!! + */ + inline int64_t + calculate_drop_frame_number (gavl_time_t time) + { + int64_t frameNr = calculate_quantisation (time, 0, 30000, 1001); + + // partition into 10 minute segments + lldiv_t tenMinFrames = lldiv (frameNr, FRAMES_PER_10min); + + // ensure the drop-frame incidents happen at full minutes; + // at start of each 10-minute segment *no* drop incident happens, + // thus we need to correct discrepancy between nominal/real framerate once: + int64_t remainingMinutes = (tenMinFrames.rem - DISCREPANCY) / FRAMES_PER_1min; + + int64_t dropIncidents = (10-1) * tenMinFrames.quot + remainingMinutes; + return frameNr + 2*dropIncidents; + } } int lumiera_time_ntsc_drop_frames (gavl_time_t time) { - return ntsc_drop_get_frame_number(time) % 30; + return calculate_drop_frame_number(time) % 30; } int lumiera_time_ntsc_drop_seconds (gavl_time_t time) { - return ntsc_drop_get_frame_number(time) / 30 % 60; + return calculate_drop_frame_number(time) / 30 % 60; } int lumiera_time_ntsc_drop_minutes (gavl_time_t time) { - return ntsc_drop_get_frame_number(time) / 30 / 60 % 60; + return calculate_drop_frame_number(time) / 30 / 60 % 60; } int lumiera_time_ntsc_drop_hours (gavl_time_t time) { - return ntsc_drop_get_frame_number(time) / 30 / 60 / 60 % 24; + return calculate_drop_frame_number(time) / 30 / 60 / 60 % 24; } +gavl_time_t +lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours) +{ + uint64_t total_mins = 60 * hours + mins; + uint64_t total_frames = 30*60*60 * hours + + 30*60 * mins + + 30 * secs + + frames + - 2 * (total_mins - total_mins / 10); + gavl_time_t result = lumiera_framecount_to_time (total_frames, FrameRate::NTSC); + + if (0 != result) // compensate for truncating down on conversion + result += 1; // without this adjustment the frame number + return result; // would turn out off by -1 on back conversion +} diff --git a/src/lib/time.h b/src/lib/time.h index c017f6713..f321e176a 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -94,8 +94,6 @@ lumiera_frame_duration (lib::time::FrameRate const& fps); extern "C" { /* ===================== C interface ======================== */ #endif -#define NTSC_DROP_FRAME_FPS 29.97 -/* TODO: replace this by lib::time::FrameRate::NTSC */ /** * Formats a time value in H:MM:SS.mmm format into a temporary buffer. @@ -116,6 +114,9 @@ lumiera_tmpbuf_print_time (gavl_time_t time); int64_t lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid); +int64_t +lumiera_quantise_frames_fps (gavl_time_t time, gavl_time_t origin, uint framerate); + /** * Similar to #lumiera_quantise_frames, but returns a grid aligned \em time value * @return time of start of the grid interval containing the given time, @@ -138,7 +139,7 @@ gavl_time_t lumiera_time_of_gridpoint (int64_t nr, gavl_time_t origin, gavl_time_t grid); /** - * Builds a time value by summing up the given components. + * Build a time value by summing up the given components. * @param millis number of milliseconds * @param secs number of seconds * @param mins number of minutes @@ -149,15 +150,14 @@ lumiera_build_time (long millis, uint secs, uint mins, uint hours); /** * Builds a time value by summing up the given components. - * @todo replace float framerates by lib::time::FrameRate - * @deprecated this function doesn't fit in the general time handling concept. - * SMPTE is a timecode format, and should be distinguished from a - * raw time value. For any timecode format, we always require an explicit - * 'time grid' (coordinate system), which also defines a zero point. - * Usually, this time grid is managed and provided by the session + * @param fps framerate (frames per second) + * @param frames number of additional frames + * @param secs number of seconds + * @param mins number of minutes + * @param hours number of hours */ gavl_time_t -lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours); +lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours); /** * Builds a time value by summing up the given components. @@ -188,22 +188,11 @@ int lumiera_time_millis (gavl_time_t time); /** - * Extract the frame part of given time, using the given fps. - * @param fps frame rate - * @todo use the rational lib::time::FrameRate instead of a float + * Extract the remaining frame part of given time. + * @param fps frame rate (frames per second) */ int -lumiera_time_frames (gavl_time_t time, float fps); - -/** - * Extract the frame count for the given time, using the given fps. - * @todo use the rational lib::time::FrameRate instead of a float - * @deprecated should be moved down into the implementation; - * this function doesn't fit into the general time handling, - * because it doesn't link to a time grid (coordinate system) - */ -int -lumiera_time_frame_count (gavl_time_t time, float fps); +lumiera_time_frames (gavl_time_t time, uint fps); /** * Extract the frame part of given time, using NTSC drop-frame timecode. diff --git a/tests/library/test-time.c b/tests/library/test-time.c index 3c1957645..b7922e778 100644 --- a/tests/library/test-time.c +++ b/tests/library/test-time.c @@ -26,6 +26,13 @@ typedef unsigned int uint; #include <nobug.h> +static int +calculate_framecount (gavl_time_t t, uint fps) +{ + return lumiera_quantise_frames_fps (t,0,fps); +} + + TESTS_BEGIN const int FRAMES = 15; @@ -33,7 +40,7 @@ const int MILLIS = 700; const int SECONDS = 20; const int MINUTES = 55; const int HOURS = 3; -const float FPS = 24.0; +const int FPS = 24; /* * 1. Basic functionality @@ -44,29 +51,29 @@ TEST (basic) // Zero gavl_time_t t = lumiera_build_time (0,0,0,0); - CHECK ((gavl_time_t) t == 0); - CHECK (lumiera_time_millis (t) == 0); - CHECK (lumiera_time_seconds (t) == 0); - CHECK (lumiera_time_minutes (t) == 0); - CHECK (lumiera_time_hours (t) == 0); - CHECK (lumiera_time_frames (t, FPS) == 0); - CHECK (lumiera_time_frames (t, FPS+5) == 0); - CHECK (lumiera_time_frame_count (t, FPS) == 0); - CHECK (lumiera_time_frame_count (t, FPS+5) == 0); + CHECK ((gavl_time_t) t == 0); + CHECK (lumiera_time_millis (t) == 0); + CHECK (lumiera_time_seconds (t) == 0); + CHECK (lumiera_time_minutes (t) == 0); + CHECK (lumiera_time_hours (t) == 0); + CHECK (lumiera_time_frames (t, FPS) == 0); + CHECK (lumiera_time_frames (t, FPS+5) == 0); + CHECK (calculate_framecount (t,FPS) == 0); + CHECK (calculate_framecount (t, FPS+5) == 0); ECHO ("%s", lumiera_tmpbuf_print_time (t)); // Non-zero t = lumiera_build_time (MILLIS, SECONDS, MINUTES, HOURS); - CHECK (lumiera_time_millis (t) == MILLIS); - CHECK (lumiera_time_seconds (t) == SECONDS); - CHECK (lumiera_time_minutes (t) == MINUTES); - CHECK (lumiera_time_hours (t) == HOURS); - CHECK (lumiera_time_frames (t, FPS) == (int)((FPS * MILLIS) / 1000)); - CHECK (lumiera_time_frames (t, FPS+5) == (int)(((FPS+5) * MILLIS) / 1000)); - CHECK (lumiera_time_frame_count (t, FPS) == 338897); - CHECK (lumiera_time_frame_count (t, FPS+5) == 409500); + CHECK (lumiera_time_millis (t) == MILLIS); + CHECK (lumiera_time_seconds (t) == SECONDS); + CHECK (lumiera_time_minutes (t) == MINUTES); + CHECK (lumiera_time_hours (t) == HOURS); + CHECK (lumiera_time_frames (t, FPS) == FPS * MILLIS / 1000); + CHECK (lumiera_time_frames (t, FPS+5) == (FPS+5) * MILLIS / 1000); + CHECK (calculate_framecount (t, FPS) == 338896); + CHECK (calculate_framecount (t, FPS+5) == 409500); ECHO ("%s", lumiera_tmpbuf_print_time (t)); } @@ -79,14 +86,14 @@ TEST (fps) { gavl_time_t t = lumiera_build_time_fps (FPS, FRAMES, SECONDS, MINUTES, HOURS); - CHECK (lumiera_time_millis (t) == (int)(FRAMES * (1000.0 / FPS))); - CHECK (lumiera_time_seconds (t) == SECONDS); - CHECK (lumiera_time_minutes (t) == MINUTES); - CHECK (lumiera_time_hours (t) == HOURS); - CHECK (lumiera_time_frames (t, FPS) == FRAMES); - CHECK (lumiera_time_frames (t, FPS+5) == (int)(((FPS+5) * 625) / 1000)); - CHECK (lumiera_time_frame_count (t, FPS) == 338895); - CHECK (lumiera_time_frame_count (t, FPS+5) == 409498); + CHECK (lumiera_time_millis (t) == FRAMES * 1000 / FPS); + CHECK (lumiera_time_seconds (t) == SECONDS); + CHECK (lumiera_time_minutes (t) == MINUTES); + CHECK (lumiera_time_hours (t) == HOURS); + CHECK (lumiera_time_frames (t, FPS) == FRAMES); + CHECK (lumiera_time_frames (t, FPS+5) == FRAMES * (FPS+5)/FPS); + CHECK (calculate_framecount (t, FPS) == 338895); + CHECK (calculate_framecount (t, FPS+5) == 409498); } /* @@ -98,40 +105,37 @@ TEST (ntsc_drop_frame) // Make sure frame 0 begins at 0 gavl_time_t t = lumiera_build_time_ntsc_drop (0, 0, 0, 0); - CHECK ((gavl_time_t) t == 0); - CHECK (lumiera_time_millis (t) == 0); - CHECK (lumiera_time_seconds (t) == 0); - CHECK (lumiera_time_minutes (t) == 0); - CHECK (lumiera_time_hours (t) == 0); - CHECK (lumiera_time_frames (t, FPS) == 0); - CHECK (lumiera_time_frames (t, FPS+5) == 0); - CHECK (lumiera_time_frame_count (t, FPS) == 0); - CHECK (lumiera_time_frame_count (t, FPS+5) == 0); - - // Use some arbitrary value to test with - const int FRAMES = 15; - const int SECONDS = 20; - const int MINUTES = 55; - const int HOURS = 3; - const float FPS = 24.0; - + CHECK ((gavl_time_t) t == 0); + CHECK (lumiera_time_millis (t) == 0); + CHECK (lumiera_time_seconds (t) == 0); + CHECK (lumiera_time_minutes (t) == 0); + CHECK (lumiera_time_hours (t) == 0); + CHECK (lumiera_time_frames (t, FPS) == 0); + CHECK (lumiera_time_frames (t, FPS+5) == 0); + CHECK (calculate_framecount (t, FPS) == 0); + CHECK (calculate_framecount (t, FPS+5) == 0); + + + t = lumiera_build_time_ntsc_drop (FRAMES, SECONDS, MINUTES, HOURS); - - CHECK (lumiera_time_millis (t) == 486); - CHECK (lumiera_time_seconds (t) == SECONDS); - CHECK (lumiera_time_minutes (t) == MINUTES); - CHECK (lumiera_time_hours (t) == HOURS); - - // Check standard frame calculations to verify build_time_ntsc_drop - CHECK (lumiera_time_frames (t, FPS) == 11); - CHECK (lumiera_time_frames (t, FPS+5) == 14); - CHECK (lumiera_time_frame_count (t, FPS) == 338892); - CHECK (lumiera_time_frame_count (t, FPS+5) == 409494); - - // Frames for NTSC drop - CHECK (lumiera_time_ntsc_drop_frames (t) == 15); - CHECK (lumiera_time_frame_count (t, NTSC_DROP_FRAME_FPS) == 423191); - + + // Calculate manually what result to expect.... + int frames = FRAMES + 30*SECONDS + 30*60*MINUTES + 30*60*60*HOURS; // sum up using nominal 30fps + int minutes_to_drop_frames = (MINUTES - MINUTES/10) + (HOURS * 54); // but every minute, with the exception of every 10 minutes... + frames -= 2*minutes_to_drop_frames; // ...drop 2 frames + int64_t expectedMillis = 1000LL * frames * 1001/30000; // now convert frames to time, using the real framerate + + expectedMillis %= 1000; // look at the remainder.. + CHECK (lumiera_time_millis (t) == expectedMillis); + + CHECK (lumiera_time_seconds (t) == SECONDS); // while all other components should come out equal as set + CHECK (lumiera_time_minutes (t) == MINUTES); + CHECK (lumiera_time_hours (t) == HOURS); + + // Reverse calculate frames for NTSC drop +//CHECK (lumiera_quantise_frames (t, 0, dropFrameDuration) == frames); // the total nominal frames + CHECK (lumiera_time_ntsc_drop_frames (t) == FRAMES); // maximum one frame off due to rounding + // Cover the whole value range; // Manually construct a drop-frame timecode // Make sure our library function returns the same times. @@ -140,8 +144,8 @@ TEST (ntsc_drop_frame) int frame; int hrs; for (hrs = 0; hrs <= 24; hrs += 6) - for (min = 0; min <= 59; min += 8) - for (sec = 0; sec <= 59; sec += 8) + for (min = 0; min <= 59; min += 1) + for (sec = 0; sec <= 59; sec += 10) for (frame = 0; frame <= 29; frame++) { // Skip dropped frames @@ -149,14 +153,15 @@ TEST (ntsc_drop_frame) continue; t = lumiera_build_time_ntsc_drop(frame, sec, min, hrs); - - /* ECHO ("%02d:%02d:%02d.%02d", */ - /* lumiera_time_ntsc_drop_hours (t), */ - /* lumiera_time_ntsc_drop_minutes (t), */ - /* lumiera_time_ntsc_drop_seconds (t), */ - /* lumiera_time_ntsc_drop_frames (t)); */ - - CHECK (lumiera_time_ntsc_drop_frames (t) == frame); + /* + ECHO ("%02d:%02d:%02d;%02d" + , lumiera_time_ntsc_drop_hours (t) + , lumiera_time_ntsc_drop_minutes (t) + , lumiera_time_ntsc_drop_seconds (t) + , lumiera_time_ntsc_drop_frames (t) + ); + */ + CHECK (lumiera_time_ntsc_drop_frames (t) == frame); CHECK (lumiera_time_ntsc_drop_seconds (t) == sec); CHECK (lumiera_time_ntsc_drop_minutes (t) == min); CHECK (lumiera_time_ntsc_drop_hours (t) == hrs % 24); From 51b3f0defae55e7067c0850040e6cc79ae5b9f91 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 22 May 2011 00:50:23 +0200 Subject: [PATCH 092/496] start a page to collect technical notes about Scheduler and Jobs (Backend) Signed-off-by: Ichthyostega <prg@ichthyostega.de> --- 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 <prg@ichthyostega.de> Date: Sun, 22 May 2011 05:45:59 +0200 Subject: [PATCH 093/496] create pages and sections for the design of output handling and player subsystem Signed-off-by: Ichthyostega <prg@ichthyostega.de> --- 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 e103b4d8aa265e5f1aa59376b6b482543fda4d32 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 24 Jul 2011 17:25:30 +0200 Subject: [PATCH 094/496] SCons: fix error in linking executables a long standing error, uncovered recently due to more stringent checks of the linker on newer platforms, not picking up direct dependencies of an executable transitively from the linked-to dynamic libs (which is fine). The error was to *overwrite* the LIBS construction variable in the definition of the executable to link, instead of just adding our dynamic links to the sources to be linked. --- SConstruct | 2 +- src/tool/SConscript | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SConstruct b/SConstruct index a2f6750cf..250936bc7 100644 --- a/SConstruct +++ b/SConstruct @@ -350,7 +350,7 @@ def defineBuildTargets(env, artifacts): 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'], LIBS=core, install=True) + artifacts['lumiera'] = ( env.Program('lumiera', ['src/lumiera/main.cpp'] + core, install=True) + artifacts['config'] ) diff --git a/src/tool/SConscript b/src/tool/SConscript index 1cd45d0d6..583b7032f 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -12,12 +12,12 @@ envSvg.mergeConf(['librsvg-2.0']) envSvg.Append(LIBS=support_lib) -luidgen = env.Program('luidgen', 'luidgen.c', LIBS=support_lib, install=True) ## for generating Lumiera-UIDs +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.... artifacts['tools'] = [ env.Program('hello-world','hello.c', install=True) #### hello world (checks C build) - + env.Program('try', 'try.cpp', LIBS=support_lib) #### to try out some feature... + + env.Program('try', ['try.cpp'] + support_lib) #### to try out some feature... + luidgen + rsvg ] From 64cf3c9d34715f1fb915714f057f2fb25f78cf90 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 25 Aug 2011 03:03:43 +0200 Subject: [PATCH 095/496] 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 <skangas@skangas.se> Date: Thu, 15 Sep 2011 02:32:31 +0200 Subject: [PATCH 096/496] 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 <skangas@skangas.se> Date: Thu, 15 Sep 2011 04:35:08 +0200 Subject: [PATCH 097/496] 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 <prg@ichthyostega.de> Date: Sun, 25 Sep 2011 19:16:13 +0200 Subject: [PATCH 098/496] 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 <<END out: sizeof\( .+control.+TestClass.+ \) = 1 out: sizeof\( .+control.+TestClass.+lib.time.TimeVar.+ \) = (12|16) -out: 0:..:..\.000 +out: 0:..:..\..00 return: 0 END From 4a53ef4cd0efaeacc8a7754f69ea57b00121f439 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 25 Sep 2011 19:10:57 +0200 Subject: [PATCH 099/496] 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 <ct@pipapo.org> 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 <stdint.h> -/** - * @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 <nobug.h> 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 <ct@pipapo.org> 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<KIND> asset_id (getID (idi)); DB::Lock guard(®istry); - TODO ("handle duplicate Registrations"); + //////////////////////////////////////////////////////////TICKET #840 handle duplicate Registrations P<KIND> 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 <prg@ichthyostega.de> Date: Sun, 22 May 2011 05:33:08 +0200 Subject: [PATCH 100/496] 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. </pre> </div> +<div title="DesignPlayerSubsystem" modifier="Ichthyostega" modified="201105220332" created="201105220216" tags="Player design draft" changecount="5"> +<pre>//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.</pre> +</div> <div title="DisplayFacade" modifier="Ichthyostega" modified="200904302316" created="200902080703" tags="spec" changecount="2"> <pre>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. </pre> </div> -<div title="OutputManagement" modifier="Ichthyostega" modified="201011072240" created="201007090155" tags="Model Rendering Player spec draft" changecount="18"> +<div title="OutputManagement" modifier="Ichthyostega" modified="201105220022" created="201007090155" tags="Model Rendering Player spec draft" changecount="19"> <pre>//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</pre> </div> -<div title="Player" modifier="Ichthyostega" modified="201012181726" created="201012181700" tags="def overview" changecount="8"> +<div title="Player" modifier="Ichthyostega" modified="201105220214" created="201012181700" tags="def overview" changecount="9"> <pre>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. </pre> </div> <div title="PlayerDummy" modifier="Ichthyostega" modified="200906071810" created="200901300209" tags="GuiIntegration operational img" changecount="17"> From 899ffa60ca7afdd45ded9ee220c8b1c00f0f9c81 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 23 May 2011 04:43:56 +0200 Subject: [PATCH 101/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/engine/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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef ENGINE_RENDERENGINE_H +#define ENGINE_RENDERENGINE_H + +#include <list> + +#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<RenderGraph> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/asset/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<Timeline>::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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file 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<Timeline>("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 <vector> +//#include <string> + +//using std::vector; +//using std::string; + +namespace mobject { +namespace session { + + class Binding; + typedef MORef<Binding> RBinding; +}} + + +namespace asset { + + +// using lumiera::P; + class Timeline; + typedef lumiera::P<Timeline> PTimeline; + + + /** + * TODO type comment + */ + class Timeline + : public Struct + , public lib::AutoRegistered<Timeline> + { + 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/engine/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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef ENGINE_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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/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 <string> +#include <memory> +#include <tr1/functional> +#include <boost/scoped_ptr.hpp> + + + +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<DummyPlayerService> 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<DummyPlayerSubsysDescriptor> 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 <Ichthyostega@web.de>"; + } + ) + , 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<DummyPlayerService>::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<LumieraPlayProcess> (_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<ProcP> (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<ProcP> (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<ProcessImpl> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file 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 <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> +#include <string> + + +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<DummyImageGenerator> imageGen_; + boost::scoped_ptr<TickService> 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<DummyPlayerService> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef 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<Media> PMedia; + typedef P<asset::Clip> 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<Clip> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/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 <string> +#include <memory> +#include <tr1/functional> +#include <boost/scoped_ptr.hpp> + + + +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<DummyPlayerService> 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<DummyPlayerSubsysDescriptor> 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 <Ichthyostega@web.de>"; + } + ) + , 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<DummyPlayerService>::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<LumieraPlayProcess> (_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<ProcP> (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<ProcP> (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<ProcessImpl> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "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 <Ichthyostega@web.de>"; + } + ) + , 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<DisplayService>::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<DisplayerSlot*> (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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file 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 <glibmm.h> +#include <sigc++/sigc++.h> +#include <boost/noncopyable.hpp> +#include <string> +#include <vector> + + +namespace gui { + + using std::string; + using std::vector; + using lumiera::Display; + using Glib::Dispatcher; + + + typedef sigc::slot<void, void*> FrameDestination; + typedef sigc::signal<void, void*> 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<DisplayerSlot> 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<DisplayService> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file 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 <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> +#include <string> + + +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<DummyImageGenerator> imageGen_; + boost::scoped_ptr<TickService> 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<DummyPlayerService> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/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 <string> +#include <memory> +#include <tr1/functional> +#include <boost/scoped_ptr.hpp> + + + +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<DummyPlayerService> 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<DummyPlayerSubsysDescriptor> 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 <Ichthyostega@web.de>"; + } + ) + , 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<DummyPlayerService>::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<LumieraPlayProcess> (_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<ProcP> (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<ProcP> (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<ProcessImpl> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file 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 <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> +#include <string> + + +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<DummyImageGenerator> imageGen_; + boost::scoped_ptr<TickService> 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<DummyPlayerService> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/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 <string> +#include <memory> +#include <tr1/functional> +#include <boost/scoped_ptr.hpp> + + + +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<DummyPlayerService> 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<DummyPlayerSubsysDescriptor> 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 <Ichthyostega@web.de>"; + } + ) + , 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<DummyPlayerService>::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<LumieraPlayProcess> (_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<ProcP> (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<ProcP> (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<ProcessImpl> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file 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 <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> +#include <string> + + +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<DummyImageGenerator> imageGen_; + boost::scoped_ptr<TickService> 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<DummyPlayerService> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "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 <Ichthyostega@web.de>"; + } + ) + , 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<DisplayService>::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<DisplayerSlot*> (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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file 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 <glibmm.h> +#include <sigc++/sigc++.h> +#include <boost/noncopyable.hpp> +#include <string> +#include <vector> + + +namespace gui { + + using std::string; + using std::vector; + using lumiera::Display; + using Glib::Dispatcher; + + + typedef sigc::slot<void, void*> FrameDestination; + typedef sigc::signal<void, void*> 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<DisplayerSlot> 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<DisplayService> 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 <prg@ichthyostega.de> Date: Mon, 23 May 2011 05:46:40 +0200 Subject: [PATCH 102/496] 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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,17 +21,15 @@ */ -#ifndef ENGINE_RENDERENGINE_H -#define ENGINE_RENDERENGINE_H - -#include <list> - -#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<RenderGraph> 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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,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<Timeline>("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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,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 <string> -#include <memory> -#include <tr1/functional> -#include <boost/scoped_ptr.hpp> +//#include <string> +//#include <memory> +//#include <tr1/functional> +//#include <boost/scoped_ptr.hpp> 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<DummyPlayerService> 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<DummyPlayerSubsysDescriptor> 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 <Ichthyostega@web.de>"; - } - ) - , 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<DummyPlayerService>::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<LumieraPlayProcess> (_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<ProcP> (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<ProcP> (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<ProcessImpl> 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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -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 <boost/noncopyable.hpp> -#include <boost/scoped_ptr.hpp> -#include <string> +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +// +//#include <boost/noncopyable.hpp> +//#include <boost/scoped_ptr.hpp> +//#include <string> namespace proc { - namespace 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<DummyImageGenerator> imageGen_; - boost::scoped_ptr<TickService> 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<DummyPlayerService> 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<DummyPlayerService> 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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,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 <string> -#include <memory> -#include <tr1/functional> -#include <boost/scoped_ptr.hpp> +//#include <string> +//#include <memory> +//#include <tr1/functional> +//#include <boost/scoped_ptr.hpp> 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<DummyPlayerService> 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<DummyPlayerSubsysDescriptor> 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 <Ichthyostega@web.de>"; - } - ) - , 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<DummyPlayerService>::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<LumieraPlayProcess> (_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<ProcP> (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<ProcP> (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<ProcessImpl> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file 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 <boost/noncopyable.hpp> +//#include <boost/scoped_ptr.hpp> +//#include <string> + + +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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,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 <Ichthyostega@web.de>"; - } - ) - , 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<DisplayService>::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<DisplayerSlot*> (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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -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 <glibmm.h> -#include <sigc++/sigc++.h> #include <boost/noncopyable.hpp> -#include <string> -#include <vector> +//#include <string> +//#include <vector> -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<void, void*> FrameDestination; - typedef sigc::signal<void, void*> 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<DisplayerSlot> DisplayerTab; +//typedef lib::ScopedPtrVect<DisplayerSlot> 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<DisplayService> 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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -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 <boost/noncopyable.hpp> -#include <boost/scoped_ptr.hpp> -#include <string> +//#include <boost/scoped_ptr.hpp> +//#include <string> 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<DummyImageGenerator> imageGen_; - boost::scoped_ptr<TickService> 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<DummyPlayerService> 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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,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 <string> -#include <memory> -#include <tr1/functional> -#include <boost/scoped_ptr.hpp> +//#include <string> +//#include <memory> +//#include <tr1/functional> +//#include <boost/scoped_ptr.hpp> 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<DummyPlayerService> 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<DummyPlayerSubsysDescriptor> 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 <Ichthyostega@web.de>"; - } - ) - , 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<DummyPlayerService>::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<LumieraPlayProcess> (_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<ProcP> (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<ProcP> (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<ProcessImpl> 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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -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 <boost/noncopyable.hpp> -#include <boost/scoped_ptr.hpp> -#include <string> +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +// +//#include <boost/noncopyable.hpp> +//#include <boost/scoped_ptr.hpp> +//#include <string> namespace proc { namespace 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<DummyImageGenerator> imageGen_; - boost::scoped_ptr<TickService> 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<DummyPlayerService> 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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,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 <string> -#include <memory> -#include <tr1/functional> -#include <boost/scoped_ptr.hpp> +//#include <memory> +//#include <tr1/functional> +//#include <boost/scoped_ptr.hpp> @@ -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 <Ichthyostega@web.de>"; - } - ) - , 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<DummyPlayerService>::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<LumieraPlayProcess> (_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<ProcP> (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<ProcP> (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<ProcessImpl> 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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -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 <boost/noncopyable.hpp> -#include <boost/scoped_ptr.hpp> -#include <string> +//#include <boost/scoped_ptr.hpp> +//#include <string> 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<DummyImageGenerator> imageGen_; - boost::scoped_ptr<TickService> 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<DummyPlayerService> 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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,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 <Ichthyostega@web.de>"; - } - ) - , 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<DisplayService>::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<DisplayerSlot*> (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 <Ichthyostega@web.de> + 2011, Hermann Vosseler <Ichthyostega@web.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -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 <glibmm.h> -#include <sigc++/sigc++.h> -#include <boost/noncopyable.hpp> -#include <string> -#include <vector> +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +//#include "lib/scoped-ptrvect.hpp" +// +//#include <glibmm.h> +//#include <sigc++/sigc++.h> +//#include <boost/noncopyable.hpp> +//#include <string> +//#include <vector> -namespace gui { - - using std::string; - using std::vector; - using lumiera::Display; - using Glib::Dispatcher; +namespace proc { +namespace play { +namespace sound { - typedef sigc::slot<void, void*> FrameDestination; - typedef sigc::signal<void, void*> 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<DisplayerSlot> 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<DisplayService> 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. </pre> </div> -<div title="DesignPlayerSubsystem" modifier="Ichthyostega" modified="201105220332" created="201105220216" tags="Player design draft" changecount="5"> +<div title="DesignPlayerSubsystem" modifier="Ichthyostega" modified="201105230122" created="201105220216" tags="Player design draft" changecount="12"> <pre>//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.</pre> +!!!!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. + +</pre> </div> <div title="DisplayFacade" modifier="Ichthyostega" modified="200904302316" created="200902080703" tags="spec" changecount="2"> <pre>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. </pre> </div> +<div title="EngineFaçade" modifier="Ichthyostega" modified="201105222330" created="201105222328" tags="Rendering overview spec" changecount="2"> +<pre>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 +</pre> +</div> <div title="EntryID" modifier="Ichthyostega" modified="201012300036" created="201012300026" tags="def Types" changecount="5"> <pre>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.// </pre> </div> -<div title="FixtureDatastructure" modifier="Ichthyostega" modified="201012162342" created="201012162304" tags="spec Builder" changecount="6"> +<div title="FixtureDatastructure" modifier="Ichthyostega" modified="201105222242" created="201012162304" tags="spec Builder" changecount="9"> <pre>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 </pre> </div> -<div title="FixtureStorage" modifier="Ichthyostega" modified="201012162324" created="201012140231" tags="Builder impl operational draft" changecount="30"> +<div title="FixtureStorage" modifier="Ichthyostega" modified="201105222258" created="201012140231" tags="Builder impl operational draft" changecount="33"> <pre>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. </pre> </div> @@ -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) </pre> </div> +<div title="FrameDispatcher" modifier="Ichthyostega" modified="201105222346" created="201105222330" tags="def Rendering" changecount="2"> +<pre>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</pre> +</div> <div title="FullScreenPlugin" modifier="CehTeh" modified="200706110313" created="200607241016" tags="systemConfig lewcidExtension" server.type="file" server.host="file:///home/ct/.homepage/home.html" server.page.revision="200706110313"> <pre>/*** |Name|FullScreenPlugin| @@ -2967,12 +2989,12 @@ These are used as token for dealing with other objects and have no identity of t </pre> </div> -<div title="ModelPort" modifier="Ichthyostega" modified="201012112233" created="201011100234" tags="def spec" changecount="8"> +<div title="ModelPort" modifier="Ichthyostega" modified="201105222218" created="201011100234" tags="def spec Model" changecount="11"> <pre>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 </pre> </div> -<div title="OutputMapping" modifier="Ichthyostega" modified="201011270311" created="201011080238" tags="Model spec draft" changecount="23"> +<div title="OutputMapping" modifier="Ichthyostega" modified="201105221814" created="201011080238" tags="Model spec draft" changecount="25"> <pre>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>> </pre> </div> -<div title="PlayProcess" modifier="Ichthyostega" modified="201012181732" created="201012181714" tags="def spec Player" changecount="2"> +<div title="PlayProcess" modifier="Ichthyostega" modified="201105222321" created="201012181714" tags="def spec Player" changecount="5"> <pre>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</pre> </div> +<div title="PlayService" modifier="Ichthyostega" modified="201105221918" created="201105221900" tags="Player spec draft" changecount="9"> +<pre>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 +</pre> +</div> <div title="Player" modifier="Ichthyostega" modified="201105220214" created="201012181700" tags="def overview" changecount="9"> <pre>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??</pre> </div> -<div title="RenderEngine" modifier="Ichthyostega" modified="200802031835" created="200802031820" tags="def" changecount="2"> +<div title="RenderEngine" modifier="Ichthyostega" modified="201105222323" created="200802031820" tags="def" changecount="3"> <pre>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 </pre> </div> <div title="RenderEntities" modifier="Ichthyostega" modified="200906071810" created="200706190715" tags="Rendering classes img" changecount="10"> @@ -4910,7 +4956,7 @@ We need to detect attaching and detaching of * root &harr; [[Track]] </pre> </div> -<div title="Segmentation" modifier="Ichthyostega" modified="201012140222" created="201012121901" tags="def spec Builder" changecount="27"> +<div title="Segmentation" modifier="Ichthyostega" modified="201105222236" created="201012121901" tags="def spec Builder" changecount="28"> <pre>//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 { /*}}}*/ </pre> </div> +<div title="SwitchBoard" modifier="Ichthyostega" modified="201105221902" created="201105221831" tags="def SessionLogic Player" changecount="2"> +<pre>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</pre> +</div> <div title="TabTimeline" modifier="Ichthyostega" modified="200806030148" created="200706191949" changecount="4"> <pre><<timeline better:true maxDays:55 maxEntries:45>></pre> </div> @@ -6874,7 +6928,7 @@ At the level of individual timecode formats, we're lacking a common denominator; &rarr; Quantiser [[implementation details|QuantiserImpl]] </pre> </div> -<div title="TimeUsage" modifier="Ichthyostega" modified="201105122348" created="201012230204" tags="design discuss" changecount="27"> +<div title="TimeUsage" modifier="Ichthyostega" modified="201105222341" created="201012230204" tags="design discuss" changecount="30"> <pre>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??}}} </pre> </div> -<div title="Timeline" modifier="Ichthyostega" modified="201011220126" created="200706250721" tags="def" changecount="21"> +<div title="Timeline" modifier="Ichthyostega" modified="201105221835" created="200706250721" tags="def" changecount="22"> <pre>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 </pre> </div> -<div title="ViewerPlayConnection" modifier="Ichthyostega" modified="201105110009" created="201007110305" tags="Model Player spec draft" changecount="7"> +<div title="ViewConnection" modifier="Ichthyostega" modified="201105221902" created="201105221854" tags="def Model SessionLogic" changecount="4"> +<pre>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. +</pre> +</div> +<div title="ViewerAsset" modifier="Ichthyostega" created="201105230116" tags="Model spec" changecount="1"> +<pre>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.</pre> +</div> +<div title="ViewerPlayConnection" modifier="Ichthyostega" modified="201105221838" created="201007110305" tags="Model Player spec draft" changecount="8"> <pre>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. </pre> </div> <div title="VirtualClip" modifier="Ichthyostega" modified="201011220152" created="200804110321" tags="def Model" changecount="17"> From 92301a3752bac4184105dab35a01c274c8acc2a1 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 24 Jul 2011 02:51:41 +0200 Subject: [PATCH 103/496] 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 <prg@ichthyostega.de> Date: Tue, 24 May 2011 03:46:32 +0200 Subject: [PATCH 104/496] 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 IHA> - class Holder; - - template<class FA, class I> - class Holder<InstanceHandle<I,FA> > - : Accessor<FA>, - protected FA - { - protected: - typedef InstanceHandle<I,FA> IHandle; - typedef Holder<IHandle> THolder; - typedef Proxy<IHandle> TProxy; - typedef Accessor<FA> Access; - - I& _i_; - - Holder (IHandle const& iha) - : _i_(iha.get()) - { } - - public: - static TProxy& open(IHandle const& iha) - { - static char buff[sizeof(TProxy)]; - TProxy* p = new(buff) TProxy(iha); - Access::implProxy_ = p; - return *p; - } - - static void close() - { - if (!Access::implProxy_) return; - TProxy* p = static_cast<TProxy*> (Access::implProxy_); - Access::implProxy_ = 0; - p->~TProxy(); - } - }; - - - template<class FA> - FA* Accessor<FA>::implProxy_; - - - template<class IHA> - void - openProxy (IHA const& iha) - { - Proxy<IHA>::open(iha); - } - - template<class IHA> - void - closeProxy () - { - Proxy<IHA>::close(); - } - - } // namespace facade +namespace lumiera{ +namespace facade { -} // namespace lumiera + + LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade 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<FA> may be placed into + * the facade interface; this accessor-frontend is basically + * a concealed static pointer to the proxy, and will be set, + * when the interface is opened. This opening and closing + * of the interface itself is controlled by the + * InstanceHandle, which in turn is typically + * created and managed within the context + * of the service implementation. + */ + template<class IHA> + class Holder; + + template<class FA, class I> + class Holder<InstanceHandle<I,FA> > + : Accessor<FA> + , protected FA + { + protected: + typedef InstanceHandle<I,FA> IHandle; + typedef Holder<IHandle> THolder; + typedef Proxy<IHandle> TProxy; + typedef Accessor<FA> Access; + + I& _i_; + + Holder (IHandle const& iha) + : _i_(iha.get()) + { } + + public: + static TProxy& open(IHandle const& iha) + { + static char buff[sizeof(TProxy)]; + TProxy* p = new(buff) TProxy(iha); + Access::implProxy_ = p; + return *p; + } + + static void close() + { + if (!Access::implProxy_) return; + TProxy* p = static_cast<TProxy*> (Access::implProxy_); + Access::implProxy_ = 0; + p->~TProxy(); + } + }; + + + template<class FA> + FA* Accessor<FA>::implProxy_; + + + template<class IHA> + void + openProxy (IHA const& iha) + { + Proxy<IHA>::open(iha); + } + + template<class IHA> + void + closeProxy () + { + Proxy<IHA>::close(); + } + +}} // namespace lumiera::facade 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 <boost/noncopyable.hpp> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef PROC_INTERFACE_PLAY_H +#define PROC_INTERFACE_PLAY_H + + + + +#ifdef __cplusplus /* ============== C++ Interface ================= */ + +//#include "include/interfaceproxy.hpp" +#include "lib/handle.hpp" +#include "lib/iter-source.hpp" +#include "lib/time/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<DummyPlayer> facade; + + + /** + * Continuous playback process, which has been hooked up + * and started with a fixed set of output slots. started with a specific + * output size, format and framerate. It is a handle to a calculation process, + * which is about to produce a stream of frames and push them to the outputs. + * + * The Lifecycle of the referred playback process is managed automatically + * through this handle (by ref count). Client code is supposed to use the + * API on this handle to navigate and control the playback mode. + * + * @see handle.hpp + * @see player-service.cpp implementation + */ + class Controller + : public lib::Handle<proc::play::PlayProcess> + { + public: + void play(bool); ///< play/pause toggle + void adjustSpeed(double); ///< playback speed control + void go(lib::time::Time); + /////////////////////////////TODO how to modify the Loop range, the playback mode, scrubbing? + }; + + + typedef lib::IterSource<mobject::ModelPort> ModelPorts; + typedef lib::IterSource<mobject::OutputDesignation> 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<class Y> explicit P (Y* p) : BASE(p) {} template<class Y, class D> P (Y* p, D d) : BASE(p,d){} - + P (P const& r) : BASE(r) {} template<class Y> P (shared_ptr<Y> const& r) : BASE(r) {} template<class Y> explicit P (weak_ptr<Y> const& wr) : BASE(wr) {} - template<class Y> explicit P (std::auto_ptr<Y> & ar) : BASE(ar) {} - + template<class Y> explicit P (std::auto_ptr<Y> & ar) : BASE(ar) {} - P& operator= (P const& r) { BASE::operator= (r); return *this; } + + P& operator= (P const& r) { BASE::operator= (r); return *this; } template<class Y> P& operator=(shared_ptr<Y> const& sr) { BASE::operator= (sr); return *this; } template<class Y> P& operator=(std::auto_ptr<Y> & ar) { BASE::operator= (ar); return *this; } - + TAR* get() const { return dynamic_cast<TAR*> (BASE::get()); } TAR& operator*() const { return *get(); } TAR* operator->() const { return get(); } - + void swap(P& b) { BASE::swap (b);} - + private: /* === friend operators injected into enclosing namespace for ADL === */ template<typename _O_> friend inline bool operator== (P const& p, P<_O_> const& q) { return (p && q)? (*p == *q) : (!p && !q); } - + template<typename _O_> friend inline bool operator!= (P const& p, P<_O_> const& q) { return (p && q)? (*p != *q) : !(!p && !q); } - + template<typename _O_> friend inline bool operator< (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p < *q; } ///< @note deliberately not allowing comparison on NIL ////TICKET #307 : problem with equality test in associative containers, where equal(a,b) := !(a < b) && !(b < a) - + template<typename _O_> friend inline bool operator> (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *q < *p; } - + template<typename _O_> friend inline bool operator<= (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p <= *q;} - + template<typename _O_> friend inline bool operator>= (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p >= *q;} }; - - + + } // namespace lumiera #endif 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 <boost/noncopyable.hpp> //#include <string> //#include <vector> +#include <tr1/memory> +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<OutputManager> POutputManager; -} // namespace play +}} // namespace proc::play #endif From c091b8e2603121038a799c35f0f81afedb307a81 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 24 May 2011 03:48:49 +0200 Subject: [PATCH 105/496] 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<BuilderSubsysDescriptor> theBuilderDescriptor; lib::Singleton<SessionSubsysDescriptor> theSessionDescriptor; + lib::Singleton<PlayOutSubsysDescriptor> 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. </pre> </div> -<div title="ViewerAsset" modifier="Ichthyostega" created="201105230116" tags="Model spec" changecount="1"> +<div title="ViewerAsset" modifier="Ichthyostega" modified="201105232228" created="201105230116" tags="Model spec" changecount="2"> <pre>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.</pre> </div> From 4731afefb83e47282d248d90976672d9fcc6d6f4 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 25 May 2011 02:41:04 +0200 Subject: [PATCH 106/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file control.hpp + ** Manipulating and monitoring time entities with life changes. + ** This is an control- and callback element to handle any kind of "running" + ** time entities. This element is to be provided by the client and then attached + ** to the target time entity as a time::Mutation. Internally, a life connection to + ** the target is built, allowing both to + ** - to manipulate the target by invoking the function operator + ** - to receive change notifications by installing a callback functor. + ** + ** The actual type of the changes and modifications is specified as template parameter; + ** when later attached to some time entity as a Mutation, the actual changes to be performed + ** depend both on this change type and the type of the target time entity (double dispatch). + ** The behaviour is similar to applying a static time::Mutation + ** + ** \par relevance + ** This control element is intended to be used for all kinds of editing and monitoring + ** of time-like entities -- be it the running time display in a GUI widget, a ruler marker + ** which can be dragged, a modifiable selection or the animated playhead cursor. + ** + ** @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 <boost/noncopyable.hpp> +//#include <iostream> +//#include <boost/operators.hpp> +#include <tr1/functional> +//#include <string> + + +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<void(TI)> 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 TI> + 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]] </pre> </div> -<div title="TimeMutation" modifier="Ichthyostega" modified="201104252343" created="201101231344" tags="spec discuss draft" changecount="38"> +<div title="TimeMutation" modifier="Ichthyostega" modified="201105250040" created="201101231344" tags="spec discuss draft" changecount="41"> <pre>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. </pre> </div> <div title="TimeQuant" modifier="Ichthyostega" modified="201101232031" created="201012181753" tags="Concepts Player spec img discuss draft" changecount="50"> From 9004818d6353aac9154a3f2dc765174b23834e1a Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 26 May 2011 03:05:52 +0200 Subject: [PATCH 107/496] 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 <b>not threadsafe</b>. But, as each change is basically + ** processed within its own call context (function invocation), parallelism is only + ** a concern with respect to the value finally visible within the target. + ** - the change notification is processed right away, after applying the change to the + ** target; 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 <boost/noncopyable.hpp> //#include <boost/scoped_ptr.hpp> 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 <<END +return: 0 +END + + TEST "Quantiser API basics" QuantiserBasics_test <<END return: 0 END diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp new file mode 100644 index 000000000..2742831ce --- /dev/null +++ b/tests/lib/time/time-control-test.cpp @@ -0,0 +1,137 @@ +/* + TimeControl(Test) - mutating time entities with life connection and feedback + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/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 <boost/lexical_cast.hpp> +#include <iostream> +#include <string> + +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<gavl_time_t> (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 <prg@ichthyostega.de> Date: Fri, 27 May 2011 04:16:36 +0200 Subject: [PATCH 108/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file generator-combinations.hpp + ** Metaprogramming facilities to generate combination cases. + ** Similar to the plain typelist-based generators, a custom supplied + ** template will be instantiated with combinations of the parameter types + ** and then mixed into the resulting type + ** + ** @see generator-combinations-test.cpp + ** @see generator.hpp + ** + */ + + +#ifndef LUMIERA_META_GENERATOR_COMBINATIONS_H +#define LUMIERA_META_GENERATOR_COMBINATIONS_H + +#include "lib/meta/typelist.hpp" +#include "lib/meta/typelist-util.hpp" +#include "lib/meta/generator.hpp" + + + +namespace lumiera { +namespace typelist{ + + + template<class TYPES_1, class TYPES_2> + class CartesianProduct + : Distribute< typename TYPES_1::List + , typename TYPES_2::List + > + { }; + + + template<class SUBLIST> + struct PickFirst; + + template<class TY, class TAIL> + struct PickFirst<Node<TY,TAIL> > + { + typedef TY Type; + }; + + + template<class SUBLIST> + struct PickSecond; + + template<class TY, class TAIL> + struct PickSecond<Node<TY,TAIL> > + { + typedef typename PickFirst<TAIL>::Type Type; + }; + + + + template< template<class,class,class> class _X_> + struct PickParametersFromSublist + { + template<class SUBLIST, class BASE> + struct CaseInstantiation + : _X_< PickFirst<SUBLIST>::Type + , PickSecond<SUBLIST>::Type + , BASE + > + { }; + }; + + + template + < class TYPES_1, class TYPES_2 ///< the two type collections to pick combinations from + , template<class,class,class> class _X_ ///< template with two arg types and a base type + , class BASE = NullType + > + class InstantiateChainedCombinations + : InstantiateChained< CartesianProduct<TYPES_1,TYPES_2>::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<class TYPES> struct count; @@ -80,6 +109,7 @@ namespace typelist{ > List; }; + /** conditional node: skip an element based on evaluating a predicate */ template<bool, class T, class TAIL> struct CondNode { typedef TAIL Next; }; @@ -202,7 +232,7 @@ namespace typelist{ typedef T Head; ///< first element typedef Node<T,NullType> First; ///< a list containing the first element typedef TYPES Tail; ///< remainder of the list starting with the second elm. - typedef typename SplitLast<List>::List Prefix;///< all of the list, up to but extcluding the last element + typedef typename SplitLast<List>::List Prefix;///< all of the list, up to but excluding the last element typedef typename SplitLast<List>::Type End; ///< the last element typedef Node<End,NullType> Last; ///< a list containing the last element }; @@ -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<class TY1,class TY2> struct Distribute { typedef typename PrefixAll<TY1,TY2>::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> class _PERMU_> - struct Combine { typedef typename Distribute< typename _PERMU_<X>::List + , template<class> class _ENUM_> + struct Combine { typedef typename Distribute< typename _ENUM_<X>::List , Node<NullType,NullType> >::List List; }; - template< template<class> class _PERMU_> - struct Combine<NullType, _PERMU_ > { typedef Node<NullType,NullType> List; }; + template< template<class> class _ENUM_> + struct Combine<NullType, _ENUM_ > { typedef NodeNull List; }; template< class TY, class TYPES - , template<class> class _PERMU_> - struct Combine<Node<TY,TYPES>,_PERMU_> { typedef typename Distribute< typename _PERMU_<TY>::List - , typename Combine<TYPES,_PERMU_>::List + , template<class> class _ENUM_> + struct Combine<Node<TY,TYPES>,_ENUM_> { typedef typename Distribute< typename _ENUM_<TY>::List + , typename Combine<TYPES,_ENUM_>::List >::List List; }; - /** permutation generator for the Combine metafunction, + /** enumeration generator for the Combine metafunction, * yielding an "on" and "off" case */ template<class F> 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 <<END +TEST "typelist manipulation" TypeListManip_test <<END out: List1 :-<1>-<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<int> 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 <boost/lexical_cast.hpp> @@ -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<class TAR> + struct TestCase4Target + { + void + performTestCases() + { + + } + }; + void - runningMutations () + verifyMatrix_of_MutationCases () { + typedef Types<Duration,TimeSpan,QuTime> KindsOfTarget; + typedef InstantiateForEach<KindsOfTarget::List, TestCase4Target> 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]] </pre> </div> -<div title="TimeMutation" modifier="Ichthyostega" modified="201105250040" created="201101231344" tags="spec discuss draft" changecount="41"> +<div title="TimeMutation" modifier="Ichthyostega" modified="201105261700" created="201101231344" tags="spec discuss draft" changecount="42"> <pre>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. </pre> </div> <div title="TimeQuant" modifier="Ichthyostega" modified="201101232031" created="201012181753" tags="Concepts Player spec img discuss draft" changecount="50"> From 786ecbe82944658cf2686ecdccd3d98416f33725 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 27 May 2011 21:27:55 +0200 Subject: [PATCH 109/496] 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<class SUBLIST> - struct PickFirst; - - template<class TY, class TAIL> - struct PickFirst<Node<TY,TAIL> > - { - typedef TY Type; - }; - - - template<class SUBLIST> - struct PickSecond; - - template<class TY, class TAIL> - struct PickSecond<Node<TY,TAIL> > - { - typedef typename PickFirst<TAIL>::Type Type; - }; - - template< template<class,class,class> class _X_> struct PickParametersFromSublist { template<class SUBLIST, class BASE> - struct CaseInstantiation - : _X_< PickFirst<SUBLIST>::Type - , PickSecond<SUBLIST>::Type + struct SingleCaseInstantiation + : _X_< Pick<SUBLIST,0>::Type + , Pick<SUBLIST,1>::Type , BASE > { }; }; + /** + * Build a Case matrix. + * The given parameter template _X_ + * will be instantiated for each possible combination + * of the elements from both parameter type-lists. + * All these instantiations will be chained up + * into a linear inheritance chain rooted + * at the BASE type. + * @note the custom-supplied template _X_ needs to take a 3rd parameter, + * and inherit from this parameter, in order to form that chain. + * Typically you'll define some (static) functions within that + * template, which then forward the call to the given BASE + * (and of course, that BASE then needs to define this + * function as well). + */ template < class TYPES_1, class TYPES_2 ///< the two type collections to pick combinations from , template<class,class,class> class _X_ ///< template with two arg types and a base type @@ -95,7 +90,7 @@ namespace typelist{ > class InstantiateChainedCombinations : InstantiateChained< CartesianProduct<TYPES_1,TYPES_2>::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<class TYPES, uint i> + struct Pick + { + typedef NullType Type; + }; + template<class TY, class TYPES> + struct Pick<Node<TY,TYPES>, 0> + { + typedef TY Type; + }; + template<class TY, class TYPES, uint i> + struct Pick<Node<TY,TYPES>, i> + { + typedef typename Pick<TYPES, i-1>::Type Type; + }; + + + + /** apply a transformation (template) to each type in the list */ template<class TY, template<class> class _TRANS_> struct Apply { typedef TY List; }; 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<typename T1, typename T2> + class is_sameType + { + static const bool value = false; + }; + + template<typename T> + class is_sameType<T,T> + { + static const bool value = true; + }; + + /** semi-automatic detection if an instantiation is possible. * Requires help by the template to be tested, which needs to define * a typedef member \c is_defined. The embedded metafunction Test can be used 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 <<END +return: 0 +END + + TEST "TypedAllocationManager" TypedAllocationManager_test <<END return: 0 END diff --git a/tests/lib/meta/generator-combinations-test.cpp b/tests/lib/meta/generator-combinations-test.cpp new file mode 100644 index 000000000..be6b322fe --- /dev/null +++ b/tests/lib/meta/generator-combinations-test.cpp @@ -0,0 +1,137 @@ +/* + GeneratorCombinations(Test) - verify generating case combinations + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/meta/generator.hpp" +#include "lib/meta/generator-combinations.hpp" +#include "meta/typelist-diagnostics.hpp" + +#include <boost/format.hpp> +#include <iostream> + +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<class T1, class T2, class BASE> + struct TestCase + { + static string + visitAll() + { + T1 param1; + T2 param2; + return str(fmt % param1 % param2 + % BASE::visitAll()); + } + }; + + struct TestCase<void,void,NullType> + { + static string + visitAll() + { + return "-|"; + } + }; + typedef TestCase<void,void,NullType> 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<Types1,Types2> 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<NAME>() << "\n"; + cout << STRINGIFY(NAME) << "\t:" << showType<NAME>() << 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 <boost/format.hpp> #include <iostream> 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<List1::List, Printer, NullP > 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<List2,0>::Type e0; + Pick<List2,1>::Type e1; + Pick<List2,2>::Type e2; + + Pick<List2,3>::Type E3; + Pick<NullType,23>::Type Nil; + Pick<void*,456>::Type Irrelevant; + + CHECK (5 == e0); + CHECK (6 == e1); + CHECK (7 == e2); + + CHECK (is_sameType<NullType, E3>::value); + CHECK (is_sameType<NullType, Nil>::value); + CHECK (is_sameType<NullType, Irrelevant>::value); + } + + void check_append () { From ae36b2d941656faf63487e962f6b1ecc33ed7dca Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 28 May 2011 01:46:06 +0200 Subject: [PATCH 110/496] 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 FA> + class Accessor + { + protected: + static FA* implProxy_; + + + public: + FA& + operator() () + { + if (implProxy_) + return *implProxy_; + else + throw error::State("Facade interface currently closed."); + } + }; - /********************************************************************* - * - */ - template<class FA> - class Accessor - { - protected: - static FA* implProxy_; - - - public: - FA& - operator() () - { - if (implProxy_) - return *implProxy_; - else - throw error::State("Facade interface currently closed."); - } - }; - - template<class IHA> - void openProxy (IHA const&); - - template<class IHA> - void closeProxy (); - - template<class IHA> - class Proxy; - - - } // namespace facade + template<class IHA> + void openProxy (IHA const&); -} // namespace lumiera + template<class IHA> + void closeProxy (); + + template<class IHA> + class Proxy; + + +}} // namespace lumiera::facade #endif 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<DummyPlayer> facade; + static lumiera::facade::Accessor<Play> 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<time::Time> & ctrl); + void controlDuration (time::Control<time::Duration> & ctrl); + void controlLooping (time::Control<time::TimeSpan> & 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 TYPES_1, class TYPES_2> - class CartesianProduct + struct CartesianProduct : Distribute< typename TYPES_1::List , typename TYPES_2::List > @@ -60,8 +60,8 @@ namespace typelist{ { template<class SUBLIST, class BASE> struct SingleCaseInstantiation - : _X_< Pick<SUBLIST,0>::Type - , Pick<SUBLIST,1>::Type + : _X_< typename Pick<SUBLIST,0>::Type + , typename Pick<SUBLIST,1>::Type , BASE > { }; @@ -88,8 +88,8 @@ namespace typelist{ , template<class,class,class> class _X_ ///< template with two arg types and a base type , class BASE = NullType > - class InstantiateChainedCombinations - : InstantiateChained< CartesianProduct<TYPES_1,TYPES_2>::List + struct InstantiateChainedCombinations + : InstantiateChained< typename CartesianProduct<TYPES_1,TYPES_2>::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<typename T1, typename T2> - class is_sameType + struct is_sameType { static const bool value = false; }; template<typename T> - class is_sameType<T,T> + struct is_sameType<T,T> { 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<void(TI)> timeSignal; /** * Interface: controller-element for retrieving and @@ -91,10 +90,12 @@ namespace time { */ template<class TI> class Control - : Mutation - , timeSignal + : public Mutation + , public function<void(TI)> { public: + typedef function<void(TI)> 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 <boost/noncopyable.hpp> +#include <boost/noncopyable.hpp> //#include <boost/scoped_ptr.hpp> //#include <string> @@ -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<DummyPlayerService> implInstance_; - ServiceInstanceHandle serviceInstance_; +// lib::SingletonRef<DummyPlayerService> 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<void(void)> 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<uint>::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<void(void)> 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<uint>::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 <boost/noncopyable.hpp> +#include <boost/noncopyable.hpp> //#include <boost/scoped_ptr.hpp> //#include <string> 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 <boost/noncopyable.hpp> +#include <boost/noncopyable.hpp> //#include <boost/scoped_ptr.hpp> //#include <string> 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<DummyPlayerService> 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<DummyPlayerSubsysDescriptor> 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<class T1, class T2, class BASE> 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<void,void,NullType> { 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<List2,1>::Type e1; Pick<List2,2>::Type e2; - Pick<List2,3>::Type E3; - Pick<NullType,23>::Type Nil; - Pick<void*,456>::Type Irrelevant; + typedef Pick<List2,3>::Type E3; + typedef Pick<NullType,23>::Type Nil; + typedef Pick<void*,456>::Type Irrelevant; CHECK (5 == e0); CHECK (6 == e1); CHECK (7 == e2); - CHECK (is_sameType<NullType, E3>::value); - CHECK (is_sameType<NullType, Nil>::value); - CHECK (is_sameType<NullType, Irrelevant>::value); + CHECK ((is_sameType<NullType, E3>::value)); + CHECK ((is_sameType<NullType, Nil>::value)); + CHECK ((is_sameType<NullType, Irrelevant>::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 <boost/lexical_cast.hpp> @@ -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<class TAR, class SRC, class BASE> + struct TestCase + : BASE + { + void + performTestCases() + { + + } + }; + + struct IterationEnd + { + void performTestCases() { } + }; + } @@ -124,22 +142,17 @@ namespace test{ { } - template<class TAR> - struct TestCase4Target - { - void - performTestCases() - { - - } - }; void verifyMatrix_of_MutationCases () { typedef Types<Duration,TimeSpan,QuTime> KindsOfTarget; - typedef InstantiateForEach<KindsOfTarget::List, TestCase4Target> TestMatrix; + typedef Types<Time,Duration,TimeSpan,QuTime> KindsOfSource; + typedef InstantiateChainedCombinations< KindsOfTarget + , KindsOfSource + , TestCase + , IterationEnd > TestMatrix; TestMatrix().performTestCases(); } From b25d85e1ddcb0d776265f6c30ecab0452c1c0f99 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 28 May 2011 01:46:26 +0200 Subject: [PATCH 111/496] 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 <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -* *****************************************************/ - - -#include "proc/engine/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 <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - - -#ifndef ENGINE_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 <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -* *****************************************************/ - - -#include "proc/engine/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 <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - - -#ifndef ENGINE_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 <prg@ichthyostega.de> Date: Sat, 28 May 2011 01:57:26 +0200 Subject: [PATCH 112/496] 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 <<END -out: List1 :-<1>-<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<Elm1> :-<3>- -out: NList :- -out: Types<Nil> :- -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<Elm1> :-<3>- +out-lit: NList :- +out-lit: Types<Nil> :- +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 <<END +out-lit: Cartesian : +out-lit: +---<1>-<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<NullType, E3>::value)); - CHECK ((is_sameType<NullType, Nil>::value)); + CHECK ((is_sameType<NullType, E3> ::value)); + CHECK ((is_sameType<NullType, Nil> ::value)); CHECK ((is_sameType<NullType, Irrelevant>::value)); } From 5e8a9b50d131e4dd934788755ddfed143facb77b Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 28 May 2011 02:22:57 +0200 Subject: [PATCH 113/496] 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 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 FKLD{o>y-ci literal 37738 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 Zb3eeo+<U}i;snP=NZ*r{$Pv@={6BMZ7-Rqd diff --git a/doc/devel/uml/fig130437.png b/doc/devel/uml/fig130437.png index e12e4b48bf38bf07aa9a405773228ec05247ea8a..9257234800b5a6d48ac6fffb7a0baa94d390f1c8 100644 GIT binary patch literal 33013 zcmb5W1z40{yDmOpFi1Dj9U>*&Fi0pZ4H61SNp~X-pdek+-Q5jRlF}(9-QE3P`1|(R z``i2cuXD~^Tr<GTys_T3p67n<`+nZ9it<uuPl%pCAP}@y(l6gYAP;0A5JX)lBKQw9 z8oM0=p@6)4DW>9_us!SbR_=@(X@4NsGV*m_$RN#8`aD;piq>KNs=DNtRt;<k${{~C zx?H0=>~}-~EA`*?j_OT&_P)KzE&bUSLihUB?Cx1exsKD>oagNBfSvAuzeun`vR(h$ z5#O3;|CML|MS{%1h5P-UA$@77+kb!Y-lpt&0REv*@zCY|4OBP<26*xQgt?3WUfD2x z1n>X-7#4~IUVex|5AXjZl$M!#1c4~Z;7TyoLn#c-9U%IIv5#E(cK7zXksvnfPrt*b z)Y<jvHG`MgE1TQs$-$RK(UZFW$F95^)6&u^Dk_o$U8qj*7Z(=|HOL^Ll(a}LxDt;Z zJwm=E#>IW9gqu7tUumv>cY9-DVR3b~cehhLKdf-|%ALFDD@I=`F(L%BW$j4$&713+ z8w?U&iW5ngxVVu94cO93Gi+sZEWfg{QcFt<JWmwqP{X%>SDK&g&eAI+ZBLdijbtly zhL9^LjedK|K~towt2-Cj2>0{zgTuSZW*`u=-PK?5LaxUe6(&O!rX%>zHAJDWUf(|! zt$CxOqC7o4U0q#U??}OCt}l-Cnmr3sQ(IPS3kwSy8ycXL805m7+}vu877$TXJ;{t| zTuK^2!RygnWq0AbzF3Zhg#|dKj7;y=L@~<Y+gjU>pFjQj8}wTcrKCD<Z?5zjT~$kb z-OhG{2-#bwrd;;_v^3vdG9?c*+pPuX=jS&z-h5{_&~0#Lx18aHb&-2rr*`EbK+4%u zjG@TLe74Kb<bmqy>VbiQwWAA*>8fAd-3vz-#k!5ZYi!cHyk=)-t>zlK*N#x0Jb8>x zB5egn*bz05m4G~qBwt=xUUt9S$aWW=pP#=2g8;|W(V3kn)<Zc|QBkR|oQ><hUJ4O8 zytoDz(DUXJxr$<L50!{>eYrcrUD#`DypUc#A+*k!IhP2+7GHOU%EiT%DBy(ezMxn7 z{!#LPh}|lx(0d-6#rAZmkbrA-@CZ$ATNs>HI&;L-|JN~$jg9f~^GC<T2=MWN--oDO zz!k(<u65ke+M8?K=mQtPUAQ?d4JIWeRr2V-6TJBMPqi*qy6ZsnpE>M34JH>6HXCJd zy%;al&dSPiaByg8X*qV<Zf$L?vX~CIo){huZ_>5}b4#b*acFCJwkG@;rOc+MC_+2q zU94b0nxK$S%yZ2b?&sF7_#J5yf%q(M&)YCW1_zdzHA?gsT7B(T?biB(h`2VvHFb1! z^kM!U7nhZhp`ooU;=KO{ObIxqo!wDE!E+}kCzhD$CJ#4qa&j2BE}iB<2oRHbkF!}Y z4>pX5iQ~Z$eK0bLjgQA5;*?fUD6XpF=I5`gs!}^|23rQZSvhbPBx8N{EI@anudg5@ zqrJWTr70JJx2vN2(;L}GLQHCPmAkX`6WI#M4Gn^Jc6I@2o12@Cdvm{5Y|YKgpp>6J zeFB@wWC%#h0jHu<Xa5zm%;U<@+}zw1MZM@PGBPsOvB2)`E|2956dj!oPDn%qMs0Iz zD?UCxS<vN8ouzc${rpNb)+Z!U)F%TgmH8N4a2B1qK^~VCq-1vprhn3>ch;hxTt=^q zy2SlZ4~~wM9U1TAhDxw&$TH*O&-srbZ^Q?Fu<@q+5bQ~IMu4PbvBd;}?}!zC^^yrQ z3hn$8g7i5*>-qUlR8;Q~LWl~yT~s1kjO<dtgzDihu%*MSIULseO^Q<eKLswR7}@=| z9{k@$z2P^GAPS0_nwgmu6cjvp@`UKuE2T)p9%_1bVZA8{h$7<s&Ywqxhhsl|s;Qwd z*?#SQ`^W`fN|&KW?*4uB*J|@t46i@LBmQigLPY)HJ@UPC2?Swu|B=W5Mcm9Lv7|b3 zMsLYi+hJ_sNRS_I;^N|ZSP&sySod*Pzh*37eFP<6t?-TID+umaopN)=)i;hKh>&Y3 zPfuitT1}0<l)LHbVseSvm9-eo2K+>SUG-gwhx+{ELCd)t9JFZSR!~S@Zq50?@US|F zH@7#Pa(cZ#M4f(DEF3c7L1aqIX+XSx+2!yC*6i8Hs#6yc6*b-LMb5v{5qNm8)I2zH z#Kx2Cd3!Af>)M%ap8DO-%}K&#H8)|Oq+a~&Y)3#2`bz#b*J;o6_wTlE-+oF-?8NBt zArK;iab|!fv4kR@m`M^4Z1wd`Yiic^_mA86T%Ncc9m!bE$r1^<hK39fkrlI0P-H2P zFGbL&JJ@S|E$}=xQIqa!Z)gyD_fA=@Xr^#~@$)dlQZ2p1r6CvAmgU3?JTj)oT&>?N zBiZr7$O#DI*Vl2GnSX_1?tTCM03NzEfdY1Ld3=z`A?SMioIZO!-U=U;aByO{OzPvd z*ut2QAj#>Th}~kl5v*&vnk#@+gdyw=S@HYHB?ZE6^Oqi-0xT?r3wVu<>S%;)YYT10 zWvn>faZ4ONWzVQgBr6HstZmC091H(!jv><1%kX!MRs=>wsJ`lM`HVz8QIt4as4d0B zG_t-v+8eW1q!a(V%+-3I2Zz4S;x}ipLh>1&t|O$<pQ0E`RW+L?%ir%OPs4jHElChL zN^PY0aG!|YDB{wRWqIS$NP0da-f`GAF){m{sa=hp$X}VRJYEo+nVvqI9b)ye9UTP) zM@}vX4wtB{J@ZG?K3*TP3maSOd)nxHFpi9)29_;0EFkbs#r_dw^r_`U_~BvC@^Z2t z>cwDdn!j^Ea6eS|4pE|(S!dI3xr?>U?_|ErC^cqc4Ib)`k=*EV<am2sGf|w+$;nwW zgK55%1jh`p)h;u9L20<y9(8a)NiY9)u8vDbK_R=K;QeTh7%_KW_b(4NFh5jP$0Q|p zA|j}hM7$Ww!o>YOEyn_uHuw~kkCGYQ3O*7IQrx^-!h9w_*wAXDEAzWy#UBJT0gw8f z-PvWYUb|1aymoYIyvBF89=kIa(YkZhdngNidS0Z*Clg}!J)2|6RT+X1x%Q0CWDQbw zTs)yCXhF%GjKhP9#p=bD*vb#-6v%837NZNbF>qkRBNJG#Z$Sc&AGW`gcx3qDCq1q2 z#g!Dwi~5?$!71>@T>Y8#)hR6jt0kY)?p6@7GL-W6b_ffrNxkUf(J{&Q#6<cgEOqtx zgoLzIad93idAEzh%643c!zZXhm?2k#R58oZB7*cz{hoYmocz!jsi_rtXjGI8l#$t^ z(sTrYk(>4;z|6mq+J&7R$#G9OEP-UGSTFhV4SZ_qWO#5eCqExYzOTRkYQJq@-3Wb6 z$H3K<9~zyQP_Vc-{JU~w&6;yVMn_&gEI%KOl7`Kigwn7T9z{jGF*&p!$a>w{=JDoD ze_ZSZDPP&#+?=eSgkumwitMYu4-)k3va(?S0Dk?lxV^prkxN{hT3C3lzdsl%5zvYv z6aBo_W=RqtD^=B~urMWC+ry)^fwh6;yUP=EG&D4p7;_7Y!sR-~s6PKQNMvS1gY9@h z&ae2-79WMB<!?=YSFn0rF|SOOmz9(}yd2jomu^h^{ccZs?%Pu`VPSD`@mf8v8xKuQ z-*()0L_}#89cxEm&U)TB5F~po+}@0~w|Dr6n3~%??Dh2AN=sk+*~ywA&M%y7WoZfX zLRql&l77X<WJBH2;U)QU8~jd8N@`+$^$Cpr*hJ*J23nbD8ag@vfDT%HQ2@fYy}i|Q zKj_GmiHT;_6E^Noc&uh`W0RYg7x3jvjqOT6cD6A{rJ|yu0K<Iv@Zrsy@aE=b+5EJh z8N_pK`eZr5Jw2Phwa7x$Co+58z<jgc*QfbA-Qk!7F%*~dZ=zY}h3!@!FC@#PdWq69 zF-^3-myG8o)74E@S5ZMO&MDYrKy~}vfKK|`^R{+AmV+Iv1F%jmwu(Cz8PN(XKa4kK zEN|@2_OA8^IoP8fpXwUDAJ%-|UsA%DF2zdDvwJjKufQvyEg_Y?J6USj5unrGFV-2N zM+U;>xg<7DNP$M_QG~+V5b}`26Vjr>UswuZGU<>m6&$NkGJbmoI=T;i-`UGQ`~ugb z>(?&_J3DGP>eCm?{fPptd=h@cLPA1zwzh9|bdc}C02vwC)YKHY=Q*W>lE>9)qs@}v zbEY=jJQ4K=qHovwcTTr8o4oFx%Je=Ja=V<VQIdwflF=52cK2NylP!;A8;xX#VCwO( zu{rh;X4|bb_4i(R+}(04GQA*lvzi#@C1EB{P6e~TrGft|<8fQF&zkQel;iYvI=V!! z3yI_BSeiyFJx`g``!Tzz8=@N<W4k1UuRhv~{+RSrhN!$9A@2}yKi>zi$lk$WZ?@jp z%F629yRfh@fSSScJ5y7}Ni9=T(^uVIo);?G+S4;Lf=o=kv$Ki4D^*tWbEBi2w6q<+ ze_w*o3}$qUM<HPbBahvNM(8gm=M{C2Xr7ODs%lLezbo5Vo7*SG53zB)-ZnUICGZ)% zJp4Fa^`&hn&5K;K#scNZ?fL2SilbxW6O^-O6@$;bcf^vs&kIy(`wdicTOrb^ngnbB z<%xZMys@z{Ha;#XC51uAM!~>f@+*!DSAv$7cE|<s!GpxaL?J=Jm*cag22$46`vG`N zsAy=)%F1phn_N!2Z^=RcJOCAup(gDrw#!+PbeeTSXUSQ+4^b+ES>N{+nP}P5eZuoj za)o-w9g6djrOwP_#2ZDod^HkSSCWv3kjJHu{N7mp^ju?he!lsK{_AJYKlT)8lB1|? z53dOMs);Xv!p{2}m(v$2w)=-7?@ElGCsudcitUDPO%1Q>r#Xpc&l@<$^z?LKF^U|$ zjkCgfF#)a?(Nj~`k8$zw0#<W^ErMiVS;M4FOx6cP?zY$0yH;$SKqcj{r6wfQSXNf1 zlrCv8S@H=JrZhU7DHB4%_u}JZ3A1+9muy@-yjKYrG%r>dXlP!toESeOO;o!&E7qv` z=w@$kwm0|l)vI^#(6jly(Q;#Ty?KYXyY*Qd>>JxFE7-WWJf_2#{6<^jg9E~W^3uG5 zW|2#gjJvH&NkYU-HcsCt%NO2P61rqDyp|Kl^;3$}HZ-JZEWqD8Q3@=7l+l*-xi>Fv z7{%<j7k8t-y{)ZQrP)|l7&Q(ywyc~SxF`$(buLG%NaXH=V7k@S)qx1&>gEQ<LB#E} z0AS{0@ibfs9e|QRF$T(*)dS~6Msa`ips!y`%gdRbKTi<$aCLO#PRs^X*3+j?tE#G2 zR#v8~EKpETBHXldFpPAH$97$xQItxxN|QmlP~8^EqEp%gS_I4^bx3M)k~*oSD@sd; z1_x7!Te`a$Sy+ldK|+Eqb*@rfUXJ6^8EySUi2RHEn)RGqe0nwsNbzY7TjLzY{gGC) z-ck3L^&>^A0rtXvu+YoZgY)zA(7Ps)-hqMQl#~?ni6UQLUmF`6pX$CZ%?2{OnN1g> zRLUUM^(<=(>5~H(OYz{#KH=xY1yVUfvJhv#l9H04bgAaM+bev0{Qmy_p`js}7#0ir z@_MJeXkKz@!=A9`=7^$Dv3p_w@j1r0#OgulUH=O@cmXJ4S9+qT%)c&U>4ox=f4Pn5 zilp|A#<|5^>L7s(zIh&k`W^1u8HP4MgqRQY3SbAwm)Qpz;<71_)#s)_)~~Djv*AeY z;Qo}3t%D3RE<M*_9(t*76!`mfqp}Lra~M3c4?j;=qUC})K4HlzQJa}<j16OX+m*_| zgKlhMv=R^C?x#<sl9Ef)6$gaeAC;7bs9{~bF~wY*ei>OwI3GU^tPwvAy4%e6kekwx zliSIXL)xA9dY-)r>B27xL#6x9)1$)c_6ZGhnJc4Y|DC3$L=4NGs;VjujQiuHvEk_4 z&(#NH#k=Vt_%k&&R=?sj1stEd++MFF@Wn$Z)r(h;F0L^|wwWhe!3u5jL;KEqke;=@ zJJ;y{?j3U9uEr;Ja9)CsJ6*s&Xu&>c1OyahWJc!Z3i9&!0bs43Cv)01Szk9PwVa(h z-9dZ7Q4LDO51XU!-ddVCI+9T?AFc=j$cxA1NJQw~@ZlF3tSeV}qUr7y1}3M>>!g}m z=Fy2WRP;GntA>i>F*q_48mjQ_^u*rgm^<uCyf&1QhQ=`P&<u5urbe>tA@!0V9-mvT z7h^re4@2l7QFvmavGIfqz;$_fu1h~7r^-e80j{%7xwySCj%GPY^+JioJUp8(=C?PI zS(})cNaP!#7Mw92$pZMBNqzVC)QQ<xK)HSkPjGN_XRyVWkh_Hekz^bg{_}H585s@^ zI~j1C00@aekx@z3Hu%_tJQDAZh8M3^vgb)rC0c}30s%n`9eZO@k>%1)T<z&8Ztn7g zgw=VFg0e?KUUBW0j2^B`b8uKmz1%jjpRm_qz#F{&@W6yyw!uW&(Q)@r3wN=u>Rh9? z?d36mV15(#M1q!}*DZjEA^jla5|bwKgMcEO>~(QCdvVk#d^@Qmm91Luc=`7f(1{E+ zRaI}6LNxs`?y&JHlQGj$Q<YRzJ^uWto%a%%EY$WN$u?M*TUo|MBm0<M(1XbNsG|U? z@ZEYW$EkT-%jyZ1o0YZl_?u4Lg_p5Vv>*)RScW7r;;Wkhk@=pU#m&vCCeKD|fS!&H zPoa)Fst&Ig$8GJ%1wF5;0|QTjNu`bs$uu;MP4p*DelaQuw!5T2pog2PN=gn_r#ApB z7!S-%)*fkC@6!YDKV3PpH#<E!Gz7vrx1hV3!^w0;hJle09+R4Ev)A20xv`OpOS4|B zEzk4k?=FwA0O`}{kng<E`qk@BM^|0lX;pSRU1|UT{cqoXi+H~6SsK%6*0sKNb~IeB zj$yHpljWapaIqRnd(~6KBig94yvab{sv5yrssihfz>Y7A=Fh?`J!qJjK{{IX&{0*j zFdP-;<7)*&oi-EwJT@Wz%DXe)?WXRvxcKK@w9=~GT7%A^LhX^0WBZ<-XOclg74O<e zSc?ac@98qPqt%`33!Bzh9Bg!QzA|0)YA(tQuRCER49P=h2Q96*V|fRANlaKYYqH}4 z3_V1>+T#jadl~~u`F0m$sS|?^?(9-)UAytt@{99D$5z@gho(dTLZ2T+vF=0>-sspE z8i`3*m`#3G7B_>5y9vWYOUt*koQ#YTZ}0Ek-h?`{YPz~CdLCRLl5=;47z5q}6DApq z8qaNU9)LGuHB+Om-D_vfw)gv_vDU6L_-Xmf`03~6WhhJ<1PNb1SB1p7D!a8)8yhhI zbC4+?Kdu`ZVkGx4%wLXsP=b6vxhHu+d6BR7i-d1jm~z3|(J{&2{{dxtN5|^VAUAgQ zqwQ^1Jp5aBkIT#LZMfSRndN*_``p|%z}WqXomP`<^g<q_617i>ok;lY!=zWk!|8?G z9I>`Le%EC?yCftOj2EAY*srg7d&^BtA;UwJvx@*0C8q@2__#4_A(xAbUZ<r$IV~Fr z`(RwF<*v^V@MhV~352%hcWq4|=qp`U=Nkp4%}`NOZ>*^1t93i*y-3A3M){(@uM|;a zhH8n45T-rpbE~ZGWN5m91(G<7Z#FMqq1v1$tLzXhv30?u(Zh{SNa*NlkF~-)dtkxj zXGi9cb_xTJ@Er`GWvW=W<L~ar>yMe3l=%;i1|kq2bb`~#I2wt$?&(Rkx7TX)yvZ`? zw26um!GdX366q@0__PhVUR6oz-7Rfyj=Zl7oe_U_1AW#A$&+oFFi*Y7n>tYuUEF|w zB{;M_HW>%O;Q`{$9)Z>eijcQK8abanTUuI%-`8aSloT@bX6AOX|E&;#V~R983=P>W zEfG9^987(8qojV&XSe!Iv%%Ti#f6q~d9gzegG_K9lm=Aw!cMz%l*`8(=J@z@_5n>z z@<v8ro|cvU{vleufoHfp^A7T^Mu&;j($aAtncQVh=z$Tz!<0-y@&)#K$6?d+l)cGP z8s77G5X7GnZDwW_;bs6z^W*M}#yt!ilaP3X32SZ5`bO4xeRC$Dsqy`vqJTwb)8KCn zk?Xv=zn|;i;Ev>9iz@*(LCRYa;Q3f`awt7!yl7TjBtj~CaylLlH<Fk4``TIzc;?BE ztFc1q^P8@eO>u1NHM6OLpdsZ8qgp=ws;caJ8_{IN6kcOBzj1YTce#kt%w>Lhylb(& z#G&HOJhn~w)cr{%iRA=0f#n3E=+PglcKK&+gVPnJ%t^d1WmiGOzEM$DwzdHcjkCDA z43IHjASKWkD8T)Z%10$6I$j;T1Q{zQ$NTy^JcxMh$y_y6C7`>SJg&Gy7rG<p<9V$5 zdwN_?HdRs19#L2oxY1!Kmm$RY2w{=2i2`K!VoDxx?uLej@7}$8;PM1eA2>J)$-;ox zL_^&l<;vk4f8_GoE9)g0X06Xh%GL*tah#^%)zv)yZKf0<iHWmd^)NkqCMzq8tH$Qo znpwD_L9mP<+NMFcP+HieNG|v5=iVtGOjvarG!T5>@hvFa>jqI8*p+_9|A-#<++jmo z>pMYjKQKA93=u?iN-iA>+tpdPl-|D80+bcL<wMcT67E(5i;h&r%d@@Y<Yd+c$P>ER ztIhpdI>PP<EfbT5vOoxh%sOVOW`-w&Fd&*fS^S>*RaKb?D4o3~k2DdnY24lztAV}- zQmj((y0W2{!cd<d=vYZasREo0q+fT$bd`qE!>HTdzV&&lLom~HL!2nBWVx{Lb#-;* z^Jg9$*zVlT?3lx+{4F1&-l~zCxc}gDhAza#m#)r|IlMd*Y&Ri;@4xJez9YKzONb~x z8ZS)j43T1fTa#I<4Kn@y`N2F3%XkQx&_J&ng$I_{FJyksc9)MBhlq&p)6x)Ma4bcH zw_e|*KiY_($EbC-=^ZchN51DwdZXoTF4nAOX13Rxv$Bh+;e=pS-w7D_b^4;%AS3(Z z>AFLnio!b&H)Z6`uPgwiQEnF+G2%`Tlai1?1?&3p>66#Zi4(wU0k%wxE^po(8vgkq z+QMUaaE%DKd%yyincp3e;RM*CpvYJ|=um`<@SL?sku%#{e;084_)=5z5hc9$=H2yq z3mlV`m4Jmur!)v550)Wr;F8dJO2H~1um{-tbcX6Cj~mOG8hYvK@~yE{ae(;wVO>GQ zXMh|F#a!Gpqqd>u<`z0Raj;2*IKZJ88Fbe-<MSeyfNo*YS!B`M6)^6ngLUO71snIj zZMLQ~{vw`Q3n<Yus~LI3xFIRk7*M(xjzv<b+Y6+_n--z%%CGKbI4i||vmp7lgo zBqcp4FF%4(X3Oi4vZb)zhv)6l-24CwR7GTL0Z#5tmx3f`F(^A1*XviBrWUhDEuWkF zlbac(CkCAfZnd;ddA)8|JIMtA`JwHs)8ay%@KiL2qV;ohN$EyzYN}W=thN?o-HlY- zZ_>qWo*w><Oo#zYxT4^@m6+t6DZB6idYKq`tOBN_WI7wN`0VV@fQ}8j=Hrt%CfNJ& z!^XZ!<kopSH-eHfLz%uUJUkn__qlti@j^FijxhDsoT$H~Uhz}SEx%GB{;5W{shQf+ z;NVYlbNRBe{4#yzc5B0|&DZ_irng^lV)zogTHkY&@18hNLCiiS>Lk4^xQU*Jwa!~H zKbL*+!V(Jxo;0;iDd6ts@Ad$U@VZe2*s{6a$qR^mB87!+!?73cP0qKEP0a1ukAN6q zXtaV2tC=AO#1}T~_{8BEQ$8|fRMZ&Aw%9zh?)@m_PMdlGvRJs1VQ$9H>Vj2npiCkg z1!Udn%`a{b1J{**_!Ng9!(3fg%Z!x$F~~#1Z2@C&wjnax82_A|0|G(WnZ&}!t#J7j zCv;!GGt<HqamN5P4k)pJw7y?*sAwnaGS6?&GzGovs+$cu!~guTQY)A=GRcXFs|&|` zZ)K&XsJQq?KPg!cJkPCjOC-&h{VBG6oeEz?V)(d6RsR9?V3Vyl_)VxLPL7268(dlm z2FI12S<UjafB-Te0$JBm%FC<SGH3-V{!`38pS^<P$};!ke`uq`q|-4kDMZWM62UW_ zZyEuj=t&2Gr}_By2Ec)x#tex?WkNL5@|2GZw2xjFu;xo0#&xx{T=yqhMd>_&#J<y> zpJ1qX%pse!Eo}M*4CXzz`30(HL?C4`NL_G_%Z5Fj-2FhG_@(m?6#%)Eln^d1Xo-3H z$A*Vh9BoD)&r+7`yKr&;0JUr&MeBnPTeH&|Qxb|YR7XmKGHj0}4<VC?9!?%?eh3ub zXmM<(yd9`r-rUVmd25NoksxxP>Z@g2>nIdDjQw9Bg)woWVv%({tRp)8f%f);zD_98 z5AO98V&7BKCktv?jjom0GKc~GmM`UiZlnLg{ug8rwk|$OK&N}J04cxI@*`>i$6@e} zu=wks;xF3l`Jm9wPngJ34@Er0*hFcWg_Btir|VP7KuO7lQ>U-Udi<0r<Osjn?3ta3 zki)0xN+5ij6Xp=KJeBEm378`2dU(PLk~NL~tTdS}0_2;&xrSY8ZKTZdx2O^4q;?On z-i1cjyl*;U|9TgE^{a7#;G!V^la_38`3$#WvP&kHwjC|Vs%QxEX67djHM%p4N7J2B z;w{z>)Ftj^7w;D9rx8SP@{H>gDGN1JDpmjC1yB^F^Os!yBtl~D0z|o`FJ19{L<F6$ zs5k>(n&8KZM^wS>3;nrc_&R3NR1+U$^V5dq`&mUUUC3yLY3O}h?hd=2gF+}!*9b*D z_J<Vf=$h0O$f});%H%4AeKD82Q7kbbIVkRev9A}yB$5l9(JHF;7ycZvG8D<mrSDoT z|7b_41w<o+HX4Z#HHOK16_#A^(z!T^in1%B@$noqCGrsR8RXYc^h-k{pe3(Y;y$i6 zH!2w7LHx7)ShHn=hRNiW<NcQw%il$+hD##P<uPR^e~e}V#}@u42y#N1=ke?_QA-DM z$_iUjwmtT=h)6R$ZD!p|r_Mb*+*U1sna74YGqaf6f}f({2MzSl@GEOp(@XDYQ8*F> z)0ZP$1?TPbPfSY}M>fX&?ecM)G@P8en;u+sVa5YJt*zK_g6|K-$h_mk;PiGT3en;T z*@TOqblS*;cz6WB4-!&&wUv2yGqoMav&npRZHWSBi3zJ)o52-jaW1FZXfSCtwIbh+ zZ`>Aw4GqG@IXQV&)ZN`8C%Zy&+HT*&hR$<xZs4I`an!M4APGN2IsEuQLOhgSug2N? z9(X_i&tlWV0$Yl6695>xqGHCwV}e<mTQ&6j*mUZ6gZjh3+<6O<PVw6*ZqMsK-4WF0 z<}QG(F!;#}j>JKCZVx2sTq|m+kPp6a*}mRs9DHuTlFq_ID=N6)p?!VMEk3CE1@_;a z5mLA@7ZAF>)+w|?L0CdYR&r!C+}@ovgh~6OTL{>jFiFo;Sz!66gObq-R6|iwrXgg? z&DV$Zogp&+Mrm@FU}0eR-&_qO@Y;aN{?sA}wv~=b2n-ulZH4?4Pnu1&b#>*T-Mzgy z@(Z@LE-o(AlHY0^w(u#J|FnEY!GF&1>=_ZCZAM^VV1EAA&U7_0Wo>OOxyNzX=g)1E zr3Sq{Jq`adL-cib+W-dM*4Ea>rn|d4l}AQNMMFcQ+Hy9SOsHq+;9<Rh{d%Z$Kps2C z79h)Ycw4FA*1THqd;KY%CwsQmPUM-{o!j11`;9~0V%^!(T_KN)Lmj)Lje1AYVk_#F zmU-EP^(xbm24Rmmg1K$Lz)+#|c~dAfSsy?&mA2kJhE%3}=LMIQfs@mIIO7#ORJYL; zIU6Y5r<-HF6BB1YJ^>a3(0rFyS4a;ZzLbzK`tX5{miFT63I_%pCe&0^MQ>{tH#TAc zo12s*4`t`(K0Q0TTMT3cDtLwa#UY>FD)dyk)18lx4={wY4K7E$F|5dxMMXtajAk^9 zjN5<+Iz2@ITL2}knW?GeEYamsNX*QvI7oEzv5UI%0ifsrX?+V2HvoblQv%%6bmy6! zBvgNSS%mYPp@htqzP$HwktlYmCJn5dlR!(hD8pQr1UFVWj4sNY{r&s*K=e;cNSG<s z6LFPlWwcKU2zX*_yjE^J;Ca|hFLHNn2kO~pOiUXhHoOVdR`b9n5FH)8xxW4-QZrOn zQ*$a+9LbeAAT2Nd-aLa$38WQ9Mn=H9O9hjZR#(S-PkW9lk-It&Cm|_0+`G8ftsKS? zO;^h1Oot&lma8JtDs7^RNjI@}1k{KFU>+G9RJ<h1m+r|<_B1mw$pJEqg+=4Xsj{aW zA24Bnfd%fJg2F;_L1*jRYZus;+FBPiwdneKftX&4`KHE@5DcA<C@6v7zM*1Zgn(xm z8JQOO4QO#qjWgg_opxuw1_xJFRz`hCl*`5!;*dNPMYgWuB>nx`XkoA`;Wbz8TNJ?g zL_|k(J8X_>S6fa^PD0-}?#?_VCN?)PI2ut9kxSq`I9TcgULN&gU9grG{$;i3nVr4) z8AJ?RBXCS{@wU-X6%P*&K=nt^E8O0k&5MbN*@xBv>YLv(ohkhL_wQk0SR^EX6qlY4 z$5&8dIU(v+&b5o8E0u=+b4*x7ucH>${~i<K6LBRKAg%$2g*8=3C(f%d<bz*5rc!T| z(<u}(<y8>AX12DYK=3l-ZlKh;t!$5Th&p6jF)_-Qah(}IN`1@L72@y_ylCxskddx? z1BFm8#IT9N1zkTZ=vBw?$u>X~`M&G}^F&=Ujb@`_WPDlRASOS$a&Zkg2#;nhcoGML ze+Io*f4V3n3BHn(bFj89si@#&V+%E(B9!Dx?#6lE%eWY0KKf<4^3XRB3F2L6KJGCM zoHM|70=Q>DLBonN5j!F}N!|J7LzOGM!vrCCKJNq}W247PBmx5UQ-H-+Q2~R(kfNWW zzv+N<zvDc`^x)6$1`jF3FE1|xf#%9d>@mJDqby}+|Hryst!Z=%Sx%A4VdDivL&mRq z^I@`+>>pL-P1`>pdoz$z2|_9<B4p~j)^v1QA7Q5ABcX$BhEujG^H+#)>Y%XhV2kXT zVX=7gj_vX9%O<(!-adu&_(Cy6ccgEm)gGAV4jJekrr%FkDd1*{eC)yR9T!eTXU-|I zD2V5!pqu``+#btn2_Nor_=>)ZRnZ&kY}Aaw8MlI<RjK5Z@i$<)0!s)AN|a9Mo&KIQ z*@HR<d!$X2Mn1>`PN(g{PuJ@>qq7;WA{rVS`ACAxP!5yrzBY=HDRqY}WVMx}0AHEZ z`viYz2m&V;SCkIi3z{rbql1!SvOcJahK?jH7x1-|DnCUWase3j6vg#H1i~y@8#4Zf zp-gJ?MXFBu>cv?EYK2;vGvT5*V5#cUFw*^e&%(?A@o8|nz1-Rp_HYMQw4>EujObEZ zE4>ptzk}wQy*hwka#Hw4S-IZjC>0s!t(;svr@5XF9^m(y&*mn}$^w8CINaO2((0Q) zDGmd(L9a<rP4(n7kTu-%eBr%|%UFsS{&W@d?pVHdpgHpE&*(Z}g-MIa+f5i&PCZ7? zx&AQws?=yj9zmsYBne?4E?+Bxs>^LHkkzruvQS4?_u^_#0TlU;CuYLQ19$svb7%#& zdvk3iCGf8}B4)cy51H`sg`YF!uMe*@=H;2ey8O_t04M3R^ZQe0{?&T=Ba7eb1)3ay zn>Gf-G@l(e$eESLV-GT6gb01<Wp)!pL3k=v%K;f?SW(s(j5P|6nBm5Uw>C$sjg4*! z9#=arVA3`R$?(wLm?Lm{>IIXg`uhiQTy9WG0@mB#&36f)ly%xTd}VL{jI#X~GaM5e zyKDsr`=+L~z#n6Ad3>_65(OAFE>4one1R7f6n>a*v@Xqn9{>0;`Pt5v(}`KregLrQ z0n~hY`T-arKta`fb6R5{B`zfu=&*HKWn~o_TWi#xz^<H)zGN#7ionPT!9gmpRw1H5 z_Q1L~hhZRp)vGWW{(^N1w$srO0;L3-yu7UAFm{FeUw!;-?~S>*RQ{p;3qtd4o%zw~ zA7Y*-_loY4pb!!$yzA{Zf}^89<f|q8`J*78+$5<q>WdOTQFyiOPtJn<KX`b`hWCJk zOw?f&5=zX=+XN1X4+FQnptAn)X%(Om&AL(lwhQB&lf7m=hxI`<Q?!T7-cW^;1qcEN zI*!bg@asJ<0<YVPaX@Zg0#yYby0|EE;eNZ)>$Em-c6n|f1vtW;-y1lv)n7-yE4O%K z;)Oqa8>yaWp@%R3WzfZ;wf2k?16J+7E6b57y{-?AS9${~%_Hpo5=wb_dBE6tcD098 z@33WkX^+|{)dI%2_A}CSgR82`nPvCJ$V8SLF`%;7)=dtsFJ#2Uh4}5{<Sgv%H#1&| zM-FlS9p6aAqv1H1k#=tFN-DX<OJgQ<YM13-@zp?<DJ?zPn^$P`5*ZsBN~<{^y15ET z7Gmn{y}Id%st4}_rF*h!M_NK+>|jX=aJ1+oyiy`Cr=wLNdU~)Lb?0mCyw(SA;h{i# znwtwtO^pCUc-|3cE7FVwh<HKW*^{kD0*)j*;}(m1%_3L@8h@#L4=E`W;D<;Q2=Tbg zHMZQnaojm}JL7h=uYjU{o2@@P28=J@c?8|ms@`|l(^lhh;!p|df4;uPUdXOz=wTLV z&KUamEDeY}k&Nc3Xi^Tlf}TTEyTI4f!!jg-Ce0J$+kwhoYNgkFNmSM3u%fFtCaM&v ze;7bzTq-?7AfW`X7|<#`E{~_Gt@J#$es*+xecpxPBUt8Qrv-E`(I4N*gub~Rv%O$v zf5viqbhuI<7e@pS1q>7kDYJUPcT0;|yS=#)AkTnm9Do!qp}r6LDL`GZU3u-PqeCk6 zB0D?Vz+l8~bF>opAUqG!+yPa>qBDnrc9O*Jumjfj(xMOG3{cd7sVW8NJ%2<|08<zj z_d6x!Acl3`!I~|d;Ta=iUssNYsAylA(R5*fEUWI!aFqq|3O6o9nf;Fx`k1uk?q?u* zfVJ7f0;PmMeEf5+P8FA&yv%+h5(o|I#dGP~*f&yO>4VAwn37!XZkMu(jr3ClEPh*G zM7@@P`a9)O)6=g3pD3^c>(yAl^Kbz+VhQ|ZBr!5%jO5F+c@ZGmWZlKb)g7Mh%mP<~ zx%mJof5mD?VEVxUN@XQqf4>?nEz*1gUt&bL9blmBR|TA!gk0_0O5WXET%*j^IZ(hW z%sOgaI5xT?$g4hTcCG29D6~vYUf|);2L_(Gxz!^VudjaST+>?zBL3-)jG6g>!`|G* zU@8~Af+P;i^>o|y(!OtNf<9SDpus?jkgQ8tfbi!7?sTLGU?<^XJD;3(i~3W(zdm%d z&_*EQelDx0r!F|~KP#MO&s$J(fZzmtdw|pH>1E>LnmyYSQJz1IUjYlk)bwe0H&#<q z8v$!lLUh$n;3{fBJ#PBYcb;c4t#q{38AM8)n!SdFTcNq14WviMbvACs4MNI{rm+p7 zCm|Kc1h=O<^B*mYiHU;ac=kk!71t+<&oX74%F1kkgIhei!C6Vz^P0fbl??Ef1If*= zpjdimWBENESMoTp@CdKXS+}^VQ#R`K10|)aOcScHQBi05{1R41#_pjZ8(@RJxjM`4 zoNn(#Az*pSYciOE@Zj;=dPh0uS)ko7F1`nTCRkT6sethHej9LqXlrY;YFB;3TwGa^ zfV!OTr|PHX<b1&dj&q>Ixn7-GwCDm{b8qfeQgL>!0q_%mAzVC=d@0;OT!vCsRgt%M zJj%>8ii{M@RQ?$hcz-^5svI<{u|jT3NaXg7wno$E2cPK`f(r^)g2;r{fmTqg$2vM@ z;jl5B2&{l_a1V}2H15Z@xcL?+#A<3;$6it)WXmfnRJxBZi;If5fm%0T$K^4@yVljE zfP)hPt>VxZo&N%z9~?a2+>E<av#PNHSL^baUD)M?mQ;3>HQE2kTv^lv%7_16O1|L? zd-A4leKwp{1{((lP~JcZ^o=9x<~aaNSW;9}B`9LNy&(&W!1{W5anaG)325cndZ4qN z0>G9e-~=?;Ab9BUu|4cdX=$ncCjdk_IKZWupVup41j4OV(}f@A3zx&?dwe%rry3BT zzxc@e8$f9Syt>R{B2xf@^e&hT4O9U4&Tz}GnOXy6%5r0?4D$DM0Kgt!pUo#XxL;_o zmCEuC0OP4mEhQK(Ay?U(!`?kI5f)&!AN*a}EfswI0#M6EI$*^PINFq2PRQSNM-X6F z&p!vQE5?9l&)tDn41v;ct>1u)OYq>uV65@4%bJuwA;7EF;-fEcwCVy}<;=PbC*YbC zSvefo$=2+Hd_C`8uK&ZkDLPu{y#dCP!<w4Omr~g#rlx*y=gxK#3i#V*-S5eI!+^4H z-nl5aA7U~xbdV_dY!+WozQiNriTo<*^-`WrXg^il9~5I?goi+p5X)iwWO!?H^I<Qj z3u)ob&d%o-7svz(&I0oC@_;qi`CYkW&3h8{Oh6$1nz+H1mqe^CJDa{y-`F@iHI=eq zD@z<qNSQDD-WGFS%@IvayJ-?L@7f*%@5wLVd~`dL2SDSyG?9I+gL!uB)G-in+JE+E zE5na8w9jT5_?QLVH$OdCn{yjTk%>722Q-+gT;JYKezvW73;b!XUUkBy5VU!7M(Qe0 z;mAcC#-4as3s)eR?C-z4JTMP?0gRSZ;k)C=G&Ced`S~Lg6I?IMSeq`%y##6D+fzp) zBiXF*3GBIGz%+own<f!~PTGUnzqD-VvZM70lsOR*w2AM5o3*Zvj?2Nw(NR~dg&c&% z0xKPuG%(46v<j4iDyzGL$<poU3cP6&VfXv`4lHTVuTfrI&C0}-&X5QC4KkKzXJY=% zcYk8h`QObhP*vv^U;*Rdi5Xb?mlxm1VS~5{j){i0VO6*2$n4z*xJ!JqS*<ItJMH)| z8}pvXvDnz=+dVIH5YJDHc1Lq(4_8L2K3*iZe@z4Cq_bV2ifRmLg#kflwl^{|%HrY% zqYRxO{nyuM2=Hk}#i$2AU7~4grKfqmIXit`K9H84u1vs^fJwGXt?MVrV|!wDdwFsN zV*g+so|L--PDDQto5jRz(E5Vpq%|Vr-Ml7CB!KWjAip;`G&ov;Lb@@`Xg=OIJT$aB z(#b>h-rv{vYgU$|2e9_8{#U1OxYzvA03^t<oVhvE+|p9mWRSwx*jP_*9@t1=()#)- z0A?l$?PaYdfaN=wB1X((3B<4hD*>RVfd&jR;P?ki98ek?a<Q?oLoHN|C2nVyurKN9 z>7aq+>(^H(GAytU6Ev0e4NAC~<INw%l`Me=@3<_laWO~9%&Cg=5j9f>_AseHG`WrH zE{u4W%t+%9;1s~jJex*GQ{D=MnzDr}*JEVK^5c9Zes{8?b=vR>x*W7J;Rb<B<>ls< zmX$qYV8~(p-0pugh!Lpo5SpE(*KG1dQ56Z4eU2|Uwu;6lu%iCcZ-1;hh%5u(J;Zy* z+6AJ>7(pl318lCG@qZ!$L##bCgu;;qZ9#s7gfattZ>=7hf@BE+)O3bF6xiNX@b7<x zLqpiD88XwG6dvbWbtA+*0_7wpCO#x*JK?jW0dEv@EfwXk(Sw<yM<(1aok8n?pJmkw z*s}i~JH?}y&V8RBJV^;2B<2Yi6HC$xnWToPJ~;;64k;|w_4p~dfS-eS6M-}`NCW&; z1uG)P=#Ug2P|1QOtGE_|y)69`4i|_VeD0xW!lm%jf+kud6MSID3IZCLGjG$d(p0Uv zvx2TN#lH>#3I+nAs0?(BwlhK#!oPp%ZKEB+S?2%<ReS$Rl#8;}0E5IFQi(}GK!A-6 z+mE$)0S-PCA_Rqa=OLq^sXAhfqWTCzl<w;d!?cEmgx;0am6fmA-w4?k0s4W5en!Gz z(I`pD$uU{(3S$9Iw4x%w5YfSxS64lchosQa(G5C-nVFc54iBl|g6`*R^z{Cii_6Q) zi;FN;92i3YsEa}S&opq5xd<ldJNcuNV!wKacRE*uS7kQ#jL+ifg3s?3N&`v~8%Qh= zWfyI0FD@>cnwlD2Pp}mZPELfZ|9BS`?#wm1O%!SqU}9q8;20Pit7~ZR0-U<N{n5+o z4!9D6fU%I0l2SlG08}k$3<d^EfWN_k0TRw0xDf#f2b^fWbLoMBy}*SC)M6fB!1ug9 z2SFG({Km(|RB0q)gVL;D)F&+rGXz`WRD9!&vcQRACd{FTPG){~%RzZRzkSY~obgy| zskjDTGvngmlmq)8I8)H?G#2wlNoiqw+x>KVN<sKyc`ez?awJR6$cXla9^L=wbVnPs zOAQadAtfbk`LKbJ2#~F+DqiwHaY4cMVCsWLd<i9xdw&1^4G#r6cw}Vc{{B9-RPf~V zw2Z70<BJVh8q0^&E{9YmmhCQ(AA*|#PI4YHMUwx?r?IjYD~Q~T0%lE6LP25R_ixv! zG9!);eH8Hj+YY2+U^lZ_d`L+{P2C2Lv_zS|m5GIA4h#=)ID*IpKoo6O36kXDGOU4X zP`Fj4oJ*x#B?U4}>YBiYm`C!AOHeYihgO}2PpDkwFTDngFa48~8pOoJ2a(C1m!^yD zUqi@6#z#i#T~ExUrJ?r*S7#+9rT_mHqcRYSKsG#v^nkgzQ4@44NCzy<hK3K3kOcVo zhsCQF8J|geQo8_IK%s_lQ~LuS-p40+&#@U$eH!rJtSBOj=_+^awb&DdbdHRUe&o&E zAUa>~c9eSZnL@$hONDsboe{IDIg%<91_PmwAYMxqosqVOk*~`R(D_Fn2;n||LGk=n z&o41?6J6?|HDV58)h`tT@QFvv<Ill9k&Tmni+LfWJelNH4zf6TWYp3d`l)fS8hkKJ zfr|8up;YkjXVs+-Hjt{KVQ?hGn)YX5?Fv+%m&mHd2qIC$lH3>U^5Z~U$nkkB{Po*6 zw@EcUMSj5u;G=v^VUrc-O!m{A9PDrfVQ1tm^w8U3P|h2gfbp7gAyN0+?Z>jhrj#f5 zG}%6a8rQlih%U4sn%qlp5Xh-$DALDIP>jvbNOK6@VL<F5U?F>kh(!hgsv8f>qlYd( z+}DXf<N-PmLK|i4XUPRr1c)T;uXe#ea34(cnZbuN?jyr@gqvFX`#?4Q&IcF5hW(dl z6xZCZ1d&q%aBggv_n(J=k8^(6PpP$Tg+QpmIZKKGdG80?`ebHS<~CvuXRM$S{j8)l zOa_7UKIQ>>N^<{#0Jm8=z*hNMevFh#FqBB145Cj1ViesE32=0x4}Am`??E>N(!StW z7y$G?uEr7&H&&uwc7TJ9Zex6yQa0FK{ZR5yE8~At_yVEw7{|c}bf`ig9c|LOf%nB= z(~Y*fdshU#m)EUJc(6PZsLGF%QBm!)<%o-B7TL*~K%j<<B8ep(&o#KjM@QRS*!na! zKBdgCn%}Lg6l~GcYs{Oe-aDRaOa@+>@9_#}2Txr$N5{~J5rJbp@-U!}0r)A@tXcjt zB8A6*gR=rn3`Rz;@0^a;#XyojF(PA2v3d!&GBI&?a0p6!(q&ll>;Z(0tj;yp5E{(} zOkC9Ir??V$_`(&HH{O_Kc59_^L`m`SiS2#=hEuuOHwq@hU!Vz}LY~3stiHwUJl3A% z>uQkfnI)}zt#&D00;f=LEz~ww`2L6AZdD!hu>e4UfuZoW^u4Rcy{itGAvd<Rh@Mue z8&bw10qK>bKPOtThj?j=I%31}nQD5p;`MSfsPmba@_l@KE?)?`9MW)cjR6vTczF27 zj}+P~v&BjZkQ!)cGBmaE3tGK<(JUvbq+zXTbB#eFSI_zR`5hKokrx((PgWXSxx8*J zJ+!Nyn~VjdcVWLkeIRTLfqaV$4}WiDqzd}xM3eq2shs9ppAV?N*HwdqgR7RBC9iRu zQeDB6mjsRgw|)H#>ws8Ir%UY={ZCb1cTaqyzkR*WvN;8>BIsP)-0XPK&<rQ1rhWmj zBrOe4)fv9^BSS+#sl=6F)~P!No^RmDoh~zy0UYj|H`xAdxdjEsM@RM63y74<3vK>C zfBpoY)GW)Wt8?v*W<Jh@H)Vm$*B{RV!evia7Xu%k%W7Y&kE*@Bz2#g3pVzIsaB^{K zDi%4pm9eo@@#GKrcmZn$q&>@tp6$HuE)+HiC1&#KY~u-Ta{=5H{HIS}v2q+5b-hVo z{wCNWDk1IUxR5~eVw1)wXC*N^Z!Cv4(v~r7VJ&SzZpbT>Ve|52YZkb!XKFb@IypEw z`?|VHDl3^;S?8-Pv^6xo>r>-OWM*fF5cA0M`VOptKG{NGZ3l@P^l}2k4H`UJpFTz< zEcx(@2qX!RxC?a~FMFbx>|NUgqAcXJ%dlpOq6ESyg+dXFvLJaOo(}|Xie+LHfU)9r zBbFwC9Fg7H&(rM<@iReShE$FOso{(kzZk4LV49Ar(F`M4)dqctBUh&<z*PgxU4H7> zM>+CLxrjgjKqut~eoHJEcm=r=<fiYM<sU$&<d@~+<73?CBNxb&KqLeB0v`I~hbRUH zMwqIto!#ur43rXdj@E)6XIueQmisPC3XZjb&O|;O0n_2o!9iz6HTA@aE```dPr&0e zG|d04ai=ET^^J_4l{yb3Mfv*DN>|4K7E(q=Psr=`@Nes*jDuFLQn6k$zGf&NT98v% zAGqAr7SpGojkyT0<-aRT?bGzXiE3-dgWd%dQ`3z??dtWH|8(!dpfSC2uU=tkcC@wv zU*u=Z@V^ZxQ^$#U(1$K5n{3~Z4*hUs{XhW&en?3A=9&s=?}F8t^_o0Z`_Nc*BSXmV ze3O#=YcGzE)~v8$bg~*7t5u<xWuU)?nfZ)fL3gk?j2`|4@@Pi?KfD0OyAuN<Y;09| zd0N_T8ls}IE-sl_Sy#Y?y{r<}^4X%!-es?oob}-1I$f$fOP=)gtw(R8bR(Vo+gDH< z8)R%U{dE7C|IVg#oDCQ2?a9RK0^7&w-n_p&R`L12H+PbWu@lWU4WI1I>lunsX*Rz8 zIWyx9Y6j2<08kp}1%P96aFmCK-+c>t{P;0H@KjBe``_*Xx<#!(;~o-SpC1757g$gR z=3Axt1V102$Hhtv=tq;A$=w7F14UC1-$w%}dcFoc!K4xP(Bk&E{Bp8&wz?KG(-2$X zaTOvgY)&S`&*^d5r68=Sq;<J@e$Y!T#c#hmvkN#?SG<G1bSaF>`8!YGqE=L|9s|@8 zi(ZqAQu~NqO2V_W++21rQ9whL$MwMvU}NGp>QOupv~P@NjyJOu25g1y^@pvYbWKW1 z9I0S?UUyy&Dk?@n_dMyx{}3u5_JM9yK)GWxkzry2?$_r1BA{)X2WNJelB7ctfwQkn zabD}!&dx@kkLtSbkNbz{!bhjQIXp(fnBGhRz$`&2Z$PaFI&6qEB$kWirgAq$Lnoku zXDPToRu1<A&^ue-4G#saVvc}R53RA7rUGJ3FKct}0PDs`XN){>$i`-8i`P54`ePV_ zdnE7)--_5_16B#Q8;~R^fF2JaxiI>P8lqDV`rMOt7T?bnB{p4ezAy^7ie|pMI-QOH zoFum1dxZ%Znkt;#O2L0?eZ}_N?ds^D<L8$Eo_v~i0lHO4n92b#0$DX_vgwQJ$@G$y zmLmD8TiIW2qRj6bA&TizN4_wI@s_FVZ%yF&<(?oqz<a-<#>P4jC@fo7Kq<4bbn^1e z-zckpFkZvMy#^L<DL`)LD*G4e#9yCU>`a!*x8{E_=H}j={O`<zT45aJL>(%L$?feG zY4yj8`PE-7*c^i=X+oZFUpGH?7)VM^624iZwFUxzdwZXqUHNp=O{YO;Sx5Ds|Kt}& zL<Zcxs?YTFaDoH`?CSpLMqdi%)=$Tz(?7eMC%i@&SkC1a#^KUpHLB)^bj`(amD}4_ zTpb?-eVGJ>Fqk)D&{$!Q<<Cf7atY^QMV4EB9Ofn_--uSuH^<Bg2+RT554vdC4H7n| zqCP6(a)EgzoV>TQGcH>7pKVLP_ubg=*2ib@p@G}~RPqA~rc2XPb4_C`XZh_nM}xuo za|VfXX6CBRkJ<Hjy|!yJ$TImULjVj;Puf6VOxff54tp*FDMi{@A!$@)r_dI>vN<$F zeraeAE?N5QcLq>e7s?VVC1lfQU=1Z?x|Fgt;=y&nQviT+o?X^jymWr!a#sG~-p>Jn zs8U|fRF(Cno!+;cfArUeL9iJQxS)sATqwfZPkmYhAG!qna08xK?@B@rG~b`W=9l%; zm7psm_4s~AIWJaE)cf+WO7MLIPfkJWKTyPxkdS~N|KI^|I3tNNm;N~oBFP<<4v}5t zh{{uvq0j=zbF;9dgpHm3;CN%?EepYY8@_0pTCuJiYff+d18u}|^Rf7UOu9QeJ94tJ zkvjMQj1~a;Aiy^L&S8t2omuXfXB=LR%nJRRX0X3{<tdnM<jcjQODpN;svBa1eaPwQ zc^xcvd_3nzq!2l$HS(t8_Y&USo&uB2zdxCrp8fNuPrDVSZALYn|5$}^YpEhA?(cx& zO~?Mi?B3T+DnR78F`RVmR1-{=>2{K53(R$``M_oyU%2y8yiE!8H!zoRxt`S88Gx3W z(^FlEW@1+0KT;S-2T2pOXJW$sBl@Osu>KSK7q5Qx^i-C7AIb{y2X0(EJk?gFo^l}- zQc#6MASU+=2|hkYYa5P-yC1hr6lg;b_V}id^lf~|l$QN-;SLgLB>;pxEv>@cbvY*s z3p3@rp<q(bmE`iDo*@3Ck$<`|=K<HV{heKYt=}Ut=IlOIw5uD^gRYo|ALtR-$UaOU za$+TWfd;6xWn4bqJ~UL4GpqOF$-sX5@5{b}qc2e)8uNwQ=}({t0#qk1-bhX5I68J9 z4x<Y&21Oz;7DXlNQI*a6_=q&!IMycW6t}iEGUrmXQFNJq*+-`N9s))dvG0nmuYst0 z&=-Xd+=5`d@crfGWFDSBW|{GTf<kgrjuqZN-SQHFbfo+=E=7L(;-x^w0h$@OF$)QQ zWU1%P9O!qkzx><n=$}q&)7<FlFkOEJdP0Bo#p?O`%EfYR8MFlG)HO_$z0PO<3m1f< zT=PlhBIsqwhBiYfDgQ><pPl}MgcU<0BL|1J$WH>|IZdTK@@Z(E$FUpSl)MZ48SG$b ze+An2Oh>XRKeqvKJZLOmgcjcLznnKftbI5Iq^iJer~hpm;&iK4K7rGj=UV@4N{rEI zV9>X_11Y1RWp>-u=Su_limbzlp}axeB0<(-Q<={G|1?+_ZSX6o<}1mhaJ5%9$SsS} zC4OxW(x)lTRayH8rU%3X7hE&&%J#F~m>lcAAX_M6g0OW-`Ow~<>3_Lt58DAEWD@<z zP}|4FD#}1NNDHwrb^N}B^N##}_S1QFEXViz*%Le?0<ly;PJwU!bDy8-1zl!P-~m@Z z#2b}uFX-TYb)x}p3_J+$UGF8N5=!^CrSLBJL-6;m8-!>BV5<;s!;fxjQ0j&&O%8A* zKODd7peT|z&?}%BK^Fx<JqNz84_y$&zhCVTh&XgU4iu;$<V;mUegHen{XhS1AH4p( zBS%%+wic)a8({a}q%r6ekRn9+`(0@Y!7jiJnbAf+%m9sl|Lh9#?LTf-#Gol;Bdhdc z8kdc%A77F2?;s->2396B#;UwEq2BWxM-?MuK<LNUha~)jym;yL|26g%P*rwYyW2oP zM39vJwi42cq@;*QgMf5OgM>7a%BD*irA4{~L^=edOX-&G?!FWK&N=@%=f8Jc2LlJ} z&AZ>V-nG_Tb3U;W2M+Q>ZQt$PIBzq;ofYFE{=*J=yI1&rxp}cBfxkVF!bSAZ{XAdA zXX~R!LT`m53^CL3Ax%$Y+;IsS*gC)!{=$@<S5H`XzEmOvF-Ux-l{cN^E0%nI|2cjR zUFXO9Tph^_%xgpEb|YVP7oT4ijPP|^y+!uZS5{f-K0Gb>!$<hUtuLss1zE4iti5Qq z*M|i<`PUC|*i!E5v9pVjCAdBMN~(Z{Ka;wAJDqHIgz6%KTFzPNyc^)(W=c3q7*q+k zZ#jDqKmv;oPpVAAeYhxb7p0Z<DOzk{(R^pIce^(8c~-a@F2NhTD~M^~QW+0zD(qX3 zE_B~dkQ3QibQ)p$Z_f(^;;ygU?TVS!v`Pj9^dY{?tf7BB)>lh~;E(CE(iIL{vt~<u zL!lfqee0=rTXIrovw?C@Xh%!#X9Y~HPf_<n`*G*D-M>w9CP{sBQgD8Pvd?LX6CWWL z*>umpocx+~!~j$^QAdw8$1eHOJ$!fv9SQjnMZ6L0(6}5PAnRL~tc9BDygPvp9Y{tz zJBd5JSoRBW#ra;k7A8i<H#n@XCR@O&VrAgfHn93hdASCL4!c42fF}e}I_K>lLZFS& zZ4?QQc<y5{-IbIOOwm5Mv3U6juFM;+31eyZLs`GuX?*ngqY4uef4$|^=2980WTJnK zPeLMKEcYoX>6~Clm5MGI&kNEYCbeP;8q}x1RSnnCePHw2)D|Eker;<nm{1S!r=&s< zF;&>FO#pPutT|$}I?_+dnTi9f!Q?x)oq_N<3sOhWdOF_$un%CS$KGD%W3juNGp4>! zGX6Qi;1|1_zE7B($0a_vQ_VU?Afh-UYZoCH*L3{SxaCTQ!B}Mq%Dlg!o@}7<NZ86s zxZ2faQ38237MXIFlVU9vT&sRDGgWq_20qoFA$>^eoxHQVLVPIG+nc6VL?S&WDbsm; zdV~o?3~R0ruU;90TaCtONJ`365M2FW5QnVm6NuudXI?ruOorT9T3Km_#$WRtF1r1R z2l->+LgD?{>q>f0!Uv3S?E?pkY6y=BC9C7^*G&OpoRp-_!V)DTu*1gIgC}#dBQFX0 zPz2n^U=@{<Q&d&kb6)?I2Z_(M(@)@8z<6|!!*F*$*R=nJr>9%}_Y`o&v9=D&&Q^!z z6J&fV!=$1k=R}?1ujEzMbFMZvi4X^I<r6Qom4se!x605~4hZ3uy_ga(?K@2ys4+yB z?@PqaUIby1$DYDJFwR1E>y3k-HkFJ6A{gg|IseIY^L(0&77A6r<-iM`GxYQ|qm`65 zNsg1Pb5#myL8|sgWTjrQzci5F0bVHBH%`59Ui_x>vkFuT>)rA7eF`cCw5x8D^G1*Q zC3t75cWOoBUnX{6GSF=!&W-lFTUkm)h=8T$%}<Yt)NE>sX^eFTTSz6a((X!HV?qH^ zvFq<n6nvtPltUGH0guJKa{p&XGY)oAIKhP2ND(e(a%wmi8aARQj$YVd=i39v8WSc* zStz;<Nbcm3!>Wn1cmW}}mA;9HN4({Acl)bHmx;_AVMJKuvyKiMikr1bu-v+%+xo7f z13_N&6EmeYKn7Ioe!pKvTpzh8D4#Bd+FLbj{}TlgktRDOyZ;O)J91$ccrngj8jzMM zw?N1u*aOzbMsuG4h;h5}wG<3BKP%JG*RUZT7tcrIPZ&?CQ-1J3pp4Qt;J9zV;YD56 zb+TKxfEnQD0(aV-p@Vvgvgp6vbSa{*25$pkPCrIpVMx&lh|Y*_PB_6N@h6fA^5wq! zT?EW(7@1t2%dS6FRAfeiEZY8;p#POkaAt5lDRTHFXibNSg$Q`u{N_4m)~`_iH4W({ ze#cEcpt-52sDP`PT-zISW>};b=|Rd076YaF;Q4X3GU}+VuyfkDvG(ZRBT0$YBH72* zIQV%5KI!Sy4!yNMM|@?4<s>B4KdW2P31dlI_9ToN$sMCAYC}U3b;;c)xXFaY-cH?) z)<1E4%2l0Odi|Hrr3cmye3ZwpxU3^PxgU^?ZJoqZKSjT9W3+b#D}JzApnr5!1wH@e zU}W*wp;22jCzyskK<Y^9=?RIqtU@)%Wq;yxR~H3XZc%c)?C8r8=*>~HKG+naplIJ+ zZU35|zp=NsKk2_#QK<n0XF>w@`Mw+;PXoZj^WMFSrgY`%$%*NQN({Wdxp|6y?L(29 zo4KWBO<F>g>6nI2FICoY!Hr{V73l}|7*E2eszYf%541?oh`9nS+n<nWsLa;X+4(q3 zhTnMA&reW-f>9diwoy@0#l?F*H}8bshgAK%j0Du%n3!P{1|A9r%tC~GfIdtQOlBJv zYFb(+CnrFKs(=V8Zkj+P23QFIFAD?t$!TeG40#n+D?{vI;UC^RDxm);d3Dwjp(QRV zDkggC4r-uq&L_t#g*N^(Df?m(G$)L<Gv!ko8ArCEp59xW$vAud{GyyMru}K>_7X%| z&a|kewl+IkGd3%|yxuH(J^i=}yOq?as0Pc1ckgbIk{U1dl@1P8^POx%y1O&(IEQDk zHXFm?@<sbwfAyK`AeGu>#J$k+@}qai$Gp6}v9Ymj8WKN@nmxV~KeU+#k|q!mi*)L| zJTZx`A*Exv@*Or_0L>T-4|;Poj*^4<Rl)XwN(_sHO;&SrdmGrw&oxOvZ3bLnC=h}m zI3zkcoe|J*dqAn>QzhZq$%u-=JzqyhcQ}><%(yT4>9CA|OEGXnpy%n$j+}PM$azba zwig~l3GpQ1>{A@~RstCAfaintcRw@eSdGeuJH_LDJ$YKD(7ZBF2S?*FyPb5^BG5p- zND=2?soHl=B0f7@>hr>Kay@g=7%gLui!}j%zNL;hCsGc*PoI{9d{vOB&-oLWq=@h= zlitkAy)mcR+>cZ!Ugg-|Fq#`uIEq;#{60TFKg>%RzMb&6IE^H8_8DqqeYQ$rcobkT zoWQO=#}+f>sJXb5-)94R8n`!WT}d8*0Pv1b*%IR8FAv)@vKDC7dD+_Bf%=;5bW?^e z-qmYJXdF}PW<1~1#U4k<Y-Pq@XAKvbauyA}1$v#^k=bldhSusJo>uv;yow5KUG3|k zA;oLgHWT=qQp(E<8Czk*+b(^@xy^Ud1zb1|vzAz1ZZI9Oz{kgbIm`hyQ3(zXV081E zg$8Cu&u5{v^ngWU_!~O;)aYW|?t<f$$zcIT@JU>XWKu)nWMpKtL<1S9v7rI*O~Gcv zGPmXeMg|t&>f{)pI@8hl`udV`yo_cy?E?)!1RaA?ZuSo&R#sLN&c?<D{N#N65YVix zeyw8FuQq*@r50M!!2dql1(qn>1UUga*qY21_4Cj7wKLG0$ifr3bNlf6t$QcQ!HV_Y z18uA_UDkj39_>-yRZ;~!3msWep?=*k8#?#wRKp7^rqIsp^`l2`qM{@K^YF%YOEK*9 z#lWCbM_mx4rKOcAogN>nGCkT&c0JkI!=*W0E|dzLCL-KDBR}`<OL$;dbn3og>Rgd| zrqc14p<gQXczZ!N=|U@R^s<d^hq~8>_~{oteSNS3F)mWIuvpvQcTkB0d>_Wl*@$&# zX-UcQNa<FDk|r^yd8|nCAP5(LA=H<r<pz8!3_O~<EP%Q)-M<e7zzyg|;IM$|<hi2# zubMYt`GSEo=}FIrQ3hYc&-6YRbB^GOhK_?PVuX(W$*wU0*ZeoDdCEL6m}2{oNX|0} zgM-h0)x_zDw(5i<wS<_*&UtTtqXP8q>Lu&rEfFi>j{4kEoZ4+e67eO|y3%y?{Zcfo zU`uCMTQ?WSQ~Xu4Qnex6L@3gZk!jOM7#6W0nBxE%yMb#Z8Ybag%0bW-Q*v4UMNK^e z+rts4M3%goLg6$9k%I-2oF=Z&yyi7*Y>>)#0rdgqE-48K{i*o@_$HPE0}6fzknwgG z%KO@r$-yF7ash!o3cev4nyO;QttuY-RdQ~tmPSLAhevgm>H6-<)o^2A+OF4Odq*<* zf|<<j-uT&F)cmMZ0Zwhd{U>wng`aCNY{bf?n-aE*+j1R<jwY_f$xVaa%nNVcgh6lQ zs7;lxxdPs`$M2E~Lp<DerbQ22-(DZgaD4q5VbWt+@p?xjPjiju;T0pcM?fZJERa4S zd@8ot+T1MPAQ@Z2#kGG&CGyT~Lav8bdUB42K0X;oM_!LIwncYL$5G5xUjP<aOpMxe z)^YIFL~$(Az7YKbsD0lcW<ByGT3i{98pu*Qs&Yx=cSs}<a^C6fn{6kxTN#>-P;#z1 zG>O4^u`*<Ytk2cJw7EpeX%Ij$%ERL%rl(ik99E4Zg!1lO8tkTJ-<euo9tSC8s+!Wu zkPJ0VkV-knN!xn~eT`Bap@l44JBsxVo;)5dh4<{Hj?&U&-#nW6UVm8`7f{V&x|=Kg zn21Fa^V;I!(VEiaWFHMxfS9-+fF6WGx0ts!inC&0O`y@EIL924LH!Nn)hAW-t*jar zh@QFa1<X*Y-rSR(iMr~B#$X5-J)bAsi62D93Am*^@-U2K8n!y#Cnk-N7D9(XXFk%z zsV*ewKIca;Ty<oghch`=c_idB)!6BLFe7YlKQ}Tmb0|+w6bmkZC%Y@<`l_X@-|}@O z^i?{Vo0qx`TaMPS$EpQ5X=(F}yC1b=&2?zd=StT-c6l**i-$)o=j&g$KguX5$US<) z``X3=iJD+daI4y#vQQnA=;zyh8_BDH9zp-SponRKAux2SC5J*8f0M6=mWj!<I~9P5 zxht0)V9gzD&jC8KDVQh`;1zx}Xr~2Y#X<MEW351;*>Ht!W8~@a)|{%@Bk>k<eNByb z58X~%(w`-4udZ+!b_`3!@TUdX*yUC`AM{l^1sk(HN^m>PF%K+zD*StJfw(pA2N&9v zLz5!3D3-I{^k*8Bl>HSIumVcP@w<qg9CqI7JE+IA6Y`;!s0t3$NU0Wqwjt{SNe}vS zM@PzRSBEXCADZ@AowtwDZK(w<nElfAcUy>bku>RKr@i>>ULaMj=i=Pv=H9ILEu^5( zS?XU?Ei}l49RMV1yn7>sFqKXEmK`~Zfn^H58$~;%bMa;)C1Z(?9@t)jjkv>FrDt9> zf2k__Rb)MAimNPj>LKPmv=6ehHU)vx+-9D-+l!gRoL)lVr``j@O6Eg0vI+{J{{He1 zC%tSRa`)c2zD>@}%1le!X;}%Tny|I;OPgEgwpNsh*^R`hwR}%AK2VTYp6~x_Q8n7L zS%67|xhuNaraPT$U8RRWr_bMmg3+6L9IIwO%QVLyO-tiQ#-3?z`rG0{Jkl)mtmJTR zPGun}O^>EDE}{jeACcZ9Fj|D-P4~WUW+C#mGoyqU8zK7$XETu^m2b~I_A27D3B+K& z!q~B^>=WcL75iRgpv&@$0>7v?ks5p7jfxl1*ZMjyqhPFsi4uv8^A@F)zp?rljd9l; zf!%INsOH$5eLDts)BVL+quTfJdkDmn+tU}m$J<+^jI&6HSfROJdyC0}P_WtVy(k-E zGm3-6Mz~)~D-k&<DJhy!LvQ)>i@DturYAwcKFYEk5j8!hj@iUW?0dvBG5M=U8}+{a z_A3v`2qN?vLbb2Qn_pxPp{h<#r=m=_si`^PedFXds;TASVD6|W{HChvPGNyGec0fz z{c{=$0oM=_JT{XaXQZ(9FDhjHU}0375<kT_bc(veg>c{Gr!olwXK8tPYH~jtmY2Jk znX%e6Zo2!`Ha6P!Wp~*wxuRpnguQ={LqY;}e?MWXn=19b&Z~W}nB|$<WR(lg6Or94 z4Y0XQs490okrdlB?fUh8GdnWtTgwe3wgZ>gW(?OWZB^AsMYYck8v~I{TCyVaQR9nO zw!b&Jjb<r1!F?rKI3FMG@Bf(5U~~7&l!$V}KwiAadlfsL4#o9)`%6MXInGCUT3Cst z2|f&rjt(|7q=J*y@sfh_g3EPAu9%-1n7p@PvhX=gHU=(H5^^*~F92+jFBL=Ni38hs z;0)#mWYy_TEUKnUnk+s-*26Bpw1f)-sjeRTvl|-&3E#j<^>bG}2Zv`E!tG|}BMA!a zaG~)4Tfd`2r`fg?!-O0p%3*hq#L<zcHAx5|`*Nhj@Nk=2yc4!wbaYnJO$T7hMI|OK zO`)_|0dqMa%Kk@lhFz(GU%`{6O97!DUVnD7w;1KrMWU$0V2jY%dX(I>koCN<v$I;Q zvdql&bd)y1=5C|-1qB-n4Bc%vv);_+)ltv9_s<`S*=nSey@B;5@|Epf0Tge|UC%0F zOk{1HmRY~Ku%NqANVk&%8;>h*XSaM<SlHY`23@20Mw5dbQ_m+LYs@lVh<y9*>aCQn z^G{z@^(!d}X>mX0oO<M;IaV-3D<jh$$s}-tWZSB=7bEd)xubPqsf2sVuUgDs6XF}~ z;6l=I^~Ut<+;}sLv9w?<Vth1&pqNqfC5l}9@c=V3hVS(#FsKHUf28lLoQeXPd{Q3a zy*rpyM83UurZ4EHh=pB&dq6OLI91Aqg6{i#+~+Vu5ZXIN*mdJSZTBA2OkYT!FDWCW z*Us+@{L3Y>+zs3o&ar@5PW={q>!?Hybq&yIC6X+ZA%!U1{er4g5%ild?~&M_h4E;a z$BLFLVU;Pxyns+<5Tek21^QK&)8mAyDsu=09Uc5;t5yhvqAw<kyEkNDupdrM(WmRo zH5=SS)1My5cY(YhwU@A|;HB;zCgpoWqoW7&Ke{cdPc~MD?U!J`yzn!<HR_Dy{_R!Z z(!oA=V<UNVgoKvXLR@;x$j~q*!OedCS8h<y?qPKhL}LEaCC$nqOytE&E>lzeOYsR_ zthK#G2nAnw3c%q0w~3V)u)Sjz5*bPGAG7N|8=GBAxVD>HFbWmk*0ww_@ElwnS5{YJ zW1ML28YvnW)DGlhc6SRH4{myra&F?`Rnu)};k;;#q@<N1VMML38*!Mcf8^#Qb&vQZ zO?^+VU@gy?z-uFWvir7J=xUXG=yX)V8JL#if!^0~_Px^9R<)AISY>&UNFXG`&bk(7 z!mg)rszsXZoo8d;zG)FBx2YAG2)dq<>ko8v?2c8M4Hr`?7wB#EW^raJWRQ`K<cLSK z99G+{t)A55v5Z$70E2Y}#JgF~kM>5()uhTaT;<cGpL><+y!mKDDVnu^EBRu<^qh!g z8y(LRiVzr3e@~OO95pwPB>xP|Rv+y6{+*8RL{`w<@bzI5Y=N#J*+|j?0+Qo+&KRJZ zLBk3jhYuN)r0uLtoe#G?$$4&EL(0k7!CvQ;WxCa5y$W{l=fuC<PPFngh81!?xWGhP z>^0lkTH`<X<Wb5|I{8=D1zBnsG~r-5BAHMn%uOrf<}4=%w2iIg<S5b5@K|1H^pNnu zJ>SWHZ~@rtdeUY1_kV4Xu#+j}d{|yqciUfgRLxP$V)F6Dz!NfJlQtU;vb6c)wk`<# zyVGB96wj29DDS6*Q;l1^eC$A5iH|2Ke93~S8Tz4t`QWpkj6~Av(5ACqmy(tlpY38T zY-oalG(i&Wae7i7Z8>4n|H1FMb3l?}C{@HdeL~x{^rkzw#j+PDa?jaVrDd=XQ*yn7 zaY)Osq?f?&I#asnmoa@7<*py<ud`aF{RrK$rf#&Nw3JuL&{U0%hK3Oa@9y#h>}6Z5 zdl87NRJR$dx#<g$>3>gFZI|uNx+H3B!6#3#i4cG0tG=GfthyZj9Su%ldnQ@NRj96i z<{-CyjN(l+I@EB8biV4y%Wv*=Q7{4>&=6(LGV^CtLQnxy6pLCh@)HShm_%!Hmyrk= zVCwVS!!)RG#P(dT$E>lUb4%DRMIa<S-(8JPSS7v1ja~EjN5IuPH#ZRv7;GwN<{|{_ zxU$hPup3}GnN^tH_z(*S?9BUh0QOr9pWNk1tyr(6YJG(Ejq};Y=PCB_{#L04J9Z14 z78P!{>3x(M(dbdiS;PBmYMCn(o*DcOeG&(nR0J~XqZt+JZ%{K2Xskv{UV#%YKM&8a zr6kzeV*~t!QU>Q%p0x_=&1o?=H#eYf19b*i@XF|~z}*=L7Ep7j+H7Hfw_{$vjxQ9; z=fnXdEhrnng@Bs@Ww5drKu>|=1ggQ+7Y2tK@avbZ1wK0HK8x+rz>Izx@-2$(dQI13 zyfMg*K+HboFa_@QCR{u`q(Q`NMqOZg<cR}<b!<rPKyvNtI|yelfLtF`+c>wOtVT}{ zWg!>1YHM4YIuu55K7#9MQc}_y7>9xV4Gk1fUJl$FC5dQ@0zVe8(LC6iJKO2k67o4` z0g+sKz%V>Zf)JTn`%$*;nluuAtP}IVP#Q{jdq6j-jRsyioU||rlwGh9h|(YY_U#eT zrRC*iJ(aJ&&oSUs&oDm*Ue5B$iY?1%rIQkI6_73<PX+(|La0YgHYAeaA0YpXqGB$_ z!N)NhW6oRcaG1hVVPWUvjYjA@2CD$bd;gTS;6*8<aI#3C>*;}^N~8=VTt+``BT*vB z%}q@>LiLS}dFrKdDg}(Kz;^?8S0NwZE`g$bNl~@bO8@tc6qkUY2uv>r24Jh9M#z0- zhFOEN-Z%on8JnT?L@lN=<CK8GdGo-w)iA+@1MK$*nN)8D9h@Gm0iPJ6h9eyJ0Eai6 zKjiZn-P=%K4^)=}C_})F;R^MnKhy2ad=6jTMr`1eb-L9l0787a)&eRq5I`wd#mM!- z*#tkkyPpP<%&e^BILot=d05F$KlSv$*QP_qniXpD(5D|Yl+5Z?gmrWJKFu?8hPU_8 z9o?VYVN)g=V+p6@*LI-dJ%<Gu1ZzFSaL!RJ1s@O}f1JAhN*VvdhBiG4;A%!R3~U-< z2$MB;p+RmN--#rme+3JiMhm#PiikAqEcHv|AIs75swSULLx`GQc(-)H4-`0FP<o`6 z42y{=H~R4rNZ8=Iu=6ZRhLl1w2vEVAy2_0PQsDkFIg;4h?*aV_)G=J#+<ZT4vGC%l z#HJd9`;FLI`1}gNVXeq?ZfYu-mrap)3QSg4R`_1jqw7b4A|EUpjt&ojO*64RQ77ax zJ*@|2j$If?K(%0IUJVg{OA_9U*Ms1FcuJ9jxu*18fpE<!q!nLidA#}zh8#rM0>)zF z?percBJ>?W<plgW_>KAKVfEQb9%Jjc^HyL13cQrTsX#Vz2JXtq(XqKe9ohmXJg7uM z@r&PS_hp#Ujn-$=X+8})HF8}ly3D*A7aJJ-_bTTN2}DB`vx@TbpJ{^J{@jqdveE^P z5P^(jb8Cy7j0{Lj%fEiTOGwZvGI_2N3FiMF5)$&-%nc1;lvhGaSX2t8VTwM_RKU1# z11v?$jTyXW?rzHKnMTS{TuqCr6^h;!%ya(SjP<ar+Qtr@aS$d;reaQ}fb1vS(=QSZ z%?+~&@|o)D>M&X^UAze2VL&mXENyK^ffT8&t!-kG$tnfI?z~<wJ8O5iy+F=mhYs;g z4#Z+`i!i~#2jX0;^wa!qwrCj+c?5C^6HFV}ZXDj@6cD3N7HsH`wi2mmm@<N8LRvcL zwC2wJQ;(?da5mVpf*wUIG&C&C69>d$U|}C~U)Kv1CampH>AerfY@82&6WtdtP&g!k zser}@1qFfUo=;$)OV6fuU_pU&S^1e;Q(@KA2P!cXZ**J&Rdd11T0OzK=g66_kHsr* zTOk9K)V&!gk<r&WbI~Lvn;lbC!&j$Oe5&sd@mw}}Qu~<ubIYn~jaBqlKJ1y`y&E&Z z9#>kfGC9^vvFbQcwBW6gv;>|$dyDe6dfaqL(mmGDcK<1+;0{a{<9A!xm>Adw--<EA zX*-%PNqTK<A;Jaio$qdYzJrUvaWD6%t>QhWM&CHnZ3JN>TdpFp@222BG!(;)^gXq~ ze~}w}*&96^TZlrIgnB$=IQX7U#(t-tkREr=u@b$Q>rAzrf*|Bbi68$0hPbT4t<AoM z?{t@fdp=CkiaiF05Y;cY20k_KfxY>?_Y<L%woe*l)cqC$lZKzrD5%H5nK=xGofJO{ z^hNy%?>IYKVUu+wR88kFB=6qSC6thl{m><WnfO9Z$A0ClkIz{0)2J5>odpFboa(d9 z*3TSf#~yF}VhvhXhJ&BS(nYTCi&y%KPY7(yk{?cZ?*|0X-?&kFwAXoy%Tmnl>uN$S z^>W~Cm6tjW3RJwxsj$hWy9Cwm`xex)aW=?@^p8zUn`7{A-9?qy>}GE+!3XMqwl?q< z62)b|srRY+OjK*!{-MqO&ice{LzcUD_fPt@me|d+k26g>eqh-Y6)RnlHqiHUa`bd^ zV!&iEKX45V$05$BzNU}i8v4ayvi=<*(`+yqF&-{!iAo`No!5bwoSZ@rI%R?-OP<yZ z@}KQ;BcWJPvLoUC4Z+wyjb?*MQmXwqsA;{OoPtfpU6q&m4$YjrFbAW<*}eb+12~W~ zvtJ!*FC*(0lAn$Ps9cg}*u#)FQeoF_X|eQ)it2i>Z^q=WzfeX)Wjj6Wrq{lxo>V>? zQDoqUGhB)Rsq9N_-$-!96{<&}jw`Iwa9-4b2=vs&hp|8?&O1_dx}vOD%&q_1q9yem z@$-Ui=y7;LnXQ&usa1I?!^MF9jf6*oL+Hq#dZj4J(aW%Yd0efobkzv`#HMnpG}qC5 z6+5+gvtX)`T~pJ`dO<i(&9LO|$pxfvcVAB0*<Q7A0q;_bMR>>)W-avl)+ev)Hc>dO z*4F)673tTVPZ}v<brbTz=6op{HZxyjx&uA)clYeA$&}hOH#CS;du8@!xkW}q(6o+g zR`WaWzSQva!dxICeqUmt^?@<3+Tq#Y`}X0lfuj}NQjvEEnPX2z?Y*4T_T-!Uc8`va zC&y^?y-CfB%t=Niq|SEo*i_;{G|Hf(BYq)EI_{^Gv2xPtTv7@lcV2FVP<clarRi~N zfgoWODN_aSt;gukrYSd|!sdy?V^<I6t7~!gnt2*W8Be(pyvKm9?O4o?U6u8{9V|+} zoZH)9@&pt1x%yLu4<2}bHAx95E-HKw%Ow@5uX5@**RgPNWbd%TKDz(*?ZrA@x7Lo` z^*th?<17gxeaTm?d&_+}O+zGX#vcU|v!AGZFchq5`{rRQPgty1M~%;!QdnLd7$}v5 z^}fn{bg|UhLcLP8R6N4`+c)a$j^0K?Gj;W%wD?Rt!?fkLFclT1hc-Au6(kh7G0)sA zmU=R^&i1riji(ngk3aAl|IC`0=wvfW6p#v0)0mc6b#>j)ZHR4ad0qQ0NxIIftob@a zkE2lrb(`8q?j_lq`v;|_MQ9*uR{8qUALllVl4Ohz_jGCww%mfqhwTSGa|2RB%9+dK zcFLnscFb^RYW^Lv(Cug?I7-XB()r*B62e(#N?8s29HG5DRbvYI9^;<*tpqpeu$kv+ z(v`(V&q5xEhbo+T60MGwCLiq_Mlg7}_z3xk<mL*P4c<m^MSoD;++3ko;;6FHHx@7( zU7ViW6pe`DO*wUyZ7Qf>pw^dsG3iapf$I3c4tB`6gsj%<zb5kY*O-+Pr>RsT<JdkV z6B7&h<V{T#=c%iVfOiugoKCVo=5)Ymj*fI41J~RS?CeW@>;_poT8FP%2@UP-^$9dD zTqu0|_LLm=qriI|&$1{ARTGRY!s2J2on!sH8)?N{zkZFoiuHk8rxr2V<WH1`hGt97 zs<(^D(gQ&;Uny@Fq~k7-`cUxa8OkK|^aK|cx~b*Ah#_vhVxuq+y60SIK(w`K!oy8K zA~504GK)o-+iQaPW$1JCtvX`wl5$L>`FKT!(@yXD`puOgN3_egq<QT&CQEg6Bn;b( zR#(iXHv{v*bcDJ)jmW4-mxzU<YV&EF+~5;wl6If3W-H|6Mc5Rm#)inx?_5Jd@SL_C zc1!1CA<hmLsS%L!fIL3zSu+3GuS<<2>@<0*&xOP;H3UatUh{H#Egm|poZJxJn`K7- zoYf$U;HsQ_R7W%t)m7P3s#P6cU%y-VdO^lOS?BE4RQR8csTF^frV@?%X{?r|xT9K> zxg<hE69i85e9rs*P}R!K4J6?pyQkYwur-%QCg3{T%0gRK&NDa5%&FB}bN0FFagtSE zcxl#MJlDn1FSxuv6`@UZ7#L|mWW-_7%Bj!!Xat13=#(843)eTbuZxBHRS*~ae4sW@ zeG&rIKdBgwTLP{%i@g~DPZ2RYk7oADy18}oyBxpu5C{lZCc@oZ!|h+<v|UutkIpjH z!G%<N-ml=V=b4J^_8121qH2|^VKR#K_1ok;UzZmzxO<CB2l2j|%=UgdaJW+rhuoN$ zsu@qz`e2dn+N_dI%=VRNRd>uX<KRHuJdv?0)DSm#NI|hf%Jj>0hTtnENBwF|)lKae zWq0J%)ko&5Ej%S8C=l#_SzU0)dGTee?gr$B+2G=UxSp}TsERN!%;br_RUIoNA9{dr zhdr)j^o#<nC)L4w(_Vx&ch9&6Pl`-}4p_~^8>n4#O)(4W(kj}(e3r`ScqkXHw@;=y zwIDF4NG^U7e-R{8J>#KKRdy@8M|%u|A9N&klqzR-S2kG^MN`uC9tjIuRN`@85M(#n z^IZId`QsU}oLUpl`v?Ng7bwQo(7^V2G|X^A9jVn(r|q|Bhn*5TVV_Q?8dgqcWBOPs zw^!2=NIGm;8BEQUqu+fZQY%(8;D+cq;&ZduVzeMXpO~C-e4yP--fFX9T2GCqy!_xm z1LT{mR~8gUd&l^tA~AF9b>)5@dSb!=wa_f@`|7l(L+SEKz(P_~w77W8<VV$s*LVNC zSOLo8>Zn1|KHU<wx*QxKe3fR4#-MgAOgy2I!~JX@-yhXt?VoA{k@YEshG%8YgO=m; z;sfu5oSH{FJ1voAN5~~?c$mBOZ!Qg8#a;!yl}+5kXA)8LX!8~Yr#J35JDJo!z22t- z8jBXhUYcNYn=NL<rWPH2Ku{U7$hN=1%j<^g*~vbi(-m;yG90+4Ki#&ow1XG_QGUEC zVRhtvfu&A}bvvAJQiH<4c>g}$O`!XwIT*)Dw!PITza18)`}naZ4kkc*kY1$m*f*r4 z@Rhh#H@&{Dlv6!cNGlznQL*10$0HyurNQlFm*l8L!Dck9q=kXxa68bifg_?`_kO0R zOCdIYg5jhMpOJ99?opnQFhPTkj5qE|Y;wuEtMmBUN(wHaoG7N&czLk}wx6H)NO5Nt z<f3hobyCcZ{^A?)9~l|dzNS1&cD-@q6u=H*;-~EzHxLT=uovQS*-pomAK|fm*qbJU zA^)(ve0X30U_N~Aev83DiqlgeA2-{DkCF*-@43G`XV4$$>i}~($h3Q{=f+B27g<Hn zkG}N?%$9Yl%msPG)MO^#jsi6gPo4jT(UarJ#sKd35j!Z4$8la*n3X2V!4p5P7k?}f zmnP%t%9N)?3j5z6_sgn4xv158;SWhrGYV%2Hx`1x<@6-twI&uek&q<d%0e;fwS*C? ze4fptB_Wi!xDVV;(+Qb0mCLq|-dr*~K3o#FCPD+3uOZb<Sle!-q+ufA8|TZh3KX5$ z%hN%-x>AzTvePg6{y3A`=ZQp|NFe{&!5o{4COEG-`4eIw?NUAkh)Et>)_HZQ6sBU6 z!3NQKsqZmEkJcK*VDH9;s&ef&xG%1KzXRbt&)r?+(WA2)828nKbojg^9*p=+(i;y{ z9DF0^LE(7YuQC?AREd-rD5icXngqu5LR4a9t}`tx-ZvIlx^u1cX#;1CM7(e&m-hEp z7?eoNtuWLS%J%SSq7vNpzFo>uRFf?>Zx0rrC^cwTK-P!cNpp4<0Ti{o?25&>s~~zv zSXkOpO(<1$Y%H&S8$co+9s(`@0oyX6O8x_uWgDKo;EpD*CZVY(_wzX9PL%LdiBL9X z?$RFXK#d9qbLD}b#1B5euC`keNIs5r-lj!E7jrlmX++Q`KK%$V8txnW@$Bf=BZrsS zIJdvpF6nb~Hz*CWuv)zcrs(9~iw*PlH!ZizdZBw1OqwR}XwLNnQnTT`MSZTkfAmqn z(Q+YNm?h@^<EaJXfz+Kp;ZOYUj@o_^!{LSL$j<95TyCd%#id0zER>&^eQ^t$-!l7| z?q=rmowztmGb;@7z4+Q`lYUo|=FoF0LP!WkZx)Yq0>#hNcK^p-ZRSH?zD<7DRmziw zqV=LDrWO(B(Rcs;w{Oe5ext#2Mg0%2Q}CX1S^&w5{U_k-Ph>F<ZptI2wh{ChD+~+> zUYezHsS0X=^4-m~>i$fnP6Y{uE)#iNTxkIAXzu?|Xl3jOoi{SH-{WAZu5g)YIfGIT zkvDeK4oPpIVgDip#)kwq!_Ii9(H6g67lcK?D?R-aDQA_n<@(u#x6IHNN3bCr?#;Aq zZB>KeMjl9$o~Z4OQ`?Z*{d>zvBGpytGz_emm6dV>Jw_XQ`#vT(WE1`5O2xRV+R5=& z(N3Qx1@@Jh`=s<Wg{Da)5Yl@3LJc)(Bprl&Y!jRd48|%-$ay47+9n;~q%jmq(q#&Z zHqa|bBKjSb7@w0g8P8V>LHGXm4~Uj}v#3!3Uczw{LmN|9vo+&X3unWCNyTeD`w{0w zbLbj)vSbRB$HnE~)DD)OZqBGd@=r;2tOHp4yLWD!CJMk-ILcC#>S${N8y!i&X`}U* z5H+DUHyM=@e0=zC-kd394osVidKwhoStgq_Ff5S&;`>!R8pfbNjb5nmPEU&>fkCQg zWCSrcy3qQB-82=*H&#}e<hSN|tn@}mEo@lTn`%98KK5>)q55RAwmO=pWr!5Ev1y92 z=r93@)#FNMeLd=jlE2)PIgRZ7r6u#v)2ph2kL+_wUw=E*<QXlxcWtytHW2pf&L=?W zmIW9En2F+|udxkj&-vr&DAzPVQ-B2Rd0x9kWQOFDP7=~1S}xnCA`sT|>7;<z+{K%% z^z<uL#LB8mL!&o{Kz7QkwcG@;^wTr^oj<@%BBQca)6{A*Tmt*MClUNdujj%9SBzGF z6-*>=&UDlzO+{gS74y7qm3VJd_mZAUB!%7WlF;>l01v^^B&d41yNQN=LUDNBW1gc6 zqmw6ja9`G-M25nhwIPm9+g7|EUnxCVkvR1^_H3OC*aqHT4ZE6^oX}Z+Cc>8D+i~wp zgfl$_?c%qJd7!=jqAUv<^0uoWY_SZbI2qGSbcT96RJi670+sZ1HIwi~Gd<7}&p&i> zVKbJwn4rbl%W(8nB^o2vD6qPD)st40>c*7su+##fX?d{Dw(h+0hKRVu$^48{658;~ z&GI>8&l_vF5sX#ci-=i3`?Oyl=KKZz0}phmYm#5b8dOZ0r(KOkJHH@;{=DJP|Edue z64~Y><Wm~m)DE<382yfa{Qwb=d$VORZ}hN0F#8ndRj@?F;>gqsEsp5C(n5LOi}?PQ zS0a$VAJ`SR+`5C&&NE89sx)U_*^cw(CK}bVrepu}IYQ0PfHxiY{Aau=bH~q@f8RX8 zIrv7;c4(5M_^+R}U&lsXErj|yf}R;(dGW7T5b5Utwd|t^TP>!vnQs-p$H(txC<v=t zT{wsMVEp_Bu?uRA9q-+Np_Nz*5Fo*Yf8NO7?N(r%8BuD5_YnOae%GMD|EIEvNEa;0 zB_x0f4Z^+PJG=q&{30I{iLnW;{ntg%XIHXjzVL<%{JF^BhjO^eHHKsn7F|M9JyWd( zdbVb}S1afN=UuIQTSc1TEJ8dqVFORi)ieFqc{lxI{9pS$7z9$KVp6Y5zGwYj5rN;h z$$;_%+#jKh2J9%RW|=*_TU?;vJlY*4(jr@4;*kRu)JJfgg#+zOx+CppT6SDdKAQH- zYnI#P-PfI58!gw`+y-ndEX;6-#9atbErfC)P^`k=Kx%-A+}vcIos9>R_BZA=(VpsM zHcYInI?Dr9G>nYA&SOBAVOG9(2u0jjHoC#FQ{VRwygyhT2-0=I*!WBW%(k3kpdm`2 z|Hz->5%P)BBuOUy&xnUFq^Cz_`^vG<q}RQsMvT*9j8JHMl}`m-N3Ss9{_03+v7OoB z&y;5#S1OOkoLu^I19#Vr?53OOfI9#?rQF<afa~E1k?|j!wnjcS>6t&>9j0V>KqAky zF1l^e4Rtt#GFBjs=)u8amYI;xmoEoGKH1rwHkS$wm^z!Ac^vMQJ`Mf~C4R`ACnmOD z?{14popA9S6%#Og#X$Du1`-M_59uqwy#NzeK2l$)U!)}uSVveu4Ef)ZNJ0*%0$+*Y zY#bUENRl*`EULNlqdM^R`E1JC=u+!m8Bd4_3+w-~@YhVg^&B(#|IDyhk~4wss2cid zWTcQut?Dn-J`nZx)=aS}Puclhzhs#vdtqH|i_Tv@K45@cUf^Ilj>Ak7c2E-)j;3wT zndoo87^^Y)1ZeRkDD&>_+TD*$e3o1W1z;!&TNxPa0tIfd->u<)i3|+#@j!Nhvzci! zG65LPs~$sg?boHJKLSV^Xc<4QQeG}CWrz2y>~CCB%#+Sh*=hzxYFQbFU!VrNK^y5( zUy6W95j=ZXu|AW;Jq%QIJ=}>=oWA_y2Xc2eB8Gz;pCAH^UKBDy+aWlRax5>5aM!ly zysCDM^Tg@o+j}|HuyY0LgE1I!<2JAv|J0uE0$k+zTGi3v{>BNM2r3iHwKQ?_tLsVb zpO7OiddqryJ_Cl8_3DWJR2G`J)Z|SEgzUA@`i(-!JoR$Dy`<tEJ||{AV77_WLehsr zK>6~|`TvjL@sBm`&-{Fqzzfb8p;Q9aQR_J8f5dg(r?~Hzu246~ygVTI_%`_*>i9eG z{QKYjYk@!{3XOmmN!yq4OcVL9tEil6%rjOCDOlQ^%ZGliLjTVzsigyM5;8N+P`}9_ zjP~?=ER(53+SdJ9t-t3VU%OCd3hUW9+yaCHp-@+k2cVFG_T#yy-2F{$gt+-X7A|<t ze=e2(yaA+GsCo47>);;RxBgitMas;5c)nf|GzR1o5$QpzdA<VT|Mw-Rb2UfPNoVVI z5O=YdD<BgtS%(HfE&Bi6+a~Lv+j8KAu5zFG1@Rk!SQLTuo0md$6Rbku8wisEBYEL1 z@Yp_+{eE_Te|+zG7^J|4dBu43cc>yj`_HBKpX=toJ(u4%-}}p;3F1)+F)O0=Tl+ic z!vFf5^!Jbu==>fO;`7CG|9y?$f$9I_A^v$Z|J`ToKW!g}GPW8pUPfexQ@uLAtWD>~ zd;2`B{>M{#ms1LK5>sli&{7G6`-@kTK-zNu9Sr{Cji}So+SqeHm_(ufIm}!6C;6Z! z1H^<MDg4<l{XJ|0f!;-Q{z%ht&cpRTqy3+m@PBRbmA!L0uq^xU=kt%D_uuXNw~zX> fgZq0V&YvOXhI7e-bA>bEQxRfMq=j>ZwBP<8P)WvI literal 32728 zcmbTe1z40_yEZ(E$_P?Y3K9cKhcv=aL#dPq(xFIqcc}viNOy;nAl=>F-Cfe%^)H_1 zefGECckko>|KsD}U|^WJ=Z>|mbzW!O-tVNvG0=$6AP@+~o7W=mArKTv2;{CT^e%Yj ztS%7;0wIUI5fPHNkKLShQ<FL&L*4EDWg7anE1>WB!OvNaP<a*C(aVm@9}P;6lLPgI zpM(sts(Zim)H=gFFmmORI*XH~;~0rN>t|0*+2)%S=&`l4+u0i3x`wYFIiD_`9==+2 z=1;KNUai_$jqNSRlN!7SJ{dA5Dn5Px_7_OAa3Csps6#;aZ~s(+3X$GEqQlxyz{5w_ zJ%`&r8Ia^y;KAJkd+82%%nOXKK!-pQ!)Sco+u;b-?`}ZqSkch;$EIiJL$DwNeQbFT zD|5B#q<o&We>QUSlHmt$(kFq3{LhcF6BQTVSX<+BIhWsNY-(+#&x1n}1r0IxX?&u& z%;Nvl9;|f#r142xi{rIB-kzT9@6Wf}nbCB+;y5Tu=+avxI<0$F0fRsuvP-3>q}+S( zfRx)@aN7^*;X#}C8f<CZ&7-5(Xw=Zq@bK^u{LIm;m4=R&Tdn4(S4zk6440Tp>h;F^ zJ!SJu&M+8EYgeoO{rfk}=^;8gItIoE?eaU2qN!}v(hBRf0`vI>TDi2Y1c3zX;Mm)* zuEjk%I=Z`e@BaAlqv?iMtLZz~ZjI}einzE}@9N~_<i+{<V+1LmqqeDO&h`Yv9ZyAQ zX%Y|ioPuKEaBYxXDuK_5lY-(aww95R(_#yTY<!ktE)O^N%c#}fgq`*EiAuX2qtP7i z67!qut0ZBsipt89o!Oh!1h+JU^X=)%`1tsqo*qhZf9JFP<v;yDAHr&{j$`Pu9zZr* zzQ)7Y*$=^!>{7hEysOL0{p`~PI-Q%F#?fh-)sDrV8XtJyU_RsC9m!T@ms(w2J=vO! z7>A-ye5sH2fqc&pXzT3hxjtPCFo?!{^r*^ag8<gl*SCALF`5itUS95w;nHJuySX|6 zr#&_GXnk0s=>D2(x@N6wx^!H!fn#SlBehI?Xr;Z>FCs`@Ov%Zk;o;$}$x=d>dCeN< zS5kxEr0a-nw)hd-ZH}>c-w+WI`RpIq!*zKlW-uv!|AM-IKmP_#diQ62T}44*Wo3m5 z4=pOFYGX9lcVDg6wc6=qTXMz62Q50Sv$GT9(IXMnz-_xnj~+$FN-<~*j^2lq3>?SL zH+kbWdSU5>o6Xjk7#Psvp$Q`pkvn7e@84%sEeSo%&CX`DDqX7ohRSQVtv#YsVZreM z5ma>Le)qN94Vwd==-KHhgGQBoTOh29!N&ugkVz?Tu5zd5b9{S@fZO%g`{<Ef;7XD4 zJD;u%rpLv`im2ul6-mB%lV4iucDg$c-T{S;jEqD@MWLW#E*+eKfpmOuK#A;Q_ZuZb zfxN6aTZsYhWb4Gl!~}cP)zwv0QnEdon-CNvD<vf*DJdx><#*}^wyfJ5&xj4ZcUQ>T zn!PY5EbN1YMZSlJ$AsZsNO7Y6{VU1)Z1f5>#oM#Bvp>ZHrl!=4jEsDek%)OP%uk(N zZEQGT&$+qnk2lBhL}l@c!BFmuV8%s&(XVS})@wY!KAe@5l$7kMH&^$C%WUS+!-p_r zWOQ^|S{e~Cv8t+S0*}qx8q?P`nRg(SNqQfjK703(j04p{>JW@$OcI`#Tn=HV3C@r} z&-gDo=7L{biY|28P#!#jLZR>MXu;%%u>0EvEd6@m3kIj9aBm$ecVZp?dV>8ONMh>C z2w(6Xp{zG<60+KXtqTFDzFBEZr!9{d+y|dP*pXd=qCrBW=e+vl_qxHBI?|?bnQu2K zNcF!LC+x5A+kX=v{%<PVKW`q8vjde*&?GY}Yio0J|KMPw`;BZUTF1}l>{3~kP>7*B zsEF=A-@kvKoSck?hDH>bkj6|`$lZaE)mUe`>+bl@=iF6T7HtwQkF<y2;~j$7mb+iv z(+o%hMgx7`;iXm!#g~xLx;wUS2E7bMh1AJbR8)ZXL1=Jp_x@w~hb#pabb7@cd9ybV zd2iK1BiiHlc7t~zm*N^4ETRtMW0Ie*Ckyo;q7KW;_@mVXAzsRgYohl)^7i;G8LY9r zun{(cLZW|V{5c5@R%DkFu-{FYH;RgUA|1sl;*ffG4r=NLO1b;JROz>WhO;h~JMGxm ztQ=3a>r<+nl{t00^u)wwMn>eA)yhPnpsEpyw??I<HLiA@1-Bope`v08(SyRAjxYPF zFYsKh>UDKLC;vR5&y9urzFjHg@5vC-AZ<B*dU?5swZwIGJ+rfOqSBeJsCIB@E;W4- z@T9-D*Q{8O{1KYZR)6E=(61*b-*g{mk@1E<CE{V?va_>4O~I$P{Z)0E$hmI6{U(NA z8KbFuve?+8)knxTFO677N;I^!u@Oc^B`PDs#cu6vym<%F=0|)Vd;p*0Dce(00oV9A z_0wU+gNws81Ea|oOxua4>oYS<7bgjP4*O`ZrL9TB;Rv#!*Ng#*`I~#xKbD4nmMoHx zB`BQuEVkxu4(g5k6z|2?q=GPt7k;gvqc9Z9e&S?qRaouHcQaA!%*CZj)jUw}Eha|Y zVh@y%pCiAc#(&AF^73{@Mh?*{Zf7eOifbJO2IjW-9ZZy2NC%MaWL9lLRIr3J_~__m zIwi5tB{H0vi;50^{}!=2D0xeR=YE_-^Oi^Q&a1W`m+YJqMw25$0|mnA(k2tdM$IX| zHTW9q1Z%~yZra+mI5`O_DtNrT(XA{=ACXq=?ky5N&gzZk(CSZS{M?%Jl9Tf}5)c0> zXB`cq&o-6wLPrN5T&|ZN>n=Ds)l=jiJ@Onl6!qCxDQMdH)1UAFYrERv;I8JZ%tKfJ zDWBWH%GOl*UaqF%`kz0$XGzD$>JMRf%f<G4B8av!3;M^85i;>rU^)3jN%?JHV1K^x zB3n~{o_cjY_LZiBf<jbO@k}B0ezR}wb56@YeF5Fkd(O`1>+9Df9unygeVn%!ZqE>n z-qDKi9;3H0Tvvh0$~L;DrPGzAs|ovR+5|oEez?8km39G@cD1_UCtDYi@Pi1=o{RlX z2g#1i4`f`aa0o_IT5AF~2@4(pKPI|F<V#q`&`^eIsWhvc2$l3R^R<B$Mpb`o1SXk= z1eAr#j2I7(n1m!WC@3)Y8R?TJlpUM%^L5&6TxL!;*FQ}r2{bE;&MwcGxr|@KkoO+6 zh=`qQR#yzSrkg1!<Q$5@`A7;1(NQtbCW~mda%(j@CGYeReBYISBircIwT{M2>OKW( zc~R=}bK%BMJv~BEQLm7JVB!&Tkp_O2v&_w~DdP<$BzF2ZniJx5EONXK#u}c5wDh#K zwKWovWUr;5uzj)P*1&LJf5Vv;5a6pn-rdc_&!7E){xhjeMEQ`89-*Tn4>scKH*s7< zi@!Q7Kd8h+O5lQloT7pG#d2)ehr0fs8?kZODROwDPax}|{|->9s;X9c;v@!({Qad7 zh_4|bxH!Ie&4dKZ8qNS;MMp<>bi8F_E97%JuCm|L+MX)U)vO)q@1H+7J3c-(%!yH< zb+*S6RPX4Z)9v~`Q$*WTKVQw~<QmDke4y$2#^Jh(9S#pFw;7@N*P(KmM3*MaCg#1o zj?i>fRZ(GL6OHxv*H-Ok2kY?hK`*bt`NB8UUvJ*{intAou6l=~+#P9Y>XLq~REM-? zmjVmU%UQ(f<<9%}J?&GkW&3x6f+pqO_cS-%<K?NLX1gzVz@h!bY;U3Y&6_t7OzIV2 zr)_L(K7O3jOK`CTU`VGkygQmB%O9T)4yU81r=+CJ)u>)yTnq^d!^gwJBO!?k3JQvi zefH!@Mqj2r%bDlN%ShDdpUKIYX=3EqP+n{M)lTMB68IBQ=u<A!`>>|Q&!h*qw4LE5 zG5jvor@g5!1rmd!qCS6S%N9j91JkCnlfGF|UeQM0vr<SShLcg?ihXW#T#2o4WU(dt z^lX)4EtE&0v|wFL?b3KOC$&(owY=g%09oxoLvnRj?EHL)*bgO6$HVkW+c;~xQeW>k z(={%8`^&u3)2ngZ7CKGeT?dHYyydBIKJyRY--laJOw77{ui=;B5vBFJoA?f+O4jL5 zU((pvSP-RnPb5oYOA8e>b!%s*FE;DPI(L90eO5!Cygk|5YwzhfK0JJmyoZYF<m9BM zqf>ozeSs%>v^fq2LX6MvN2>bQY8Vg$fYm0doyM=P^h`|BZMN3!4s`Fpo->*N96`Cb z;}F;z9|qvFyyNuTTv-t1bWg$!E7!^B)kW{y+%~PLYW2>!y~E4P2WMv(kD*9bxkxWJ z(Oz_4f=F?1Muv%-!;mFqiu?{A{6U4^kyJtA&Ww6`^xl5X`|aNo6J>2cjVSXJ@UdHJ z3j&Y{V8fYOx7qRWad1?8eAAPYyx`}^h(b|uaB%R#!p-IWubP^g;$j<J-Icm8C}+;D z8zb3_jEr*c-*0Yh0eEIV-*CTq)JK%ak}bmfez~)kqQmg2eIuuWXmgA|$>ims974*E zoObO()VHK7n&Ul1??^Vf1DtQ=B&d0kQ{bi)O!<|iq3pzuxo^?4@05`)dB5&e`LGCA z@S}U={*1`xPT_yxwgi3`a5_sJj|_d1gF`~1;Pv(O+uPeSGc%7jOlNEOo<G+H!xX@) zU%!4$SJ=exIh6>4fp@RJzu)KgW2ZM&PBIj-@Se}(PFg~~@9;P*mkOGFsU4k8uC_|` zNO((duGZQ^9_xN_$f#CkBZX<7Y?H<FIkl(QZHyS~%&>j<Fg%#<hC|A4(DDOIl-~Bw zuK(MtBumia3Dy(wir_rE{bGpUACwVzvjk>&7sA;YURbx8teC5@5ET~}mpba<8S<)% z`gqirj?wQcJDjt-y*-Le&tIQ3;MXr&*mDMk%~mouYa5&8gR{v}v)KY2UjSMS4Ayq% z8%J|BWuQqkhV8*G!~;l4;P$)VT%^jOp`+{cpaw@;;t>&LhMpIG{UTI&brHd=UcpF3 zRqSMUu(V{3Xsg^Q*_bH)S$m_xe>0m_n5Vz9vB6@dafy705*f;<+Q5JIoQh?yZFfE= zgbD*sb8o7gJF4<Ch6e_rQF9xcsI1Dwn_<7?DuBu&b+YYq0~BHz$;q~#sYwrqD)$Xs z7tZsg#KcT?gwcCp5Qj%b>VsY0+LDqT0t0QvX{S1zQ5DUj;e|x_4>Ez7@$s{>gH<pU z^hh74(yW7S1$3d{@>L6x2TDI>*MDbh{5vE>PDrSJ=$Bk!K|v%Opm<3c89Qrh273C* z3L7JP`?7&agj2?bW46tx98MJ9V=-_IK9%8~nBT*l@t#rsxxGLWcM2z!P5$=no4-Hi zAk@^<bZ>7jTcvnm&we4jA|xcFiMOOlF7ixJk|*F~r|6Y#M?V1XF<fT+F6Y+EBN*Hd zLC;R}fCA2mgSAs8tVJ4-kU(~}b5RI7Qvh6fZ8w$Py~8FWV|MKPad^8a!x*gREXx^1 zMe^mtDBD5+CZoAS131c_&agaR*?X-A#lj+E(P{#nj?w4Oi`(0zxVVMd?ZJ**l}4jX zQ3qe1r#+)Bpn@Q=Z}B&I!`?@AEmEwr9fr?{HXnR?h2KiB{BRM{wfo?ukpgcdU)_Dj zP!)*+iS}6xx)Wo*%WXNYEIh~XW9g1y2zkTjgb9Dzi<uCwLdB5$9cMW{uu*aWHA>}Q zC1e$SGm05Q!TyDcKYwlD(`C;7+MlyEQAZ*^Whqe7bZqR-KSH!M`;Ox+H!osjX4uQU z->2i|?&&g?^d*@2hRV#q;4BF}+sGZ|;X!#X*Xvs%I9L=l9?4uYHX1nnL2=Z0%6_i? z8}-{vOn0q$qIRuhho8nCg@k1gAq{mjG}5r}aJxUd(f~Taw6D%xbak^yp3UGwkz~#< zZ&tc5vJ`vAK$Z3-bnA{T4#KwFQX!QZ+^L;aFY=(4c?%#Fo9PrP#`yHrML<I%?)Y^6 zU86aoc1H&P1M}m%;^b{v5&&Do$LACle$dhRU~4<iTnje<kZ*WccWa_LTQx>o+c=)X zXlbea+u3E_@84>OwlMl~N5_iJh@F=#XXZ!i)5yTV^jx=#t>&J%NkT$_qkWZ+#+$g0 z3%tke-?V4te^!i)bRH|zt1C8gKw>lJzC*m$|D$^}{3KDx+;0i0(BeR3dHnw7ot=<- zH}=x-3wrv|_FpkUK|(q@!=4zlY<jMdAqRP8nj4_5Gbt~$UJ|gvmU;y=iwy>Y+s%xP zxm_ykM3A#BeyC8GV(yHU<@(6)r)JlexoYYr0c4s9fxFW?wYB8Pu7sPg_;^#X763vf zw<c_Yu^&H!Pqe1*%r_bhD}HuAH)!K{FKeEi9mBd%b$ZGkwT4JCZ}h|iO$R0>+tk#` zOqFt73R}RN;k~?}0-Z@#)@>0n(_FhzNp%{GzEfQkeRj#J3~)N7Z>}+OH9kyLDmt%p z9ns2pLOl?QiZO)D*Q^{ITKn>fqfJdI(vdIS)7Ah&?~dEJnD@$g@St0zn3W2tR_&Op zDKOd5k-1ab3%6WK|MTYxi4Cv<cm@Tv=jME)UOH|f_7!fYF&%GZo|_x0%?E-+7>%u{ z{X$cg*wuwmc5xc^uagH{+KeWWI5P6z^YV~=eHH_278bLBO4;g?tV#NGK+J`fCV2+u zvMbde4GT-xF=Yg}IfUBH(SFQq)|G+lB*{-Ig2z^UGWPMXa4agFJ9)0c@^YBnHXMqT zV7)!nO~UK%g>()LIY|6Kzqmc!t*&nJ_1hu^1(o85YAI=c4HcCd=d&SHta!MaMs&=@ z#&FgN7=P)~B<CyLlar;s1Po73Rugm(NxPflWvu}T8-M;pMism6r-+ba&y)qZEr=+{ z4~<4JAAps5w!+43zTx3|4wqmy`_g*G^Mwx~9QpEM%`YAk7ld<y!`-WjjrFoxaaLAV z*3lht?2UgdCr1eyiz<69=^o)Pf~V&;si3YUkQtiI+FTreRxNqP$;wK@$VkRtHG6(k z=<Fi!0tt6Kyadao)oP2cp0p&~^6(FjaN`OxaHiVn8T`o$Z0OOUbyOVDlSeO*462;V z3(dw~1j|e&wZvObpfKlayU#6$)X39w$K5lpv&(mD`x2v~TY3eacqn5SLK@jQn!SW* z9xfLXDe1F0IXO{WW|B%zW}BORk~5Z;b`ulF5)+k#8+B`HNHv{VvUEBLsHL^n*4MES zo*3u}tU7J`%W`HjRhn{?<`((eV|#Jqh2nnC%Deja00yRb2qWRC!X@Bz-CyDWD}{eR zg5Ib8cx){#ttYr4`}L^)hB>bw5pyc4Tv2|wPo5N7E+2ksjK#$b(AHUYbgINgz~RqK zXR64Ed0X3msS4Gb$))FXcD$`}yXh}6@r{eiju&7ynRV>~r|Pzsk=0mPF+s&pp$RH4 zJ2e>1ArW!PFWsS2C*|U*wAr|eMRLwUqyJe!fdaO4xMq~#N+bxiuq{z3&^8jL^Z1s7 zuXN0bTl{WJIR~HYiBGcp*-YHg#tA_CJG}z65!YLW&B-*m85uOX4$B>3Z>J*=`px5a zA1gTzEoBk;5{rl|5R_!xu(Ll?cyBpWml&laotv4-maXFVJHx&)W!K)(Y&M?HnR{{4 zjJd>6r)zPor8S*JHIarnx@Iidqf}&tNu7$6w`8WLC-}|f;o#ETTt7IRsLQSwwaE*p zfCB(~VlZiIE!vEQ4AkxR42?)w+B=-7$>!v|&dXf?A<w9N5@;y!)sWepN^b8+#kf|* zmJdJ3f#=hLN5M1R@N(nv5&$4O{C6Sp5JgJN%*8~|OK9t^VE?O|VDUabhf7ElCb-SK z86+fwzm|HdA_9E~RU*b^<Ar?^GT+tTpCz*i>wz1Mv;j028ukGj!K8kEHdl`ht}|u+ z#=)u?4x%Tv-1PJ3`jjWc#Pag;I0!nrOi9T{#2P{hJtPmXy(Wu07M7O}7n=L+W>{qH zguv2wghN_sM4eE9b$-0%D~zZCb2R<eFImHNG}-eUbqT#*e_TNCeR;{l^Bwy!z->+6 zb#`hdds<o=rl*;xsBGFF3(#lvG&lFAu!)OE5}+9nMgS@=Dw>FdJ1^Mbf!I|3=ZMkY z<K6I())a1D)#X*f$}*+ZWqk0HD>J&x41Mz;;g0=Oj?SHIVvP}>!uwCSm5M)EbhiDD zraDaMa#8hea=GXMP_0!j;r?%p>Pja&D}h>}0V4xlto8-Dj5YJhl6><@0tgW*A<T4O z?AuJTsMQ)ESW!W3ekZ#}#an59Y0n%+h$d)ts~wD&;m*(3GnmL~xW46)1sn|z4-Ip! zeWT}i@=11EHekB|18k#he}1@@CKbcV#s)~~;U<?mG{md%?Tw+v?w)hfNK}Tt>dJCN zNl0+;V|@G%@7}!=kN^7hYf4HAn_kcEnKLdf+y;Zl&q^qMB7s)+fk;lG()V5W7%Iug zm#&xgqydWJq}0^u0JerlM5rn$W&Hj)g_KYov|wsOaj&p=*|4bMlEhh2aWuQd!O5vn zq#uNa)@ZSi&;iJ|U-{2fFna%1J8n6_Q$kd)J;?g;`H_+LeTYf@+GH`SX8Dld-kw2H z(qeQO4#clv92!uF@8$Vty3$TVBmVSmMx^sjR^w{)$LBJppFTajr3ERHgpp!F7fGk5 zr$sy^YfJ2d{T8|bq}Lb6Z?5l|2s5abFj6i`YcJWJb+iPYl7GL;D`KyLT<hDleV&8r z;NcPAg@Jyu8@amn>ZT`7Gg8E7-{<@5f|7V5u;yc1?#$%I#~<+VtpZH#w>J7uX^m|s zBM9%SJo#O2wKyz?>P{t$M5U8E;ab|<Bq=r9;kKY~tpD2WH*G21(?~6IeYl(Fc6lml zx-ER>3ssRGN`mz)ItCK{2Z8c7f!m@kRU)R;_!R>B@X-(D3hVs<0au_6G+iF;&KD!G z0jJgFmM(QrW6#3RviH5i7b_9+eV1FqrQF-Cw!4SP+Ad*rg>$s=vxcoL43=UxJ11~` zT9~&B=uLb=0Y)a*iBhw?Kg0#rt~g2Wl;jU9Yp!Q^SdwURcXvf(de0u80qV<jxBf1e z2|vX|!^6XIZzp-nk2M+S-qF$A0ajKCi=f<VYyD>d2cTus(OIs!xu(Wj9tMD@JLW`m z4e&1$MYpoRLm1$^$zIO6Aik%kf9vw&=C%d{qRHlN5e@}BFy{1RvaT+&mmj^%IjYt; z>gKmH@k3anS0(7U78jZ~Yh1E>6KZY1$}IbHN@O-L^?;40LqgH+qhQ?x5_vya1PjXx zFel!ZU%s#e1zjuVnjY<~#e{`PKwr6>t0eIAJ%si2D5T41%9(t27YiIuvUt_Ac-VxF z_@0`o{I&zl=^_V<?Al_!fl;=?zo&LSH`^ovozT?fG-)GM!meo)KaOi5>P``q0<MpS z>XpO8!_!3z&_Q7ND9cCp!P7k~D2!6<&^JH^7j3Oq;0E-Ha_my!3{lTj6nO-MSKypv zWP}2>!;jAj3(=#PXX@*JMi~PksY;z!4)MCuHVqUV?QrEwt={?&O?elEk!pWTzjBSj zf)SfVN9zKk(Sh;8MK$$rtpS<_pZYa)<4JhKov+WhOiir-N!YM>MM9veY?G6Xd#4X= zYI9$EVYBp->hX;$=q-ZcU1P9=<6S35O3-0TS0|OLOYqCZfMB6-j?<MV8_czrre?EY z?UI8<*4I3LHV#q_{=J&xAb$8^fZ7X%f$Bg-<;lwR!nMbj%x<0Rx6CK{G=KRmD7C;^ zENT1a8rEyTImp!|=LUB*%r`!jh>#{_O_6!qQ6A4Xt8;C=Gea5`bqB(&e{sC)aKM?Z zQ5|y&4qhO!eUbqyp7isluxw~?vB$4pI9A>|H86=)v(4jDGtFm@_lE&Z17w=_a*Egp zK)~%E_^PY@KG^pGKR2e+nnF}<E<e1XmET%4?2y3Oiq@IkOUY`i;pq_iB$bGt4Mc@! zNn-$_0b-4b!P(fvq<+<_qI{e-NnZ*`39_=XT?NiNxbIoAxmCw7yaZ#Nv+IjFLvwHB zQ~$SmEm_fW9H_?YA^*duo4&9H9J&!XA>jt#Q5Ap=gXd}FqFLQXwE5Cvq3$7WG9BHF zwjQCF*m+hZx5~Gt@0!xx!^*BDNg<=;4Jai8gM)tlm>r$;GkKp%K%Y|K`;#LQ^UJs1 zEQ>p!i{-wDLz<_0vc0m-^i?4^GT^JmR}LU$)ZzcBH2TK!2x9RpDzNi@EOC3t#R<Wk zA|eVnL?1d;t%KWh-+_pqSCap`T9WDaT|rpiusN#fTmi}|D6=_2sOro`GQ!_W2Qoio z4Jkc-$7fBcf(9Dgm?WbI?o<e}<m#7ux9T|&+8}EY6zqo*dZRrC($kRP#g!?N6%#4K zO>w@4{`C%>LqB_WlY_osw--5<dtBfBZ?>O$lyqWCPd~_c2yYmmPvzYI2vchmto+`i zMAakLT_Xs~aLcG9Cr4Qo+uucleB@JWBtq{_)p)G;JGkXQdmB9(b7y!^dkXEJ{|Z*N z!9R!Ltg^^L`AObV%sL(QY0?s#j^52fb(HL5vWd+{D>tK`;O8fz^CT`K`l?Fl0AcX# zjGXGTHmtvQ$Hwa}+AH?QSeNrkQL)L#|9AoDRU1{Gh>MKljg)aqvnDBPBMNDrMHkU7 z8SxdBH~d;5$X<EI-A(KLe%R&bvtCU)pRRMQ=Q7@*Ct?tY1$QgN@LPcA1jlZ*V@Fwa zR-lRD5iOEjM%nxuL{N2Zv0Z^RfZ<$wIO}I|GZw0pro4I-k=W#H+ZS)bx3B|0h#>=3 z-V-K8h@S_)vfr)--v>lsy1h*mjv(Xm9Rjd!kdqy@uOkN89T~&ww^Z(Do%lhx%Y|gk zd1%p8f@Auqvw{wdpV)+@Xe5`e)2&g*grQM7_&j%{6g2s+o6wOcYHo2?pXbNwueh$` z-cI^78s-&EFHEx3l-Q8ZofuQELD%X>jP;#4@8MUtXmaXMIJ6J%`z{3UzD4}HcYw=~ zg-(?@>>Bhf4{;wLJzlg`=Q?ujUpg4r+b5sijUjJC>0|xAi-)<@Zt?v<UYI8@>*6QS zzL&uFg4WTo3C5Y8w&{3*3Fw+@old^QOsWCC-qv=oH?Hyxw7sh<KR;>&;MClmbkK1X zn`rmOlU3LyB<Bi@0ZzgC(Dg6%0}3N0JrF_fFh=<x9GI?FX78>AYe0b*0M=%m32{Z_ z#Os?}b$IyT{32Jq!n%NFZ@h5emz)>CjPuneJQ~#%HeHd|xYT09Lr~DSiTPXex?Oip zO=+I&2&hhq`IB@$!XSx=spN$_5BT6$LSY65Sr1`PpFT{!e_ug)3ygk2BQQPRJvK=| z;t&uJ05Wa+J<V9PlM@tXGErnan3hUou`%KSkDrO-tp)0ht!=n^h2-_+fmuA9w!Qae zYa=(})hio%nlaT@v8rnI;~fE~{Uv-v+hpl5U3LJ^$LCm(GR)F-_l(Ol6J_P6u;*M{ zig1tK)zy_1p!FUeZ%rZtzoBCFBnZ^z<(ZG=eIg|#1zrTZ?I{>?X=%xMw;q*=iRq`9 z9~~p((b3Ug?Hvs-@8tCKba!`mV<ROmZ_GG@sO<dwd>oGrkL_k8;6Asu`;vqo(?+*+ zWEO^f(A$FsU^gtT;UG4*DorOsZBMqpfp&bS>hnNSBQIb~Cn`_6!st(aXQyvZIV6|( zA(74;rm>?fepZf$_KMDhz<ltUArIv%e7T_+1y3}~>nWt@tKmXbLqkJeUfx4oDr`iB z%?4GxmZ9N9wknsK+YONDu@S&xLj2^(lYiF;UITpd2zWfI9FH;s0%Q~vM&=v69>Rbt z;memV2`<Ou%bgJsjH;YQBMkkdQIX8NyiR?ILNS~s=Nq}TP#8Ehk7-q&zj}3feI*XW zA>c>=O>Js=dZmuU*vYIWDfi2+nVOB1Hz-D`79+oLd<jgR^4faj=L1j#^jo`GlhI-$ zakrb|<ooy|!fSZ8<8}D2sn~rb^}|~bzO-S^-M-(F&4~t5-ZF|A8*c)|AISWJS&GcM z;tRBr@%#Ihc6KLLt357<{o(*&efA|_1h$n(fj#>wK->a$Jv=lN9P{f?<xsHR^ruM? z>oWQzXJ%#|9vwY|0pA8KEiDcX4xn;vz)*@xX2KISSm})x6%}Ra9N1|e3}g$36~45m z!V(-bkQZoptFM8bKD2rOVEr-Ree?2Ou8?MlbO5&482E3rwYAO6YOU7>fY}BI!SEj> zJ>vU+u=rP(myueNV`C}l=|HYXPfwqmnDDXf5U~fJu3Bmed?2Qhl0Sa_{Mm)4&z=1z ziX|A;(7iBqzE6P&-AinqJ@ZYNO62<bdQnkPdkFPxjmu<xy-;Yu=T`r+GHz5<FEZED zPg7+UGVyR0R@MsJEfszJjDLuDFd(WN4s>6?hQgYLhS)hchJn5^F>wyKUbpMBRq($^ zavNZDk(_Fge-6$OA0MFgn}&zO%3m<XC2LG@1gFcc(h2JZVK03lnlm;aXAh-n?3d;C zGklMa2kb`C(M0O%>U$zz1j&Pvu?J#44EQgli3-Jw#br>iN_cn^LWth7UEnATAU{3o zW6z92!bo&!X?Nd``zy&*I_lnY=<x7GZ4<9N$AKs`Fn@Btr*qh~P-~iN9%+9k(E@w^ zjy}}H?M;Dg5)HErVG@(%vov)J0kFn)f2H?XPpVJC^z%E2=XU|>-}=G=upk*}X(bO8 z<p0p+52033c;A#elz~%f6oyU+tY0Rh85bqMHv{xG;7D>g+fRKjaX&Nt2k)t5Y+{Q3 z9k8$w-D|W5j)VE1+|tt0pg&_^W-iUkdnZ&c*j%)M$qznQDoK<CwKH*9=*tg3KSD+o zc{9$H-;L$zeno%k=54br4MuUJ6U8P5bRg#8cg=>q`FA4gvQmqd>p(;KPC&#Sq9O!~ zu=Vb(EJ31^3(;df1KZ>fMyA{9yXluvX-w$2&J<IqK@6g2ZRVoj%LA8ekRfy&QumRd zi=f{;BLm5;RiWaIvmxnzz{Zw=ac&J^d?7BWVCLH7Ky)2n<@iBSadLcod3BXOo7>NG zvKX>nzc-`yb58ncTT=ds(nfaxzfe|cO3K*yI8YG*Qn;y~4uTU{Z~Vb5B6PHZ5a@4? zsgj)aQ|niP0s{nu_}0Z=f4z!4K!2&tym5XUnC`ME*?NSLt#|)2e0+UWrjDLy_@|Ep z0~57P%HKQxaaN)*WEqh1iSPgP-BC)TwIJoW_l{0_HfPv2OCei}Kp{u%*G#Y=N|G?@ z#}}`tTC|uLGaz59+^+Uo6M*%h*7XX^x<OF=HQlOBhYg#quR5uvh?FMjhKD&~I7x_j zM+SkZ;OFrRmHvk0E8d+Spi19tSN6KN@G)t0k-j9CmytQit@T$_sJYrKT-vFrlvhv~ z&rRqa$@T>$b7)EB>qMgPs1i53<-36?4pEa5f0WEBWjjk_<!U(y$5xSEec?*<ND6fS z`e<B_H=1jdnLg|EXfz#E?Fw7QfV8!f*&U5mI<VZKVaYt>+WywEEm5Q;RR0q=2{1`! ze*OMUf&8sm+uQZqrq$o%-radrvM#g!hL1<<PsPL@5Obz<MHlY@t$sO|Gy}f@`jN~p zu2RA5cVhkv#TQb(8BI>F`(5btDc~U|rK+XdJJU{}3`aah`1rU|Bh|`-vq0f>KEuQf z!2*=E6tvuWZFQzvkHd^VMc6AqOY8kZn1)6YGO(|&d)C=XMaFC{_=Re^w7Q~#0*k{w zk=)?Cr)T5WuUZdbFJ9yVCH|Av{-@sfJEJ+v-no%sVPNYUo5mlMmBWDhq?1`@qJ+c7 zrdYYs)@);h>gm%My>6mc4*MVZ1%N=L=+ccM^%V-HmpqmbDGb=cNQk2I%;%9w!f}A` z^z|XdMHCcR`uSm`%bfUs-<>SmgTR`4&5rt#TwJdmR@0<F-%9Y8Dd6Jtkk8qE3)pzT z8)G;YJ&?}UD_u4|$xIqkMYdbivo3bt*sN~Xia<U9djV9eQlrr~d-#O~mT|G*nWx|y zn~VjrTCI4!dWCgyTs%{Ivl;xt6+Sa7oDHg7rc_M0p%J<MT@m|eEL12&>0znSL)enr zP37TYK&jj1-Z7!(CgrW;M0)TLtjjAsM_0STih$9}R(XnxSn1lEE<d=J;AX${`c_O8 zgSPiDZO+bq!L~fJloqV(<1o^FjBvf!8rO6S%T+F<{)<h@{U5~prfd<gO8mdG{H%)q zu>67~CV!n@LB^NN*)aB!Fd|jbm3}enl7W}O;xkna#H8_02)Rz`zX7uRJz&v&d`N*$ zYU%7?0H85o7$78#=eAkz%T^5tk5G)~#vvi*U9J0a&_!uoYC3IaRp@lIel%T)^#EgC z3Q9}sz~SmFuQY~?ViRyaD_|i12&BbwLGmgKN;g1z$MN`GfH{zi51ax!Qx0?MhCj2i z3O~2*1qd9sw>$J@hFO4CDkq1tzTU#=Bz`=9u`GS{a<x|?f;k~`CH$)x0*R^?7@^2; zZB6*M=PO1^I5e~>n%$uI*V^v!Rx5*Ao7+W+%Jhr#$~#dj1^H1e&yP2szEf=+Dajc? z&wNVb^H@qlcA0@7>LQt@qrJTySZTqUesR2&WiowwYVUm?R2^|nlehGYj96$y<Eg&N z5lp%S1iGT4wVLIYGKz{`*sR7%Wxx;z2=T40%I#^VmikW*5^knTXLGbX#{mQSS?hg{ zMmH{+M^{^HP|%le!lUgWqp@({QR^%KGzsukP%{``F0~tt<j#_j3G@Q@gMRbI=z#~~ z(`*eV7@MF*9jy43TQ0}@`r?bqwztm%)$Q9?tq7)RT|GSq;OYSaZANJlWX$KjfMtfL zr6nm9H5n){K*NIGS{%2Jw;GVhz84ZyCgYa&!<p0TO#N<PSZciAUk839Qn>xVZZX)? zbjhd5RqqX;d%MzB+Io5b8C(D|6P<t|gHc9FDYEJ7K6x7@4o=9;<$)8BBD&n<<P@jM z3p;zw-br<z341BjIy*4HK7Fa-@135`DLb75hS-8ayB|*n6WX0mwntn2v&rCPTvD2w zL%%R!Davd!*{ba_39hBUW))r27W>u0!m6`V1|NS5=*$w%qRh<HfFsXVO$T12(V^I# zsdC`>&60`pfg!E8Cic3b#zu2x_}pBzFOJROf$26cf1s+hJfiB3C(+Z|pFiEB7ftX0 z49uga1;F#W+S1}V;IKX8G6(pzKJ&=9=|ihk)5i$MBeX}D{#n0&4~&vM!^2ZmP)N$+ z1?J+yVv|}~Fd<^X!)x?pQfhAkMSZE#QnPJsOM;nD3UdtxYxKu0Pk|*25N$+H9eQI* z-yEicOIu{XM)&HB8<tWs9L47oP~=T`4QR<{Bzn=&m<epF0=e)0Kh1?f8Yjb9K7h*r z;Cu5d$?1gl{QO&YSBr@FPQ*`db~%#yxa5YKr6p}Jt*@^xPPV6Go|=V_GOOi%6as4< z;E%1Y&TaN=zz~v^4XUZ(hk;YHwNrcJ3QW^mTU)>$^!Any{HqIjUGz>#={30MySs;n zRaUG1`plpSIauvYr=g>uXqcGbdH!6)-v{)O@8AG$AhChKw6M6EnwkpK)z9z60{l0D zS+XN+P<tinl~tqDj!l)L%|;~46}9w_%wIS^p7iewq$Ze6(Y$0g20o-|ATrA0?d`3p zP97642MAoJ7)$`^<80?U&XuzZUrJ})<?!I(r?dTEKouk9o8Y5cUrb5)nY;yDwQQ^d z0t^gEnVA(O!yBftK&2w(>jL9}<)uDVJg`3U1D;q{*T1roFQmY9dZwysLA5kk{Kucw z)lwC5?2!^C*wR>j7my#z%PSijsk4>xCH$!d1T&vjt5(@tynjECDiOh?QKh8LctLd? z2onYrJm_Hv@bI2%z}3~K-QC@h$a{!3aK=kbCT-USQo&g=8_NR~`*imc;(K{{x&9Yr zKH#i+_3G8jm+y*bv9JPiYtDVJSuGZtz@|;c3sS#$5Smd)Rzcpq1S~dcYZh%JwQ6RT zkv(wML~-)*sZ*6j@j2nav{$+?x`exsfnWe%o^K4+UOO^ID^bO-g0XaZ`3^WLfoud8 zAuiC=fm3YO9nB{maLsNKebN(0Egjpoznf<^+yOk;5v--NG@xM6`q0tXFa0?M#+X+B z#o^(*YpgG$^tw)80xJ(7kNdys^IEN(0-py^crF2KD$Ztd@%JX==dIk$%3V2G8yp-d zkx-iwk|hPyZDb@Kup9OD?d9hKnVZWLZ)q9-<j?$#j1zyZi8|tYX=x{LNdc-80qsY& zV&EswkOj%T#&Z7Kj}^hJ$!Rv@42Eg4b<#T;6;gJ6Y*^FeBriF+V3qyCq=l80Z$N<A za3+30YgV>AP*Il>+J3YydJnibJ4fE9rluw#A=y(Om2wsb?j5{`IHcy?z#_xO$*EQr zC}3}%z^0cI6HNrz=j{m1cx>fwOn}kPD~PUeJlZZRE0^@KCk6a*3}>pSh$O%H+$HZ$ zq&4s~{Ou_X3q8Erp6+zLE~j~QR(4AB5f{}4;59Pd5v%h<W(vwHG67c{oS-JSE2FGu zLqn^`$TUw-Qxhd3sc&qouj_a5;W{9Se_$6lonZCGSL10)IP-!Qk%i?KI-xYTHH|tv zASPz(XyXiET~pH%NuN|5uguK#p&@)_<?^$$TATHuk`kMTFc?g;xus`s-*Td4>teCB zFnT~i&FVkQU*$zbp~)G48NS?BYhnf5T!F(rT_(ZS(lXP8_s*R=XJ=<5ytWK9G~WXP z2t_j%(19WV#wPGSJD=`0c%UcSL=o5A>iK786$`eE?hSxM!8DvvyK%cw4T*Pk8c6x@ z^r^tjj+@cpkwdYL>1fVyfsP!f*>ysfcQROr0A97U#pD6%=3G4u7`(t3FyE*SWUH<D zPdh8RxA@R9CFU-ALS$JUFf+;UdwYAoQP2BmEP_n1e!4#0222<mBU_VI4z7i|@U>x} zu3wxxcEu2CwoZ*THF*Hs^#Dub^mGq>?+|!985wzB1~G*cI37JhLAfI3yQHKnog7mF zfrue6x3&L`CpcT>yJ2`|#x;4US^@F~SLa7opyfX~3Gns?ZdO#(%F4=}nX8?lSTKiA zHb%`FJuyHdS8PHJhdVcY#{r3fnPQ`e%1YisT6%hVDkW_K+pP&vsD+IUiGXXBhlePx z1e4IG(dXHfRkC=g!?ni3|6-=osuwaeRP0LF@6ppY<Lm=x0-1%>HezF&6kln-s!Gdf z;}_8+rp@5GFZ35%@L&a28iEHmig^BTOJ%$24K&nmanuQ9kTP>}N(#Z++MCPxt;_46 z4@+XsIxu}RS2LOX{Zn29GV>MaX3awYEyPIDGH4EZZNQ6zZWKfFeq5d;U>*Qp6@d!z zWBt^C1jbj#LT~=^Q|PGYAf-cxr<uK)`zPS3Zyp%VcB}D)I?mlPT!OBER)h$C{sexN z{=Tb(;`N??;zB#`IQ&_nENBngP(IypZ{ZJmZ}uqtwy_YYP;&<zp`Ry!>w@Eb-JB%p z;`_UN50-#)%EespP7#jdqN4>*+u%BmlSqc)7k<#`72O9^FgqRv`0zdoFnfsrWA4jw zwc2$D!v1&8ppW%*VH!Kul?p5IOJ5A+_23Nkx8l+zh~HOotVB|<TUf1%{6xHV7(4Ao z(0}cMjSv#Vs8oEw!JU$#*Z@uz0-5lw6pBA>b{fw7V94-LpGw4j>dR-0MEr%tMPXs# zhx~;Yx9u!zBI+GTAN!|Ip8#ba4q8ldP);E3h|sXGy|YUY8d(bkDWjPhml$lz-MM;T zeIq0wz$E7EYHVyA96acW<3$F7(~HG_TG!mHGn63<)<s~vL<RyFsiUWdi<p|4g2UmF zItb|hPVU7vOivas+%~W&jFf79JlxvPkd&B-<{-)9%rC&q+&ep43s{{2+Ux6URLp1Q z*49(Nx(dwi-$29l`7_ADfTTs5bX+vB@jiJ{X1Uz)snPQV6_u`z4hg?=WHODF)d9=r z7NFRJkkRGoo^qkCdbMNNILINi2EUL1#e>syid(lU5`=C_jmLq&Ax9w^=a+1WRvAA( zK;vhCTl9n7&ji<x@rgdUe*z=XH5=?!f%$r9XOF>LPB+~DRtk@p7}(%)fK~bsgl0-Y z|Nqi!-#I&-|2)FL029n0IwvDTe=rTBkU&)S!-o&&=jX^kKvIQ<hJOG49n3}^u;fYh z)1a+MYMb5zW0=Cww8;Td$3MYqC*>rOBl>@+HBHS~04v6ZhU9sg+uGV@W?aEaJ(wm% ziBwWiNdr{H<mA7~9}?ThXagjSzM!D)pIw5~*TvZx4njXV=l5^8!~S#F^M842<)|9y z>F6dvrw?T8&sti~NJw<j%u&VmLlG4mef$gTg&gdK@`-o%NgQKeQba$a8|D*BZ3i>t z1z(}Ox_VrAc*#HP8tPi;|0TZ$cyq8Yetz(-#CW`+$)szQ<-hZ5PYc4N`vp6Q+X4M< zzaS+$$=Yi0QBMD3kf09s5=Rh~*IVd55yAm0jSV;|a&k<kX)zRA#bxq&-#@+Qpdy@1 zvVP61XQ0BXui_xsjJ;$4&EcROUC*M~G~$AXq?1G@ee}NbY3-(9b92a+o5CGe5ZBx! zjp+6j`uSo)f$6Wg4~j6)(IHZG#ZiW8S<D&e8lI1)JZkU-abG(>jw%!PTbAX_873S8 zVM=o_{juv`DEMi+K30BqVxC=-r~%y%MNd%eMnbU!afkNaXzNTh7N38en2!R5;vGs9 z+i=Q3-pa)j@PUu?6IwuwDfXM`-RE=8@6ey_9BLiug%};(mjczO!viZXO(++nOP_rq z$GX+yAP{rZukblE5&L(<@LkN?G>%NU`CcFPZLupNs<3zkpzX2nDA3QIfuN2OCJyNk zx*img_xLZOT(}z4SqMZA@NFZY--Og*{^=!_c4mbDqHT^xGVm^B1UtYJXNi`OiFEyI zt_#SGT0r+fD)jbUV9O<-H2HbpJ^=K95NRITZQ%<SYGvZ$vmq4xNxS6DbH@1<R|WGA z6huiEt*vR2c6TITU#lGZ4n>D|LX)fHYvoM5pjb<uASkLNj5m)1Bs$wVQ6f;N?|y~% zIo=)xBn41~o+`zL_=S0v%WsduMH*oZ-_jas?<MB<wR7DU+mB93SsG4xlO-kPRca=n zJCC>q!Uo6%b>Z41{}p;2ZhWl~au2l?P!bXd#2?_4c0D&J0B&S=_oS|be>I0Q3;Coq zx5qW~Kq&t5@8-yL`E7DxAwDccueXPT<8HsEW_9LR=}y;7b;8OrzN)Is(E+jb`C;+V z#y9>m?Xq*9E;=A^%9=CXfzZR+LT1U_F5e6m#l^iin8l$y$3g7x`{?Ly52ot@6F_6b z<GXh~IgMK6)M=H`6m=kw5n;ty5$OJsp6*C=a>m!M;{N_SI0)NKZVh$D-F?g8D2JYB znn~}<^!vjBtv?8QX+wz)&eJgzeW*tji$*@NvDRwhF3~SLyFYZVFu_6mFVL<CI0YwI zsi{bD&^$oX2(a@9<=4mHGNO60Wzr7qx13JUXjjCD4QJ5G3!LRvRDk}4NkE{s<dIAQ zKZq#WfE;3RF$W8aK}QXdJtKf#SQr>4L*b%Xd2^QE#*3wb|0f#KG<lV)-U;gb>`M26 zMQ#bL(ryq6<YQ`RC>}3wCGhv~>;wJf`HL6XK*<2oiqw?4j*gCS0aztCY&(-b5S5`z zc?MNc0R>^X{+sLqt%5>al-ZZ$`z?U#{Gh_hc{_9y^Z#)H<>q{O^xs`T>=t$p9z0-| zlEW(ore;uWF##z6!ViEE0bg*^+~i~ma`L%_X5YUSwy2mG@Jjo=1t5rk)GJ%W<CSRO z=m!LM?0<!3Bop}6i+sL(sRt(V|H#=CYLW1oY}O^cr`^nK??8VkDl0`+s;4{3X~>JW zjE|4s%uu+m(`FgV_#0j?DBNaeJ0JTTZT&fZ##&r@8aV4imb9{StI69^EN=hdK!3@> zA@v_tbA3uYQLqr&gWT@|ExS}@d3m;4xg`+j02Bpi7^Dvto`O!K5X2J5$PyM?19DZ0 z*TE_#={U{%!B{G$08F?KJl-(AK$M&`NOXV$3j81!u%fpyoN63VLMc)ZTmfN_)<Zb3 zNXmwe0`^OrAQwzGU;!p7Q}!lYnFPVa0jJ|fi8Q$rGri;uOZ~A51NVR;0HjGU#*l%+ zUYN}-E&k75S+@Lm%E`(3_!Ve$L^>s15w8K=ke*I`%i@4#OI0fj1az30nGtR7cTl!x zs_9#$htY4-=`>v)=zDs-EmDy$$r%`g{D(PKIrc#qz<GD>jp=qSGVpUND==yo78Zso zR{;jCu@QH9QUZjC0AZ_9b-Do@-x!(_Rw}<VfZH%!=^ZyooEv2nh*29)R~%jJ)M^5D zFO*g;Ql$UMms>so`B&QkLJn-+e}{RI*!&3z!NC;yK7M{uAeQy7wX<ZNcNiPGFF5$= z305cub>v+KhgaacyiBRuMOFCvX|76%@#Nv!hGq=|G203oC8ZgF$AnB%6v!VWl?|On zcVOCpig9$L{F#39j~4(11WDSf#Nx3j;>s0s>p<8BIEg^uQb;Ht*wQO1P(W9bmEv=_ zv(pE3(OEaQsI)b3_|!6_BG(R`)gsj`eyT!%?}()4uWNZ!zBPUz4D3ILN2oDTe%}5w z%+(P<j|O0gi;XSaZiiIT$GUlE>a4lnA`Os=udHgc_rLqd?i>&2fYAM8bXMB+53K*A z!BA3G#wWbdb7lwEZF<@TFf?6mKv)DO<Xhnp7f>TlSP)uaz#I#h-Ing|t%F_x8EI+L zIcW}5Dh+orfM65VMP#L|tpoqj%5zjpxtMcLN3xepCTmU(RIg6=l7NH`6wG~)s-u=x zd&=1b{4Wa3n&B*2uk}b-l4ozOPZmM`)6=sXfIp|@4n7|WetwrQc^rUAfLY)fukBeQ z*2VSlWZ|2T2X9tCtruIbX@d&@g!KU~)quZLcP`_|&LGOCK|$?+`Fp`?Z$GA|J=H%v zY++a0+tCrGy!ST?6B10|0zCIErkY339XG40;NBh+BZkIiz1jrX4Gf^)0$;v*0J+AC z)So)@7lQ1C40LTcYg9(yO1_ouv71So^zeovLZnayxb9JU`7%8IsFHxyatk>4ND;3= zeryu>(%w#0*uVnPPtI0n|E3&zpYyw3PPMkCexUzJDb6Y#R}mbzoAAmTa1yVzZ_hzA zhm$|)mB9r2P8_dHEO*fI{HNK>u>AdXR)YaxdAm6Q4D{6WmUc~3{I3WRW9k_r2dB!- zS-0b*c6wxBsaaNP>D>H0Hf~6AdO9%?4?pKIB<RE5=4L}~P4sNKjEw3elZ6Fpvtq8@ z#RU57F8*B*WgTMGL%<8<_clg9cxE5nH9>ZI-vx2baA{!Ng~GzaNkH8M+$nH7udLpT zfPgHx9_=BhU?_lKO*C-KH7N%57@N+Ns8=eyc{95+Q@y#G;2RtgkgN7e>X*&h<mmad z-K6QvWvx^ULR<Iadb9n03eo{%eS2B}?sR-2F^53Z`1r*wE-x3EOrEp`=)Ezk@9qXh zO=qC4a+TI5oCN2T#l`7JO0G9};9`=zjZmtd*)j@pL_OGj6^0VuK4{HOJgmxWQ_2IO znOPrZd-Ukg>2NpT#}u&mCS_#_L!)?X#u~lceh{C00sj38Hei^4M7DjtK5T@A<pg}! z1qD7J0%tfLrUjB(1_R{LX{`Y#nUs_NC(4BGdkoy=2P@6KEyuduMEPLJ2H_0g9{x*~ zicl&wQI(VE#Ej!929|F85DN9Ze<K9Ehc~AAdEeoAKxO#tWi-@>jvAg<QDX^)?Z(5) z4JK5V!K1~5J#CP^H_3bps#N|?dm|gj=G<sqy|^c++Q;4v?`n*L%KZB4_8soJI9Nx5 zrnhY~$mM&ST1~Dq`HnQcCWV-jm_EGImeMjxPGdG2d!9$xmW4WVd()2C=4b<&M>*<) zI7`FATRZJ2kUs6rjodLs$~ipre4Tp^6I-CA0$DcT`zR?Xp`xN%XTo$>SQy$O_880v zBK2s}Bl_?&O|S?Jx?iL!BqRj*M_|hZlLH`2h<gP{JNYR0h1;QW-q#&9<obKL23-&^ z?G7an1Ng@T@Eb_^Ty6{*sHsFRnf><G!_~wVm(>8zv);(Z{3~nwv(7TNjS755On&}e zvZ!y_uFX9`hh2#d2!{1M;<DVzB7_<%nwLvM_W>?$Bglu)m<RMGM0k2pXIhGKf{qxR z5ezNekqTiiJD}Wc^(Aqf?U<9Wy*>Fm^WLQRZ&~>FT1H*#U`dRMbmn%we7iDj4{W|5 zI8Vak92#mJip^>|g@*X<)e5IN0I8KU8BONuSrCRNjxk_VvA;NXRngF(tx0B`6dt^_ zOheos<d?)7er98&plAm=1B&9q{~nKLW#i_lAtV!EW@M}`J00Jtjc963e?i4GRl^4d z+%0x+cu6#Na8OX4w(Ie4>-C|e0pf?4{=}~awt!Y@W#u+qd-MCyXA0>}cvQFf95T|l zsp9@1dCiwyN>7ilqO1!Olk`ePOFbd+Kj(WYTUR_E;B~E5Jb@qw0w;|PE#hLPglKEG z;g+aUQ}wpRHE<UKiA}Em9+fncqibV@?ypn0-t}Ht1V;zGsCihJD~4vhr^P8XSZYSd zGTN%!ZI29`u5<%;rqD6T8XH$`X-Z*I<DTz>jazXvk@+yvXMw1QfECqoU(%t?#<8cD zjMhK`oc0p|Ad#akwnnlCfp`l_OuEbs1u#tgcuMA@u-P{sm-a!$_pg(BW-#>k{OI_u zfcgmf0$F4*$O?lEHKlj}g(^3<waQ@+m+Dc5Nf?-NgK62saQpcT+3h3KS`Y+bRAINX zh{^^^o#AjRGVuRcahzQ<Xkt7>oxHD1#~&ZJQ?qz`i+f_(#jzVCp8BlzG;`#SOh|mH z9cw!$bCVx^O-irVQ*dp3XNzL?q4!|q<usIVSl!1>HdIkw9<Pu=bM==2hap;#XHi<I zH!DpFTTnU9k0p-Kl~@cgAR$K$n<}@Df8EhS&8sp8QU^NR+h_jkc0HdK?t|I8($0DR zVur4@hKS?7ig4Cn$NwsS$8T>lOBOeO2cHh`K5yr38E@bE*ByRx8mm<xA76kr3j(?S zah7;-9L#7Ukjw)w$h)Pe=6HOPht$D6gZ}G2L4TJG?ZpXRaiuv$1jDtm7??I{UgO1( z;(Dk3A|k+f{qt_{_^*8i|E(j!dQ+kQ^nc!8sLm!*9UZi)iW%Vb?#60PtI$r+OoBh2 zHlda)W~-#O1PeCWz{tLbTYvyU8D^o;9e0iz0`yj#zjw3=q&Y=9LzCmV(`Y<Q(3G(B z(Dm`oq+iFjKzy3VdqWX)OjeXnK+(!sT(P+?`12(TT729)hTHTaBvCq9mIkxMK@va( zqracwe0=is)st0PtiO(5O%^gFx)P%O{Q&R&ZQ+AJI5Ivr+`(PVzjjd88O8bQcrsLk z<3&GP#J0+-jThhEQAw?JBn#?J6>>e5&z%yISP;E%+uTDS__8_-zCm!vxYFeqvv>_X z3Phg>Kj8l<?kmHx+P1bIL<K<vQ9uE~2MOs0X?zrr5RjDaknT<i5hNug1t|%UkOq}* zX^;{SkS^(Z$KyWdy!$=Zd%o-YetqZ1zRrftTx-oW=NMz&_dV{VLe+{{Ko{J6{j}}v zVo2~r8ZETUIFj6-wOOFOipYsvswVyOiV*TFWgZC+Bd}J!%#h{7&1ZrE)9~s!_+<gz zDL>7ijMVVoMIxryNo<HT>LpK7K#@K(a6RT^PgNJ+F>@bh25*5iBH&v_vwQL-!c!RQ zz?UkG*35g71WxV|RjAtb_I{3jIFv4FvokXbF*5d!*Ax)qTtIv?9S~8flt}l_wo=l$ zx~Y|S!h-Q!P?z~hPWETZ*cj!FgQ>ZB;phZ?UAAZfP%vM;DmgTy_yEOo74^@4gqos_ z%_6;NP*DmBElgEz<CgG?xdyzTBSb;9y7{Yjdvz=v+S2Jxj$Vw6dDl2B@}M3;YXZLT z(;qV&?gKfkeSLO|q2&C(XFnPwob#1eqF%h7LzXgsJ_j$8LF#)3>Dc4ds}vB;^Rsi1 zwZL0IRK7<05SnEimxm;cgIgP4VzOv`uQBSlifV-xM6{I85=iEZ3_lJI!l>5`n3x6+ z6NCiG<1OG9>r>uPSj<Sr@uZ5J%|TnFe*|;`v_3k^Q!PK-|J?<J;Uu&TJb(tywEKF% zw4Ce=n<s$j&DL9GF-dnN&k%N1^0F26H(%S|@+qI|%HAl+M3TKD<)iaUzuW7+rEh@0 z^4-y{C#6-p)@>jb1)bB-F7@b%_PH;v!2QhEr~f8qu3==DWaD!ctSz_h;qZR{p1(hT zlz?^tl<ZSp?jQfSMDPj=hi0*ol3rn?=QGp&_Db!^yf`+<_qgfl7pSPB!YH&BRxD^Z z#gtmn{arV&vEJB9j`;Q4XX2@SU|unmnFUuub@Ph)51gt?{npU7;k7+)32k!2!?UyD zo-&U^-zhe3FaLTJ7TJ$Bafpp7{tBW+D2p^JY@~nFd3vUp^4-?8^g88*_H_EY`>?`7 zv!0oohjex(`C__n9!!;4SyI{G0X2d3gxk-x2`qN@FMw#Y$@uaY%}QNFEM@xYv5Rks z;tg;_(M6^XI6otyY%_qC&3BoZ>m#;;ELMen4eovP>5+n)7=oY_+!;{J)`uR9o1|~h zt@G#f92~xV_N@&X`9b;G9-3o8XIXP(B|t_NA|2;N!QJk;@naI0rmHA;7*Oj&O)YIf zO}}XBcDNc55e_Y@@otjY$K~2L+7vH4xP7+D=W9?_yjsQ^*Le=Y5x2a1#BfB}J3qmz zzM?sKd5X@#?OdPOu(z&XNTw(vGw-$`QhIppyOi-wQ0P{K#5&6cI$&oOV$01J4nDoX zoY_q8f=FBCLVmrrmp8(Z(uOH(-tOlH2%6Cw?^V$VEFXC&$4DjmFK!qD;<v%;@0JwM zfQJZ8C;GFLZ{HUjfZ!96#(b6HOCv5Kp>z)OG6M1bo!FmWBYb={KwiHZyn+2!{2h2K zO*xE?%m67e7La|u25N1H=FF>g7@`+SdB5I)1o8&_QrfQ1i9ONoO2k(yTQcW^25E;x zo{aqbbMao(3`^$r+qRA$`pT_0KpXILgapdPBozsl^Y<xCEBI3Y><L#jk~DNXttkba zD(r~ug?i0WQ+<n-`+PhKO26Gf&3$}4#ldNhjZ{<Pg9glj$oeetjqM&W&m;8?HB*G{ zEvZY_E7XYMG~L#v)PZ=-2GwkPXQ#)`QjFOZhX+rCm}e6Nzo`Ia0rpf&%ngHiXV&Z8 zv+r~EM8dqWzMO~HG>pRQ@=J>^4l>QjbQ%2Gr>St~dc=UjH4-KgO`InUg!kVz(LDJu zNnO@?0m)mcBqR1v`oUv+N&7E5Z1p1Z!<7qmPs3d9b!@l^?;FH#3vs3<a9&>DPG*+C zW_K{%yNH+ay<TLvzh4>qqs8ca`P8XPXFM;9<~}1*PsY#?WwulZ2R+Z<PoA2ZhJ#<z zi-4}t;<*KA{b$h;ZtKV!ova;oa&pqDcJn-#+pDXya^Ir}2_|(}9!f@8*LWPPM?}O^ zdJ7H|B=#f=?0I-jY;B#A3%U%Qo>YGMf)*F27^wz4er<8w!GGI7V$MN2PM%8==WeC% zpo%t!Bu;BrmsTm9CYUvV>H&DsR$5i*yxW$pV{vh@2mH^|^94R-;VVfgDKp@PKo|jr zC;*Jz_Q%G-Aw(q!xvRKJV!w5#=i?h49xnGh+64ql#qEe%GWgZ2mGR2$5Z1b1tE(sN zj2|?sow}$qjg2XYP+V5y^>%Y{goFy$2%E8yPV2(@);fzHTgb)3JeaAe=YO`@=jL)Y ze038Sr^QB2dhRK@A4v%~d+n~2$a%vopn^I(Eu8nnp(_b+Tz8eBHuGVt!+q1sQ#S|+ z4NT}PM>%)}j`rwMJ;n#iPOI0wmX^mnW}CxFnD*zxMOYFIUm_6VTfcvka~KB2#l?k$ zNG}#J`=$s!O$Pc(_6=7qxLANwoKA(!TY?!-^@NbH?rv>?g`dLe_cDM=XVGaOY9Q@C z<{XW+^?GS3*IedDgBD{$!%~aUEpT}NxU8M)<mAM8^*gswdo(DjX~D3=*cfm_VYgg( zgUh`ct0t+ns+Gl_6II+Xbte<m;a5lpj1GUJJIPM>>ic_=4xCR8%ivEd^_zyq%GV3k zV|I_$^WeV9j5;(Y9(s6iX;rx>7pT*4aC}stsRmcSPv6MEjAiI>r}}K)z!C=5(lmp| zSnf`CUmjkUd^|yPtptUwXVOB#gJ8>p7mD_7i7ia0_J>DbSwSnmaghAZYx%D>x3@z= zLfmIw(t*zh&Mg&1Z9)_nn@GDpgBbuRcr74+F$~Jj{rqB#L6*5ZRqy@c$}N8aka^SQ zmZYWWPSqJiMxK!J<Y$n%o*Wk3k&u0yw4X&V10CeWOzV@L?_`tdBtj2YR?Dlks+|<` zm`Cc*_MNAUWRnjE*Csa0%taz&961DQvTHpvzxY*mrzFoTE;4l3z2%*(a@4?%jI{j) z@-Z@AW7!nJ{SWn40Qg!=@Pq0}8a9jLlHjwbfcNqDRB)s70Oh5}RLqk3XhD-@bkfmO z?)~%@z)Aqc;v)O{`l8wWF5&?W2SmGXRFA`1l#!Z>HsRSrUbR?7Ma65^uE7L~m=ERi zPI)LkQ>(Ny3KcIg>7hWv{Ch}AwPy^w9`7gApVbW(XXc%@dLG#U!w7wcNdFwgg|?g= zUnG+KL+!3!xs~vfCzxO>!{_FZih-F0ipSuJimNOvW%v|=wr8i9b5bEceuyId!^0D& z$)M$au-<#2sEE&e$PgZFf4^cdPt|^;NY!mODLx{iUF-(J!Om_gTKe?Xt?E*1ku8WT zci(O<q*_P6^V!%q6DhKpYv19sS6RfrA<`m|cUa#~UuLJpIhQR*$%yxK=(Axqnf*M+ zE7h%`DBwaVC<+J7JUu;UXJ?Jn!otF|0m0<#5uBZ$M{{wi+O9^RaVHHm24YY;mPrkM z3x;KMGVy6ON^tUSY<y^9vVet8arqWM7Z;aO!US-H;$h_U{y9%FnxVGNQ_eGf7`d3G zWn+F@5<@Xt7LcCZvFyjX*;vbO>+3;F*l&8#dWymsY{M$;Rr&aZcPE_Ln`$?kNTvk5 z1Od{)B4Ihswll(-t1V6{Bx5sPqv2(7O;rqRckD7$15iumVI!!qh6eIfJK_}{w|?Kv z6kCh@)#f+2`TIAT0*1E58r?7Fj9w=LR|$8gpr|Nv_$ve4Si;D7WwC9+Lz0AqWR3>u zzm^FUDqzg8&duS%kTqsfQ$#?U1ePgiCV93wG?P|P=`LYUVZhS)^Deu;dU@D4PoP&} zqSAi7HB!;bs{s_TRn=J5V@{&aKnD{5;kndMasd1Zy!WT+kPp`<Uk4DJp5Af5l#V<7 z_O2~4hOu5LTb6b1@(&^Ru&T;h<uWr}`ET#Qp=Eh~f6_fw`NL$bTq->X7Ur}n`|=H1 zR9NfBn<wiOLP@(pgZyi78PjAzG}IVWoLC|GiuEl|qRydL2m3WTmMd3IpT^GhW{}(y zz#1wz`kpQ}g@e4D{mdTQHHQG)2gfHS>_I~05p%YQP|DS7;4fx6J6IS*J}?&9)2#J8 zB6Qy|n<`%ig=;A)=tKQvUyW-6y7t`)o*LG%CPL+_U1RniskHaC(rpM7$`Ie_?$#!} zIcmN2lQjUs!M5A(Z)xcxY6!<fjGva~m8*BJy?kjfl;3vGeqO;<mBn8^Y_O{8ip$=_ zaB|XnP-Y9#(XIdZ@)Oi-yX%w%gT&WWv)_i0e88g^2LW7RV^e|TqZ_ZIuUDGLeIod3 z^CrAb)3`Z+B!^(qWj0jU^YGrid)E)%Gj))=VWtT~PizErF36X!P@Xaidqr2fX9VJ5 zb;f^OIX+;-BNZ%zj6B=YGcxLU9d(ai*lAY0Zu<H;%v#m_k;9Wc_W%OGqDfHXB3|=( zA80{cIzP`cY@MrV!=wB8H-zL^^EoL$gO=F#;-uHFhqHU4@r7@cdV5>R%4+v#VaBLJ zSD2D!a$xmgV<Y>OE4%8JUw-v^rl&{Y;QV}&;f4#Z;b5htjMB5W^JR*l5;g?X;&RqS z#?YwkP8IxooCAub*C{Dwwg&S>diI!@wRuXpE7QF$r^nn9VM)O+)t;2hqchR*!2&Qb zsiRenlOGMFAwS|4e7eo;7`LUZ6~XU)+TI$Oy1RCZ&7@o4aiVa>D+kAddXIzHTKDj0 zTz>WYGXfLjj^ROf*V|)^yWR-p^;Vp}aA00`A&&KIBIk*gh{#?|4a6R~WMR)oCx>!3 zhYm&zuAqWwq`iVdwQ@xyzy=K27)o|9TsSdaY_uk@Uc+xbWQX*h=A!gm?0gl0bu3~> zff)QXVygt}zU(`go=#pvQPWAJ+_N3(VDy-%N`}$P%0EEroHt2vty0WZf%{mV?j0E( zhEB6`!R^leP_kjNdp22Cx0&)=j@DT{PIlS`-}7BUdY$Zq2jEHo4O6X{-Ah3M66s~b z)Ak)QMgdEh1b+U;-j8<hDl*ceK%D~9%bDNbl<6cNaTavdmXFUha2pzR(S=P-?TfC2 z60`56<pBhN{agoMV|fb;11`jSn6ukGZ|3SRdt&>e<UQk83lagLr}*Y|WImg!!{3P$ zE2?7cw{*81dLnq*yfsg@w=Eoo6**pycS{)snS<|U1lA5))_Pa#DIbyZECzxB8$~hD zP`W@7v5i3RghpT`nnd;=rO|rKAe7p6S%@&&@h{;c(nx>*#5fE0$NKCvy2h>iNuzS- z&QA<f#0CS8uxF0X;3Qt!z-5!Nn7B^bVtOS0-CggR$H#b>2zhE0h5U-^9?#)Pu_3i8 zBIkC+#yL^3mXGR)_YVONN&8A)s3^i>73zw6pN|Tl5Bcfwy4uRt%?t@vHhY@8BL0G( z4Wq4_tJYf}a7<Qi+bAhTU66jx?oxGW;9=A`Sh>f?fk*of;pCi0K*hLhSnYHN{gb2c z)^`3}E_;WSbT07Cj1=c|`Pq7a0xLF2R8WZtt6HH)JRylTVs7SFZ~sEi+1&biWOOM~ z;HWfF<AJp5`Ly(nZTnPiMi44fHZ&|48q@j*Hj7KVa9Ex%u{AJ$I+6r&Xon0OhS@oa zb->$5_-LipaoJc(LDcV}TtbO@;YCKc`80>9>(H2-xVWeWTWzKZtYSQ5Z{})x<&k|O zp*(y1L3=fT;Qsy7&CTVRS&4*dQv{LnLN_Zm5_UWm-Wz)Vdv3oSM18Ue(hVefggs@6 z%vKf`%}h*G<>hrx`H6VkC@~XTv&aiCyqZ~9sB?EduCP6U+r7~bC*9QVrLNl8({qCs z@c-K@`2`7K{j|T!ymcJ~C1FWoVhF+4t}dE8vk?STcV^|c=kaj4Kb0^ho!<|U$8+04 zWL!Tq&Q}~Cp5ioN5jt_eLpJuy4Yzhwn+D=wh<?;-SOanDT?q<*0!hipJK~+;VcJxZ z3U*r+QB!02qb07cBv9%lW-3Ipq9}BuOH2JGs*jfHZsH=TC9CY_+_WcC#Xfyd>0%c- zv)WnOJveZt^d4_}6>~1<7a?Kuue0y(m0v^cr^A^?PCa;wVGxg8;8ST*p5Fn%Uw6jj z9~9(Lrd2)d>nkkwX>ZHS>DVQuyc{e7d&V>jnx!_sxi~QoyfYhQh-JYTdLDswBl8=t z0VGp(bdl5UbILpGi>WhNb6{d^-8J7Of=9kzJi2sV{>wy7Wm_N3XLXzD3-VPQ9-M1q z<^8^xAnkN6E|%S0ye5A|43;UO+m=sX%yi;MC<f!>H<FTSMoV6dj;S()zt?3T&%40X zO1mbepMmWv>iVqjoo}Wz0+E+>Mr01=48-{lW3U0I8xcHg&<%p7sPx$-b=X6~yw86> zyYCN}nyxFlICQvV)-YHGhg}BPDe}Fx`NTd9SGN%6T(&ba$pZKiVl--11PGr3*fuH) z|60YiYvXe(EB&!7SP0$Mu-Wb=Q(urt+BbG{i``oJ9?P7n2<^CIKz$mhTlz9}I63t_ zzmOp)qy-kKC*1<6<8_|pPzT4cgn}<4cv;O_O;lL|m`*9u-jHEyx7GafCm!Stm<=qv zrJ0qLoU`SeC$+P)^}q$z5>VZ36%qDIUORYpmrhd)R}`Mp=gIN$xJefd=pI@&Huw?} zF10`MJ|l(=(c6ohs5-D1kH1OG1I-Fx#XUl=_`KR_N9xg|uFlS|P%S>^H34YSudFn= zh@TRZkih1&^7Yj#IoLyM&7sGQjn=p)4E`fE2iZV>UP9K?ya^?XGi__SsC%bE@zUjl zl_N%-06oFP5p`<Gz6ockD6o$6wsG)S87dHv6WTHJI>m2qt#&RS$myMJkFmaWi?XAq zUNLVd@8Ce>9-pIf^WF6Vb#4$?hjFa@(Dh!Q9I_m{!lYKbxHL$n{9ftGl{J-2DWeme zYUe%Am5*`OlgD5*aXMLhH2D5xd2NE3R;$n_S23HRVZ2E6$tS!%S{?%>(V^pU>|MUO z>{cN{A())LzAH}aYt=>_JyupMRgp1_0u_~&hKA%gmw$J+-W#ZKeSq{ApDi}&>6J~^ z4hlb;Z=<8M9>|fEYB$6WZ-!!Gr0yh*fQsAEM1ST6T{Z(~$9J}HpiLw1^uutWW&Xc! zFoh6J+lWC#4j~estvwL08b(M@dYvsIOiypu7<ACvD@`paB}+l)|452d0Q|y0>?Q^! z9iytOlVgF1#fV^mO5PxGW1~MVl8ZF%ljREm0Rdr`w9wE{mkrUpvpj;YfDdfWx=v2M zNlFs5whk4GB|+#e-_yj!EqYmy`E6R=-j==e3Iajtj{Zh~Fvl;*r%C*d(|-6D#Uxl^ zuiQU7O@4gO_R8>^;Kvrt@{E#EQZ6yfCFdCI!W)^DeEQ&c*_2eW{BOHbw_8nBhUTpP zeEw_A%hJOa7*Va5Qc%3p+IM$DRVpLnA}c$>N}R%9vn3gg^}bND}-^3e4u`J{knV zf=tL2!N4D8gCS3Y0yzecc&t%3jaV++_Vo|}$Ws<1kksCsA_dLs`~)d(@jk#vlS1!Q z3$hnhPawy^pGd~7{t6b4beXxp<WrFOD4%%j>%s+G{p|-x!Oe3;+U-+xEt&{K7Tgz; z+#cSmB&;+ghuJTWAi;gbS^d!FI^Tlx0uMR;#RYy66+Eu<dK+=QE$I>~3rn{k=ksTa zXGd&v0VK2uR~WGCUIzE=4twbECKeZ%3Ez%SNZ{rBWHY8vN21;Oxq)pk(4;zgT})x~ z{8Vyh03P8`PkgH*91_5w2fXqd*g=WN=kP0nKp#Br*Xxefpo_TLX?1CReG0G!Fqg+g zhEWI|&V;bKxx3eIwKEPEYT^C^TH#@2%!9+RmzNjFSwYxerB8L|4xIn%!FwN8M&bk< zM{q-ZjchB3i(XOru|B)u^H$;7BwPXXv?tAEuymg^K>M{#CzzT74MOApw9doT)YR0) z#YIM@6Xdwy4GHqz00KDWfJ_z89XOzBfC(y`==}-sNV%}_@J@dC;XAEQ3PDo_bYf4C z$8^TU#0&z93F_Xnlf5Za5VZ7PC&k2NTsS2~G|#N}e%f&TVj(p-GT)id4?WJ%020SG z51<IMdT9kfC~y@cH4xqRH6cLepcV@d_4f8Qp2>SM@8uz82?>b_X$Y9gf$QMr4Eid5 z$E6hzepIlt`BCvgB0wlPkQ2Y)W-KFHFDRc}nyNp8ekGBEpRYkvOT^a<Ca(<*24DRz zS=a8w5~M%L5U)Sp3SsJiuL`JxKNA5eqjv@D*RZdGekm#{T}Ca|h?lqg)vL?MhvMSm zpyG1m1j$n|!OXCZcW7|1l%(X#&`@YaF^V+My`Q9V=_#(P{io|JH9mtJUYyn5XQywt z&4r=I%XNFc%(VBbO((v{@i#bdf>0hd>)G#qIj}M4Gb>Xs;Gw0>f}=0Ul5g<Yy(35$ z4<!Y-FJGnLjd;c=aEj<pQs_5gtHshtssr#8H5UCI=tD4^X1DwKNJmF!E-`BQMtrE1 zV(*mkh$Lj$Z@Tn=+hiA88mJe1Qpl8|31`1l(uLi8*Unsz?!r@Cl%M7FHmXt|v*E!s z&}WDd1R;eC<YwS$P_UhdZkYr%y#E%8rHF)5P?!0U3mX!>x5O*qsX$?)gYpwOJGMVP zK5*Y%sq{K=yP2gk?=NA^75pbd_S&8^03WT2>S}Oxi2X*(%j?{mE(WSLFgaLikWCd9 zj3-Be!+l@vW-}SAN9M}V?Cb^T69Ow~O-)S-vzfq4a#8O#XIsp<NarY;V12`p8&-lM z1!>+Zu)=_rE0Cg9Y{kB&lY?%8L6COCu^iAyVmLj}!Iv>UfkX(7Keo2E_{xL>X@V4K z7t=29yO%P1XcH;AdaQ}bDq??xXMtWKSQTx><Dhzm9Vi<+heyWa;_3=Nfpva%ydYA{ z1QwX(?bl(gIc)s6uNE6lDY7;@d#A$=HZ9n)8miSnqZq84$J%`P5|J8LlN)@>e&~i~ zZZzWF_#h~}m!7J8TT;DXsJmO%b+D~%5ik!xQJ|ru%xti|r^ngO4ibDZIK9GNhSP?T zS|Q+;US4%uPGBJbpQ5lF`(z1bw*Wg9z>Bo8v4L~vV~s!8imVlra=o6Ujl-z-{Z?(U z-fJ=k2k*svf~O@QfQ0a@?49<A=J;9MMpW{Iw_L8By?sY(EB58fovp2=lQoqDVE^U` z%NisT&z?P#N`zGh4d@U6l7v0oU@M}61_lNIzyZYs2(4gNAm9|_<KiS5x+CSVBfDiO zV~KA|&RuWaCp9o`G#3np0I~4%=P!K0>$AKyumG;!5q~HlAsq#V;Y|Kdp)<_IW&hPp zP5_Q$cyzoD=8E(hO4T7+g}!_VYH_YN@WlwwWk8$kZ62Ooy$-*ElG>9u)RM50HGQbt ziY>kxinb^tjWv))DOW2Ani<!8m=XzO0!)U_(PsS=RD3Muyv5Wg5*b2tu8eB>%Vmo# z8)jW0nXy!??b#B)gOB|_uHRl`2{K&UUHFo;KcLHV=zfWiqBAf8D;~kuGrti|?6U-Q zUH|QcQDQ<0rsviqY;@gim@<Le+lXk<a__sp10s?ywqJsbO=Fi5Gu!WLM86ppN-%%F z6|+6Z->-!F(sJXb!*%`K$IrFSclt$OMT@mL|GaWr<|*T8Njia=Ebo~N4gKe1PO%G( z0VD_p96|~o9I*hT^<&K>jt%)*dW<Np#(vIsVj;YVTnBoKeS~jr${y&M7<$Zdl=22z z5hcjmIzkuSJJ3wLGwO&SW)3_$n4`FtHdXgClQH|0&45-~HR+8_G_h#YM^MO36s;x` z%P*CfW1c=E*kF^Z9OOQo9oA|1UOH;CV;0%9Hc_cqDHVfK^vbzOugF%s*&sWPlU-@n zo#eN;sHpm(aOP(;#IaYtm~n<C(oye9SzRroC6dKuclAVQ8`MU|bz+G)+FYgQoKpm{ zm2-WlC1d$D1ss=@j9l8I>E~GyONz)8EdlzwkFaeQSJdW)FJwr(D6_i7>rp{4Gv4+( zQmZ;_W6!(%!HcOYlq_4T0AhI`?`nXqFh<95g0IS#B`mD{b6b8sHf1Wdj*ihx(_<0Q zC_<_`J0@>skfKq<a!EJ((wmO2-qA;)E^?dm&@nNILgY?|X%XAsKX&^SvBuo=c+>`` zsZ5j0>sU9`__Q_hFwfM$&d&eCdh70LgL?VU`WR<YYv8+gZ^3HPV(QcqKfLK0Pz$n@ zLWh$ftXH;o!`$X#5{C<K7Fs3``1|XzvnwYh`4c?WC{L+!bM#fotLF*UFl#bduicX! z`=n?2o&4;;1o=?rQ7fx}6MUnERr`3b^jVd0Akk8^x>@A<pr_1X-W8q~j5nX~0L~C~ zJ?(g);7DDnod=G)(Jw8`{RtL}HNS=z`VvrqHRO7sjw=6|_OAhY_!kE?ZKvak=9ZQX zE&g8fVNzKsZ&B>tQpe@Rvje#*rk=xH4I{(IeiBrtW_fp!AhL05eB6<Ql>u|=_#hjs zw+Uu4c9#2%_}sckPw21n8a2>gAGX^(J<0tO2N#Fs%iMN)N{ai84yu=jUv#Au<bAWD zI-$CAZEsaNZVo*XV~Ok7r;orycWnI%z%}tctDXKnbfQ#TeufmOuLze^n+}vNFwbz$ zUBqR5*RfS&Ii`IHUlr%6>r!-Krd~i*s)980R&CW5P67WNZ;MUV4ji=uv(l|CUC6SL z-i<9`%tR=QDT;E(<r?QH&sxCeD2%b>55o!cmqeIqucQ$Ic&p^)XgN#F46H<SKu>ok z`S<rR9FtJ;7ui-Zbrc_Wgx%^t+FRfFZeFzsODZtbyQh11>AN!aCTgL(J$@!@eWE}k zvW(lb@1eJ7LI?-<`sdFHeis+jiT|3AKIoh6?k@cHTt=tbX>h!fw@|x4GgGSk#}7i| zg|&rpT9(4^*W{G!Y~)quVQ~`Q;>Sj6hX#GCed2AoJn&xUEL_Li^5<IN@f*QseR)@} z%5j^fO=Y!avTV`x`1tIz8^{_P3C;gh%GP_J#o2)4D8*dnzeilY1fMp+z(@gjA^=x4 zmU)cHzx5CJEf>%B<M^PyIc$}Tn1>FKmDy&;g&uD(v?bYH;o~=K1(D_1_FS9>tInx! zNl8X#2bPj^h05C_)dGib`Xw&2-%YIb@G9CBvnO>*b6R<&qLj`m7hv;e9PJ#&Fb1{| z1Q3LdPk4jbCblgbbHUF=0i{9<kGb{%ugT)3rmoeu46K!zr*87C#WkGVjxzc)60kVB z-*BIF@k+<HcHZ|3Y-+A62ae>mp_)^#Y1rE69shk=VJ{6SDO>xisNa`yHTF-Bz_kw> z={5S%;Am-(dwGc4ICn?q@Y%Mps+^n=v9Pai$wmBA)ki^I=qX{1Q8&A^&TREo_oQOv zT)1r1xs_D`Jo0<?U;QzvB1u@a$pkApB9JAi?uF1&_7c81wLa#&<cIY=D9BaF{V+4r zGb;JyD40lt`6&;>y_4Eh9QUmh%b^v0ZEZ`EIMghi$H80;I{R4qs*q7!GuUqiCnZ$S z7_aq`wkJ20zDu<e+)-d?3yZcOub{``tD9Y(o}o!~>`u_8Y<T$lT!GWdOjEP!X%Tab z^3U|%%hqE<y`ySJKkh%Yu()(>k>n;H_#b4ax;8g;b=7KB$9()~@H$HP7T;C~zKBEN z6N9bx7@BA<-D8)^G~M{}a%#zLZZjFRvi3p+8iQDM+uO{Ra7`5Ugmm)Zj`U+0i>bUg z^u!#6lV#T^ndQ>dknjXcQ|S`6-yc&idq4bMS&Ns0-S(BA-^p}CeN#)gowM~o(X`J^ z&e&uT@45X=P90C~-u`}WUT4?M*XP@lPbYhJodPnowalyA7xfhw_vEFu(gw3=sgwBa z3}y87D^+%aDyoL4^9A$NTwU43ZB>UEB#d!u---0K(4{}?zc|~1|Fn#O8HwEa8epwn zM*6)h&pA~X<I5LHG4Wm8O9!eix967B<DUk=C|Sz6>VthWPd`6k-uW&kqILCJKYW>M zD-R1H6_Z5nI@74e`eDOy*{M)lXmGNw?`uHD@Q101+<sFySwsJQv4gJ<0l=PfK*PRv z87bu8bS~(9oqcYc4*&gqIRKEU>@a{G6GZR;E{X_La&crvgm4btl6jRX`!?247;*b2 z#$6I(zClT(Bu4BTZp2gk9m>MwzHGUti9A)=qp|lz)sCotn^+z{jE<!#XMG?U*fY(Y za{AhG5L$LXZ1V|qBIJ70e8{5OyFRM+LadVaP@?q|k2i)G!vh*z@$fYvGbPvSn21=h zdR8SFOXRwtipu!LbTJbUjv2&E@+kq_mCox=aEX73uyTt=9fp&YFOOMk)c@{tPxUn3 zFMO@0X5Y{dKrJ*?RkjstPbb%mN9lDm#0(uPPc}D0g#>wM>8u~gesZ>{NKBnF`j$pz z5Mec>MSiQ_sPdZ!FEvepM#Z<@(S!LibIWRH{G)w?lf7nte+T!YRpQmL4>~pb?FZXm z&fF7f^@IWyIW=)0)W#fpt&EIs*dYC@Q>TLCZ=56!$e_=bn*8p&>m6};!v*R77wAi4 z1u@TErc1A;EIt*T`e-0WelHEAE^Dh}aznY-${cbPd`_vBKM=h3$P%~EFTw*7Q1N1g z$OS3mT#cM~@2ni30jCNc9xjJ(Sd@)$QnZ<A<kQn@Z%tSpTBH*cYpL@T8T)w0ar!IO z6gPZi1mhYlP~mQ8+cG7U;GMwEj_b7cN=7ye5041N1S<QLbg|Uck*jejr$0_lsFvB2 z%1%9cQtq)kZO-&Kt?W}XGaK!$pw>NIe|#>`@dobc_n@k;b*=I*<p#IQ*~S&uY4UyH z8XkwU;VBpxfg!j3UEbgKqx$EwO2t1yG9RC;Pu2;sF)-Lp38z|36mxRokyQSqVQ4T3 z-YNnfLOk>k4DJBinJQVx$?U-J2}W58xn_q`DC-BCw1j0J{-%GM2~Iu9$y!iYN3I_V z35?~Lt0Eh5lLZ)8OE@`Sd@{?tJSK1t8=3V%tJ-XBoJ{qDN})oghD}i<(NfRwL=V)1 znq_n;IX{2sy0E92h>9M^v(uFnQ`p<R?=RAOtBMCt{$^U?x@y}?Oe}TBmAn3?y>B1b z#>5p_Nk5H1ndkbyyc_e&#d(={qP||C2T~9!s12NPOe3YArWbo=u~m}sZaqNh&-YM{ zrZ?FG)3`C8$mw;&+|(zAAsUeODMgTkxBS7)o2Vf82VNpC>)T8?LBvgLX#FiuJbtE_ z{Un3c>wrcpRyBox=QLdm6_lKtYe0BSM7>_vVJLsmx{ltz!hTsL7oN*`KJ9*kzQL46 zk?1A%fSE|BM&xvV<bUxyBO=tlck3T7s7bl6$aULqtQ;44Wqss{;b<|_zn-e~<)bKv zllB-L{9ApgSr6vgSxCEjh1~rKrW#wUr6t)le-g&#SftX+u!x+k`C-AyH_d9|O{xb6 z;48VPAkZ1W7h|05mX_8ipe(^aK9rSBSwqB9UjSc0dIMTnO>*+%%@iStiSfzz05{$> z?E_jifZ*R*ROU}E2;HWEq1Pk{(Olk*K{UO+QPiQ*vMk%x&3{i9F+A7mj_ttT6mCQ` z4=)_fI2EQ`4P&1i9h`=QT?np{G5TX$PPnPbhHVb+hP+A|$!O^ZVtr<QezI_)ePrZx zwt12F*T4rIGkyId?b^#z=1;??2QJ5@-<uUa^MHXnj61)%xRdQZ69S;WsiuZ-$np}d zf+@4mR95KpLnujZOco}Ge_@~dC@Gm5Mt;!rf|7qYhaK$K4}pOot~_saB%@~sdWw&R zZH@l^1Ffz8OM}VTa`?~s=MRA8<+c2-yE*eQPbG6_RRA65gWZpsLWbe%6NFdUKbXkX z4{I;8nSSd8_BJqJCyCqK6|lW8er-U>u1|>^j#rk&FyyW>G9m=2#;PCJaEny$Zc%y< zyjSf}Qe^yQrg)7|_7)!pEqkw0KT~(qFH=>y9v;^E8fKcgsZ@`xx#@;EAqmIX(XuLu z=kp6wqqRq-*|G%`g5OxwONAL27MnxYflUX=O}6ECLG)+E%m%CG<_h58A12OOk)~Ts z@U^=TWSr6g1f*R*-1k25-y0F&cN#4*d7GV`XLSg+Nki+Ok52ZKdxSMKtk>pU*zSgp z%44Kx;SS!l3x5aOpaZ)8uc7i!e)kVTgN5ak)z@dlEdp$85T=&Mtl@Q%SNLB7xlhSL zBh)n)FCHfG*?-ioRYze%>e^oTa8tzV^LyoIfCGVSq6_#N9Aqrh!Sqa1-l4U#O%VwS zvLkdzb$}zWpPxwZs}&N$tq<a%OYm(1@45E2goIIGZ*z6d=tG!pA)a!gQg)U&K_t?f z(Z$IcTE3?$5f*Qvog+_vi&Ep`Ov9yX#4XK*769Ga0g6P$$H&3N1w1NR=(IElu1buR z6$4jrwkX-D)<SvtNo5VEzxF<O(M(JI4xn$y6QDnEaA?)A?lSxCpbP9-Q=`9qKCw~r zgiM<LC+CVUZ}5lD;anM3U%ERYV!vR+{OaqpdX9LcJ9XVD$g*<?0MkK;2B0aGKU4{* z1OK2#J~+0xM2NZP0*VM9BRF73<91A<niNU`yY$R`gGUWpaU-au<3JSddbmdf8of6c z7%|%YRs#grgrWUdo`IzXEi^=qErUY(^Uq-Sns{o-uR(r~1m~})0Br-Gyy+96^X@2S zigBW<h}wAd%FkyM$SK4=YeRn4`3i;p#6F)KtYtW*%d=E)Ua*iI-BNpdEHU7>^QSO? zGJ;1!B=yoP|F+cH^z1{Sr^IM*>a4G+SiyZ3$LQXP;$Ev$(M2@O9GvAnC>ELQ8c^7F zd+f`5^i}T4#4X@Bt}OBroFy(v)nb#daiXEJOvV9elaXg8KgBA1Mga?gM<7D;sv9D| zBK-JmDu_iZe+bhebk$(ktn4XV4$JRcxGLua^CcFWXG;a25?w({-TyhKzmzZf6#vLc zVh}oPX%EB*xog*uK?k$_y4U;B#Xn>GWBA+WCDh;Yqi+=v(@Jo&N)LT6S&h$JiwMy_ z1l7}@yZCEh`QF}8EM@%?82dJHj-;!{8w(^GomC)cKjojGpN)uA%SxURJ%kSqyf_4# z&{`~32oTN)29~fvRMWq|>a^z!>jq^>u~^$3|GYN5{IB$g=nHf!WBl75&n@WK@F!xk z>f3tJtv_jmi*{93@#uHxIt>p^Fwu&TCngUpc{YE%Kcb|2QOwF13cvrJ2e_b5a8qZC z93L}&EFk#*y~NPBM}$3Gl|BP66(L`Dj~SmB-Z(<(y2I>S6dK0-*JPpx!TD0$Vg`fE zOZ+v6yEF(;MfK(e7L7U$LxG&o2%uGCDZJ$yiGD_N>W;<v)LiNdH>O4o86RF~nMU_a z{9`5?0ibng0O}X<qu$}{PPR;f{-{*2!3oh<DS(?0MHqh_@{cazyrK55@H|@E?!n*M zqkIceB?Z+&O)%MsdDc-4Ol-YZFSnTk)C9xv?3;k{u~ydp$xR8LE{Yyo|IUUNuFLt! zI$ZH0$P!rmfdP(BbT=L@yikQ<Kx?<yR|fGLqR^Y7A~*%Tz(4z>`y~fpTJyelh7v6; z#@*y*Ul%crM4@8-Y&?*={ccOK-isO82r^7Yf0xBf_A1{zRqNkNJH93MX+YB-?fRjD z{#|s0i*(ydVe)4y9_zUQp1c|*v<8cPIY8igot@N~4?T2u<p#DYI(5p1F1v7T?aSDw zTLdU_;bV=C*av0<zfOVKylc&srJmM_p4Y^;y_Z$I>Q3E2G9ctxht>_)Pkhc->#(=m zx<Vnd)|+8@cG|0Mc^@QPDMBZGeZp<sr=Cs01*jly^Yj1$0cX;|Rg+h^B&ShEqZ74} zTjt>uAhth#aXH~++<s?wRT?a{Jg4duDFwd;zUYBA;V)T_KZb>c(=&(W65r)S?=cJU z-14lkdJ$+011~b)_74l)TKrzeovJ$U0a^!;z-r4X55jCwO9K6gRt;cp9xRZpPYqMR zQ+XWU1+6I_*_C*9gVBR+RbZyQ4i~E-A5DQ4%5_^s<Gs2PIqW>2u19h`3K!plOlNu7 zcqHvfMinp_V0~w2Y`nWUYqB)#-40=TYfb~YktxZKpBNg}M?`qjGlToTLb=uB*GkHn zQgg#P>jdySrGd}(3oP)3zh4dbSne)dDI(ro`<s|51yXrx+nXQ{uBzq@2G-WNlgxbB zSkR(O^d<xxSUmhr<eA66mw5|XF9PWs78V1Zoi~VLe$LM;<VH>W`?-q{^*4RZC0yx8 zH~Qa6`n`Q%U|^tHw>P;w+p;Go8zyKwlOLzLU*#yE`XR;4T8AE~*V&tqt?l-#v(i2; z^aD)uONk#CqBkEA9`)E!OGaf<kn*50!it~O-_*PVa^!=@@B9@F(A2}fw$)$Rgofi` z;GX~i!~_Itgl;G`JurpQU(wqTy?3A9k)B9v+;1MzK{DL?d(5Qi%g$U5y!0m-y*(qp z{@+myp@hyF=mAj|V8Hw{ZRDT7hX3L-soI&=ul#zHk_~!8|0{u^qszbk?0?*4xjY8k z!*ac9(2i;lP8bYa1Twkh!0g9~#$Wz~fph<3B+0{D-3Y`TNx~yJ#8CPl@)?FkD+~T_ zuJ#`@?B8#Y@Fvv*UYY+mU=&kFY#|21BAoi;*?C>&|I_UMZ@K*MSNdOG*#Eabk$M#M zj*RwCocCwy_$%@Km9_r&7xy2ha#W)VY7ynbaQt%!^E-<o=-LLc^!e{0|36nc|1Y!i b_6+lNA0@x<Sw=Wq0U;?S`>+tD`~1HE(mKY& 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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 literal 12187 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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<Timeline>::detach(); - boundSequence_.purge(); +// AutoRegistered<Timeline>::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<Timeline> PTimeline; + using lumiera::P; + class Viewer; + typedef lumiera::P<Viewer> PViewer; /** * TODO type comment */ - class Timeline + class Viewer : public Struct - , public lib::AutoRegistered<Timeline> +// , public lib::AutoRegistered<Viewer> { - 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 // <generalisation> + relation 203141 ---|> + a public + cpp default "${type}" + classrelation_ref 214277 // <generalisation> + 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 // <realization> geometry VHV from ref 128645 z 1999 to point 289 293 @@ -135,16 +139,16 @@ relationcanvas 130949 relation_ref 138629 // <unidirectional association> end relationcanvas 132229 relation_ref 139269 // <generalisation> 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 // <generalisation> 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 // <generalisation> no_role_a no_role_b no_multiplicity_a no_multiplicity_b end +relationcanvas 141573 relation_ref 203141 // <generalisation> + 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 // <generalisation> geometry VHV from ref 128517 z 1999 to point 260 139 @@ -50,14 +58,6 @@ relationcanvas 129285 relation_ref 141189 // <generalisation> no_role_a no_role_b no_multiplicity_a no_multiplicity_b end -relationcanvas 129413 relation_ref 139525 // <generalisation> - 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 // <directional composition> from ref 128389 z 1999 stereotype "<<vector>>" 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 // <unidirectional association> role_a_pos 328 250 3000 no_role_b multiplicity_a_pos 379 273 3000 no_multiplicity_b end +relationcanvas 132485 relation_ref 185349 // <generalisation> + 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 // <generalisation> + 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 // <generalisation> + 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 // <realization> 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 </pre> </div> -<div title="StructAsset" modifier="Ichthyostega" modified="201011060513" created="200709221353" tags="def classes img" changecount="20"> +<div title="StructAsset" modifier="Ichthyostega" modified="201105280022" created="200709221353" tags="def classes img" changecount="22"> <pre>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 <prg@ichthyostega.de> Date: Sat, 28 May 2011 18:14:21 +0200 Subject: [PATCH 114/496] 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 TI> class Control : public Mutation - , public function<void(TI)> { public: - typedef function<void(TI)> timeSignal; + typedef function<void(TI const&)> 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 <boost/lexical_cast.hpp> #include <iostream> #include <string> +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 TI> + class TestListener + : boost::noncopyable + { + ScopedHolder<TI> received_; + + public: + void + operator() (TI const& changeValue) + { + received_.clear(); + received_.create (changeValue); + } + + TI& + reveivedValue() + { + return *received_; + } + }; + + + template<class TAR, class SRC, class BASE> struct TestCase : BASE { void - performTestCases() + performTestCases(TimeValue const& o, TimeValue const& c) { - + cout << "Test-Case. Target=" << showType<TAR>() + <<" <--feed--- " << showType<SRC>() + <<endl; + BASE::performTestCases(o,c); } }; struct IterationEnd { - void performTestCases() { } + void performTestCases(TimeValue const&, TimeValue const&) { } }; } @@ -103,21 +141,6 @@ namespace test{ return lexical_cast<gavl_time_t> (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<Time> controller; + TestListener<Time> follower; + + VERIFY_ERROR (UNCONNECTED, controller(Time::ZERO) ); + + target.accept (controller); + CHECK (Time(0,10) == target); + controller (Time(FSecs(21,5))); + CHECK (Time(500,10) == target); + + CHECK (follower.reveivedValue() == Time::ZERO); + controller.connectChangeNotification (follower); + CHECK (follower.reveivedValue() == Time(500,10)); + + controller (Offset(-Time(500,1))); + CHECK (Time(0,10) == target); + CHECK (Time(0,10) == follower.reveivedValue()); } void - verifyMatrix_of_MutationCases () + verifyMatrix_of_MutationCases (TimeValue const& o, TimeValue const& c) { typedef Types<Duration,TimeSpan,QuTime> KindsOfTarget; typedef Types<Time,Duration,TimeSpan,QuTime> KindsOfSource; @@ -154,7 +196,7 @@ namespace test{ , TestCase , IterationEnd > TestMatrix; - TestMatrix().performTestCases(); + TestMatrix().performTestCases(o,c); } }; From c96cd666884e7a807602f58f9d94cce9fdc261b0 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 4 Jun 2011 16:33:35 +0200 Subject: [PATCH 115/496] draft implementation for time change and propagation --- src/include/play-facade.h | 10 +- src/lib/access-casted.hpp | 1 + src/lib/meta/function-erasure.hpp | 1 + src/lib/opaque-holder.hpp | 2 +- src/lib/time/control.hpp | 275 +++++++++++++++++++++++++-- src/lib/time/mutation.cpp | 4 +- src/lib/time/mutation.hpp | 2 +- tests/lib/time/time-control-test.cpp | 3 +- 8 files changed, 277 insertions(+), 21 deletions(-) diff --git a/src/include/play-facade.h b/src/include/play-facade.h index 33429323b..bc8fa26e9 100644 --- a/src/include/play-facade.h +++ b/src/include/play-facade.h @@ -45,11 +45,11 @@ -namespace proc { - namespace play { - - class PlayProcess; -} } +namespace proc { +namespace play { + + class PlayProcess; +}} namespace lumiera { diff --git a/src/lib/access-casted.hpp b/src/lib/access-casted.hpp index 9f51110f8..a54aec853 100644 --- a/src/lib/access-casted.hpp +++ b/src/lib/access-casted.hpp @@ -34,6 +34,7 @@ ** e.g. throwing an exception instead of creating a NULL value. ** ** @see lumiera::WrapperPtr usage example to access a variant record + ** @see lib::InPlaceAnyHolder usage example to access a subclass in embedded storage ** */ diff --git a/src/lib/meta/function-erasure.hpp b/src/lib/meta/function-erasure.hpp index 6e04effc2..40acfa46c 100644 --- a/src/lib/meta/function-erasure.hpp +++ b/src/lib/meta/function-erasure.hpp @@ -22,6 +22,7 @@ /** @file function-erasure.hpp + ** Generic holder for functor objects, concealing the concrete function type. ** When working with generic function objects and function pointers typed to ** arbitrary signatures, often there is the necessity to hold onto such a functor ** while hiding the actual signature behind an common interface ("type erasure"). diff --git a/src/lib/opaque-holder.hpp b/src/lib/opaque-holder.hpp index 3cc52a607..646c24b45 100644 --- a/src/lib/opaque-holder.hpp +++ b/src/lib/opaque-holder.hpp @@ -219,7 +219,7 @@ namespace lib { char content_[siz]; void* ptr() { return &content_; } - virtual ~Buffer() {} + virtual ~Buffer() {} ///< this is an ABC with VTable virtual bool isValid() const =0; virtual bool empty() const =0; virtual BaseP getBase() const =0; diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp index 87bd271f2..26c827d7e 100644 --- a/src/lib/time/control.hpp +++ b/src/lib/time/control.hpp @@ -60,12 +60,14 @@ #include "lib/error.hpp" #include "lib/time/mutation.hpp" +#include "lib/time/timevalue.hpp" //#include "lib/symbol.hpp" //#include <boost/noncopyable.hpp> //#include <iostream> //#include <boost/operators.hpp> #include <tr1/functional> +#include <vector> //#include <string> @@ -75,13 +77,192 @@ 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 + namespace mutation { + + 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 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 + */ + template<class TI> + class Mutator + : public Mutation + { + typedef function<TI(TI const&)> ValueSetter; + typedef function<TI(Offset const&)> Ofsetter; + typedef function<TI(int)> Nudger; + + static TI imposeValueChange(TimeValue& target, TI const&); + static TI imposeOffset (TimeValue& target, Offset const&); + static TI imposeNudge (TimeValue& target, int); + + template<class SRC, class TAR> + struct MutationPolicy; + + 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); + } + + public: + // using default construction and copy + + template<class TAR> + void bind_to (TAR& target) const; + + void unbind(); + + }; + + + template<class TI> + TI + Mutator<TI>::imposeValueChange (TimeValue& target, TI const& newVal) + { + return TI (Mutation::imposeChange (target,newVal)); + } + template<class TI> + TI + Mutator<TI>::imposeOffset (TimeValue& target, Offset const& off) + { + return TI (Mutation::imposeChange (target, TimeVar(target)+off)); + } + template<class TI> + TI + Mutator<TI>::imposeNudge (TimeValue& target, int off_by_steps) + { + return TI (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps)))); + } + + + template<class TI> + template<class TAR> + struct Mutator<TI>::MutationPolicy<TI, TAR> + { + static function<TI(TI const&)> + buildChangeHandler (TAR& target) + { + return bind (Mutator<TI>::imposeValueChange, ref(target), _1 ); + } + }; + + template<class TI> + template<class TAR> + struct Mutator<TI>::MutationPolicy<Offset, TAR> + { + static function<TI(Offset const&)> + buildChangeHandler (TAR& target) + { + return bind (Mutator<TI>::imposeOffset, ref(target), _1 ); + } + }; + + template<class TI> + template<class TAR> + struct Mutator<TI>::MutationPolicy<int, TAR> + { + static function<TI(int)> + buildChangeHandler (TAR& target) + { + return bind (Mutator<TI>::imposeNudge, ref(target), _1 ); + } + }; + + + template<class TI> + template<class TAR> + void + Mutator<TI>::bind_to (TAR& target) const + { + setVal_ = MutationPolicy<TI,TAR> ::buildChangeHandler (target); + offset_ = MutationPolicy<Offset,TAR>::buildChangeHandler (target); + nudge_ = MutationPolicy<int,TAR> ::buildChangeHandler (target); + } + + template<class TI> + void + Mutator<TI>::unbind() + { + setVal_ = ValueSetter(); + offset_ = Ofsetter(); + 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 TI> + class Propagator + { + typedef function<void(TI const&)> ChangeSignal; + typedef std::vector<ChangeSignal> ListenerList; + + ListenerList listeners_; + + public: + /** install notification receiver */ + template<class SIG> + void + attach (SIG const& toNotify) + { + ChangeSignal newListener (ref(toNotify)); + listeners_.push_back (newListener); + } + + /** disconnect any observers */ + void + disconnnect() + { + listeners_.clear(); + } + + /** publish a change */ + TI + operator() (TI const& changedVal) const + { + typedef typename ListenerList::const_iterator Iter; + Iter p = listeners_.begin(); + Iter e = listeners_.end(); + + for ( ; p!=e; ++p ) + (*p) (changedVal); + return changedVal; + } + }; + } + /** - * Interface: controller-element for retrieving and + * Frontend/Interface: controller-element for retrieving and * changing running time values * * @see time::Mutation @@ -90,28 +271,100 @@ namespace time { */ template<class TI> class Control - : public Mutation + : public mutation::Mutator<TI> { - public: - typedef function<void(TI const&)> TimeSignal; + mutation::Propagator<TI> notifyListeners_; + virtual void change (Duration&) const; + virtual void change (TimeSpan&) const; + virtual void change (QuTime&) const; + + public: void operator() (TI const&); void operator() (Offset const&); - void operator() (uint); + void operator() (int); - /** install a callback functor to be invoked - * to notify for any changes to the observed - * time entity */ - void connectChangeNotification (TimeSignal const&); + + /** install a callback functor to be invoked as notification + * for any changes imposed onto the observed time entity. + * @param toNotify object with \c operator()(TI const&) */ + template<class SIG> + void connectChangeNotification (SIG const& toNotify); /** disconnect from observed entity and * cease any change notification */ void disconnnect(); - }; + /* === implementation === */ + + template<class TI> + void + Control<TI>::operator () (TI const& newValue) + { + this->ensure_isArmed(); + notifyListeners_( + this->setVal_(newValue)); + } + + template<class TI> + void + Control<TI>::operator () (Offset const& adjustment) + { + this->ensure_isArmed(); + notifyListeners_( + this->offset_(adjustment)); + } + + template<class TI> + void + Control<TI>::operator () (int offset_by_steps) + { + this->ensure_isArmed(); + notifyListeners_( + this->nudge_(offset_by_steps)); + } + + + template<class TI> + void + Control<TI>::disconnnect() + { + notifyListeners_.disconnect(); + this->unbind(); + } + + template<class TI> + template<class SIG> + void + Control<TI>::connectChangeNotification (SIG const& toNotify) + { + notifyListeners_.attach (toNotify); + } + + template<class TI> + void + Control<TI>::change (Duration& targetDuration) const + { + this->bind_to (targetDuration); + } + + template<class TI> + void + Control<TI>::change (TimeSpan& targetInterval) const + { + this->bind_to (targetInterval); + } + + template<class TI> + void + Control<TI>::change (QuTime& targetQuTime) const + { + this->bind_to (targetQuTime); + } + }} // lib::time #endif diff --git a/src/lib/time/mutation.cpp b/src/lib/time/mutation.cpp index cd2b205c3..d96c96419 100644 --- a/src/lib/time/mutation.cpp +++ b/src/lib/time/mutation.cpp @@ -54,10 +54,10 @@ namespace time { * and thus is allowed to influence the basic * value stored in each time entity */ - void + TimeValue& Mutation::imposeChange (TimeValue& target, TimeValue const& valueToSet) { - target = valueToSet; + return target = valueToSet; } diff --git a/src/lib/time/mutation.hpp b/src/lib/time/mutation.hpp index b05fcaacc..bbd3f9fe9 100644 --- a/src/lib/time/mutation.hpp +++ b/src/lib/time/mutation.hpp @@ -112,7 +112,7 @@ namespace time { static EncapsulatedMutation nudge (int adjustment, Symbol gridID); protected: - static void imposeChange (TimeValue&, TimeValue const&); + static TimeValue& imposeChange (TimeValue&, TimeValue const&); }; diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp index 5b4784835..356139872 100644 --- a/tests/lib/time/time-control-test.cpp +++ b/tests/lib/time/time-control-test.cpp @@ -76,11 +76,12 @@ namespace test{ class TestListener : boost::noncopyable { + mutable ScopedHolder<TI> received_; public: void - operator() (TI const& changeValue) + operator() (TI const& changeValue) const { received_.clear(); received_.create (changeValue); From eed2b0f32000a9ef35a6ca6fc31e25471ed4dfbe Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 4 Jun 2011 17:16:29 +0200 Subject: [PATCH 116/496] time::Control simple unit test pass --- src/lib/scoped-holder.hpp | 1 + src/lib/time/control.hpp | 5 +++++ src/lib/time/lumitime.cpp | 3 +++ src/lib/time/timevalue.hpp | 2 ++ tests/lib/time/time-control-test.cpp | 8 +++++--- 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/lib/scoped-holder.hpp b/src/lib/scoped-holder.hpp index 67252b053..cc4bff08c 100644 --- a/src/lib/scoped-holder.hpp +++ b/src/lib/scoped-holder.hpp @@ -187,6 +187,7 @@ namespace lib { { if (created_) get()->~TY(); + created_ = false; } diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp index 26c827d7e..1ec66acea 100644 --- a/src/lib/time/control.hpp +++ b/src/lib/time/control.hpp @@ -342,6 +342,11 @@ namespace time { void Control<TI>::connectChangeNotification (SIG const& toNotify) { + if (this->offset_) + { // we're already connected: thus propagate current value + TI currentValue = this->offset_(Offset::ZERO); + toNotify (currentValue); + } notifyListeners_.attach (toNotify); } diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index de79b7f38..00d3e72b9 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -41,6 +41,9 @@ namespace time { const Time Time::MIN ( TimeValue::buildRaw_(-_raw(Time::MAX) ) ); const Time Time::ZERO; + const Offset Offset::ZERO (Time::ZERO); + + /** convenience constructor to build an * internal Lumiera Time value from the usual parts diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index fcc287941..fc5c9d467 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -193,6 +193,8 @@ namespace time { : TimeValue(TimeVar(target) -= origin) { } + static const Offset ZERO; + TimeValue abs() const { diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp index 356139872..032bef1a9 100644 --- a/tests/lib/time/time-control-test.cpp +++ b/tests/lib/time/time-control-test.cpp @@ -80,6 +80,8 @@ namespace test{ ScopedHolder<TI> received_; public: + TestListener() { received_.create (Time::ZERO); } + void operator() (TI const& changeValue) const { @@ -173,7 +175,7 @@ namespace test{ target.accept (controller); CHECK (Time(0,10) == target); - controller (Time(FSecs(21,5))); + controller (Time(FSecs(21,2))); CHECK (Time(500,10) == target); CHECK (follower.reveivedValue() == Time::ZERO); @@ -181,8 +183,8 @@ namespace test{ CHECK (follower.reveivedValue() == Time(500,10)); controller (Offset(-Time(500,1))); - CHECK (Time(0,10) == target); - CHECK (Time(0,10) == follower.reveivedValue()); + CHECK (Time(0,9) == target); + CHECK (Time(0,9) == follower.reveivedValue()); } From 47afbbeab008e3ea24912a2baa0d8d47fbea0008 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 4 Jun 2011 23:15:50 +0200 Subject: [PATCH 117/496] first attempt towards covering the various special cases the intention is to generate most combinations from generic template cases, but to define the difficult cases by explicit specialisation --- src/lib/time/control.hpp | 128 ++++++++++++++- tests/lib/time/time-control-test.cpp | 233 ++++++++++++++++++++++----- 2 files changed, 323 insertions(+), 38 deletions(-) diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp index 1ec66acea..2a813aff6 100644 --- a/src/lib/time/control.hpp +++ b/src/lib/time/control.hpp @@ -59,6 +59,7 @@ #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/symbol.hpp" @@ -66,6 +67,7 @@ //#include <boost/noncopyable.hpp> //#include <iostream> //#include <boost/operators.hpp> +#include <boost/utility/enable_if.hpp> #include <tr1/functional> #include <vector> //#include <string> @@ -83,6 +85,9 @@ 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; @@ -110,6 +115,9 @@ namespace time { 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<class SRC, class TAR> struct MutationPolicy; @@ -157,10 +165,88 @@ namespace time { return TI (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps)))); } + // special cases... + template<class TI> + TI + Mutator<TI>::changeDuration (Duration&, TI const&) + { + return TI (Time::ZERO); + } + template<> + Duration + Mutator<Duration>::changeDuration (Duration& target, Duration const& changedDur) + { + return Duration (Mutation::imposeChange (target,changedDur)); + } + template<> + TimeSpan + Mutator<TimeSpan>::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<TimeSpan>::imposeValueChange (TimeValue& target, TimeSpan const& newVal) + { + Mutation::imposeChange (target,newVal); + return newVal; + } + template<> + TimeSpan + Mutator<TimeSpan>::imposeOffset (TimeValue& target, Offset const& off) + { + return TimeSpan (Mutation::imposeChange (target, TimeVar(target)+off), Duration::ZERO); + } + template<> + TimeSpan + Mutator<TimeSpan>::imposeNudge (TimeValue& target, int off_by_steps) + { + return TimeSpan (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps))), Duration::ZERO); + } + + +#ifdef LIB_TIME_TIMEQUQNT_H + template<> + QuTime + Mutator<QuTime>::changeDuration (Duration&, QuTime const& irrelevantChange) + { + return QuTime (Time::ZERO, PQuant(irrelevantChange)); + } + template<> + QuTime + Mutator<QuTime>::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<QuTime>::imposeOffset (TimeValue&, Offset const&) + { + throw error::Logic ("can't deliver result of the change as grid-aligned time, " + "because it's not evident which concrete grid to use" + ,LUMIERA_ERROR_UNKNOWN_GRID); //////////////////TODO: we could pick up a "default grid" from session? + } + template<> + QuTime + Mutator<QuTime>::imposeNudge (TimeValue&, int) + { + throw error::Logic ("can't deliver result of the change as grid-aligned time, " + "because it's not evident which concrete grid to use" + ,LUMIERA_ERROR_UNKNOWN_GRID); + } +#endif //(End)quantisation special case + + template<class TI> template<class TAR> - struct Mutator<TI>::MutationPolicy<TI, TAR> + struct Mutator<TI>::MutationPolicy<typename disable_if< is_sameType<TAR,Duration>, TI>::type, TAR> { static function<TI(TI const&)> buildChangeHandler (TAR& target) @@ -192,6 +278,46 @@ namespace time { }; + //special cases for changing Durations.... + + template<class TI> + template<class TAR> + struct Mutator<TI>::MutationPolicy<typename enable_if< is_sameType<TAR,Duration>, TI>::type, TAR> + { + static function<TI(TI const&)> + buildChangeHandler (Duration& target) + { + return bind (Mutator<TI>::changeDuration, ref(target), _1 ); + } + }; + + // +// template<class TI> +// template<class TAR> +// struct Mutator<TI>::MutationPolicy<TI, TAR> +// { +// static function<TimeValue(TimeValue const&)> +// buildChangeHandler (Duration& target) +// { +// return bind (Mutator<TI>::changeDuration, ref(target), _1 ); +// } +// }; + +// template<class TI> +// template<> +// struct Mutator<TI>::MutationPolicy<int, Duration> +// { +// static function<TI(int)> +// buildChangeHandler (Duration& target) +// { +// return bind (Mutator<TI>::nudgeDuration, ref(target), _1 ); +// } +// }; + + + + + template<class TI> template<class TAR> void diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp index 032bef1a9..e85fc1685 100644 --- a/tests/lib/time/time-control-test.cpp +++ b/tests/lib/time/time-control-test.cpp @@ -34,6 +34,7 @@ #include <boost/lexical_cast.hpp> #include <iostream> #include <string> +#include <limits> using lib::test::showType; using boost::lexical_cast; @@ -95,28 +96,6 @@ namespace test{ return *received_; } }; - - - - template<class TAR, class SRC, class BASE> - struct TestCase - : BASE - { - void - performTestCases(TimeValue const& o, TimeValue const& c) - { - cout << "Test-Case. Target=" << showType<TAR>() - <<" <--feed--- " << showType<SRC>() - <<endl; - BASE::performTestCases(o,c); - } - }; - - struct IterationEnd - { - void performTestCases(TimeValue const&, TimeValue const&) { } - }; - } @@ -150,11 +129,14 @@ namespace test{ { TimeValue o (random_or_get (pop(arg))); TimeValue c (random_or_get (pop(arg))); - CHECK (o != c, "unsuitable testdata"); + CHECK (c!=Time::ZERO && o != c, "unsuitable testdata"); - // using a 25fps-grid, but with an time origin offset by 1/50sec + // 25fps-grid, but with an time origin offset by 1/50sec TimeGrid::build("test_grid", 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); @@ -188,22 +170,199 @@ namespace test{ } - - void - verifyMatrix_of_MutationCases (TimeValue const& o, TimeValue const& c) - { - typedef Types<Duration,TimeSpan,QuTime> KindsOfTarget; - typedef Types<Time,Duration,TimeSpan,QuTime> KindsOfSource; - typedef InstantiateChainedCombinations< KindsOfTarget - , KindsOfSource - , TestCase - , IterationEnd > TestMatrix; - - TestMatrix().performTestCases(o,c); - } + void verifyMatrix_of_MutationCases (TimeValue const& o, TimeValue const& c); }; + namespace { // Implementation: Matrix of individual test combinations + + template<class TAR> + struct TestTarget + { + static TAR + build (TimeValue const& org) + { + return TAR(org); + } + }; + + template<> + struct TestTarget<TimeSpan> + { + static TimeSpan + build (TimeValue const& org) + { + return TimeSpan (org, FSecs(3,2)); + } + }; + + template<> + struct TestTarget<QuTime> + { + static QuTime + build (TimeValue const& org) + { + return QuTime (org, "test_grid"); + } + }; + + + template<class SRC> + struct TestChange + { + static SRC + prepareChangeValue (TimeValue const& c) + { + return SRC(c); + } + }; + + template<> + struct TestChange<TimeSpan> + { + static TimeSpan + prepareChangeValue (TimeValue const& c) + { + return TimeSpan (c, Duration(c)); + } + }; + + template<> + struct TestChange<QuTime> + { + static QuTime + prepareChangeValue (TimeValue const& c) + { + return QuTime (c, "test_grid_NTSC"); + } + }; + + + + template<class TAR, class SRC> + void + verify_wasChanged (TAR const& target, TimeValue const& org, SRC const& change) + { + CHECK (target != org); + CHECK (target == change); + } + + template<class SRC> + 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) + { + CHECK (target != org); + CHECK (target == otherDuration); + } + void + verify_wasChanged (Duration const& target, TimeValue const& org, TimeSpan const& span) + { + CHECK (target != org); + CHECK (target == span.duration()); + } + + + + template<class TAR> + void + verify_wasOffset (TAR const&, TimeValue const&, Offset const&)//(TAR const& target, TimeValue const& o, Offset const& offset) + { + + } + + template<class TAR> + void + verify_zeroAlligned (TAR const&, TAR const&)//(TAR const& target, TAR const& oldState) + { + + } + + template<class TAR> + void + verify_nudged (TAR const&, TAR const&, int64_t)//(TAR const& target, TAR const& oldState, int64_t offsetSteps) + { + + } + + + + + template<class TAR, class SRC, class BASE> + struct TestCase + : BASE + { + void + performTestCases(TimeValue const& o, TimeValue const& c) + { + cout << "Test-Case. Target=" << showType<TAR>() + <<" <--feed--- " << showType<SRC>() + <<endl; + + Control<SRC> controller; + + TAR target = TestTarget<TAR>::build(o); + target.accept (controller); + + SRC change = TestChange<SRC>::prepareChangeValue(c); + controller (change); + verify_wasChanged (target, o, change); + + Offset offset(c); + controller (offset); + verify_wasOffset (target, o, offset); + + TAR oldState(target); + controller (0); + verify_zeroAlligned(target, oldState); + + controller (+1); + verify_nudged (target, oldState, +1); + + controller (-2); + verify_nudged (target, oldState, -1); + + int maxInt = std::numeric_limits<int>::max(); + int minInt = std::numeric_limits<int>::min(); + + controller (maxInt); + verify_nudged (target, oldState, -1LL + maxInt); + + controller (minInt); + verify_nudged (target, oldState, -1LL + maxInt+minInt); + + + // tail recursion: further test combinations.... + BASE::performTestCases(o,c); + } + }; + + struct IterationEnd + { + void performTestCases(TimeValue const&, TimeValue const&) { } + }; + + }//(End)Implementation Test-case matrix + + + void + TimeControl_test::verifyMatrix_of_MutationCases (TimeValue const& o, TimeValue const& c) + { + typedef Types<Duration,TimeSpan,QuTime> KindsOfTarget; + typedef Types<Time,Duration,TimeSpan,QuTime> KindsOfSource; + typedef InstantiateChainedCombinations< KindsOfTarget + , KindsOfSource + , TestCase + , IterationEnd > TestMatrix; + + TestMatrix().performTestCases(o,c); + } + + /** Register this test class... */ LAUNCHER (TimeControl_test, "unit common"); From a8fd0bf7c50eeb84375973a3ad01ce47ae0dd4a0 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 5 Jun 2011 00:10:37 +0200 Subject: [PATCH 118/496] better avoid throwing and use a default time grid in some cases --- src/lib/time/control.hpp | 18 +++++++++--------- src/lib/time/quantiser.cpp | 11 ++++++++++- src/lib/time/quantiser.hpp | 11 +++++++++++ tests/lib/time/time-control-test.cpp | 10 +++++----- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp index 2a813aff6..398d2c172 100644 --- a/src/lib/time/control.hpp +++ b/src/lib/time/control.hpp @@ -162,7 +162,7 @@ namespace time { TI Mutator<TI>::imposeNudge (TimeValue& target, int off_by_steps) { - return TI (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps)))); + return TI (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps)))); //////////////////TICKET #810 } // special cases... @@ -226,19 +226,19 @@ namespace time { } template<> QuTime - Mutator<QuTime>::imposeOffset (TimeValue&, Offset const&) + Mutator<QuTime>::imposeOffset (TimeValue& target, Offset const& off) { - throw error::Logic ("can't deliver result of the change as grid-aligned time, " - "because it's not evident which concrete grid to use" - ,LUMIERA_ERROR_UNKNOWN_GRID); //////////////////TODO: we could pick up a "default grid" from session? + return QuTime (Mutation::imposeChange (target, TimeVar(target)+off) + ,getDefaultGridFallback() //////////////////TICKET #810 + ); } template<> QuTime - Mutator<QuTime>::imposeNudge (TimeValue&, int) + Mutator<QuTime>::imposeNudge (TimeValue& target, int off_by_steps) { - throw error::Logic ("can't deliver result of the change as grid-aligned time, " - "because it's not evident which concrete grid to use" - ,LUMIERA_ERROR_UNKNOWN_GRID); + return QuTime (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps))) + ,getDefaultGridFallback() //////////////////TICKET #810 + ); } #endif //(End)quantisation special case diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index fcb916004..61103008c 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -51,10 +51,19 @@ namespace time { , LUMIERA_ERROR_UNKNOWN_GRID); return grid_found; } - }//(End) implementation helpers + PQuant + getDefaultGridFallback() + { + static PQuant globalDefaultGrid (new FixedFrameQuantiser(1)); + return globalDefaultGrid; ///////////////////////TICKET #810 + }; + + + + Grid::~Grid() { } // hint to emit the VTable here... diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index 06df4d3f1..d7717bb03 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -64,6 +64,17 @@ namespace time { } + + /** @todo placeholder for accessing + * a current or default session grid. + * To be implemented later. + */ + PQuant getDefaultGridFallback(); ///////////////////////TICKET #810 + + + + + /** * Facility to create grid-aligned time values. * Effectively, a quantiser exposes the value Grid API, but diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp index e85fc1685..1b5cb2dd7 100644 --- a/tests/lib/time/time-control-test.cpp +++ b/tests/lib/time/time-control-test.cpp @@ -297,7 +297,7 @@ namespace test{ : BASE { void - performTestCases(TimeValue const& o, TimeValue const& c) + performTestCases(TimeValue const& org, TimeValue const& c) { cout << "Test-Case. Target=" << showType<TAR>() <<" <--feed--- " << showType<SRC>() @@ -305,16 +305,16 @@ namespace test{ Control<SRC> controller; - TAR target = TestTarget<TAR>::build(o); + TAR target = TestTarget<TAR>::build(org); target.accept (controller); SRC change = TestChange<SRC>::prepareChangeValue(c); controller (change); - verify_wasChanged (target, o, change); + verify_wasChanged (target, org, change); Offset offset(c); controller (offset); - verify_wasOffset (target, o, offset); + verify_wasOffset (target, org, offset); TAR oldState(target); controller (0); @@ -337,7 +337,7 @@ namespace test{ // tail recursion: further test combinations.... - BASE::performTestCases(o,c); + BASE::performTestCases(org,c); } }; From d39ae8afc639e2d98520e6d7357aae1ef630f2aa Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 5 Jun 2011 19:11:46 +0200 Subject: [PATCH 119/496] idea for better organisation of the template specialisations --- src/lib/time/control.hpp | 106 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 3 deletions(-) diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp index 398d2c172..6d7b3ad73 100644 --- a/src/lib/time/control.hpp +++ b/src/lib/time/control.hpp @@ -94,6 +94,9 @@ namespace time { using std::tr1::ref; + template<class TI> + struct Mutabor; + /** * Implementation building block: impose changes to a Time element. @@ -120,6 +123,8 @@ namespace time { template<class SRC, class TAR> struct MutationPolicy; + + friend class Mutabor<TI>; protected: mutable ValueSetter setVal_; @@ -145,7 +150,75 @@ namespace time { }; + template<class TI> + struct Mutabor + { + + static void + imposeChange (TimeValue& target, TI const& newVal) + { + Mutator<TI>::imposeChange (target,newVal); + } + }; + template<class TI, class TAR> + struct Builder + { + + static TI + buildChangedValue (TAR& target, TI const&) + { + return TI(target); + } + }; + template<class TAR> + struct Builder<TimeSpan, TAR> + { + static TimeSpan + buildChangedValue (TAR& target, TimeSpan const& newVal) + { + return TimeSpan (target, newVal.duration()); + } + }; +#ifdef LIB_TIME_TIMEQUQNT_H + template<class TAR> + struct Builder<QuTime, TAR> + { + static QuTime + buildChangedValue (TAR& target, QuTime const&) + { + return QuTime (target + ,getDefaultGridFallback() //////////////////TICKET #810 + ); + } + }; +#endif + + template<class TI, class TAR> + struct Adap + : Mutabor<TI> + , Builder<TI,TAR> + { + + static TI + imposeValueChange (TAR& target, TI const& newVal) + { + imposeChange (target,newVal); + return buildChangedValue(target,newVal); + } + }; + + template<class TI, class SRC, class TAR> + struct Policy + { + static function<TI(TI const&)> + buildChangeHandler (TAR& target) + { + return bind (Adap<TI,TAR>::imposeValueChange, ref(target), _1 ); + } + }; + + template<class TI> TI Mutator<TI>::imposeValueChange (TimeValue& target, TI const& newVal) @@ -243,10 +316,36 @@ namespace time { #endif //(End)quantisation special case + namespace { // metaprogramming helpers to pick the suitable Instantiation... + + template<class T> + struct isDuration + { + static const bool value = is_sameType<T,Duration>::value; + }; + template<class T> + struct isTimeSpan + { + static const bool value = is_sameType<T,TimeSpan>::value; + }; + template<class T> + struct isQuTime + { + static const bool value = is_sameType<T,QuTime>::value; + }; + + template<class T> + struct isSpecialCase + { + static const bool value = isDuration<T>::value; + }; + + } template<class TI> template<class TAR> - struct Mutator<TI>::MutationPolicy<typename disable_if< is_sameType<TAR,Duration>, TI>::type, TAR> + struct Mutator<TI>::MutationPolicy< typename disable_if< isSpecialCase<TAR>, + TI>::type, TAR> { static function<TI(TI const&)> buildChangeHandler (TAR& target) @@ -282,7 +381,8 @@ namespace time { template<class TI> template<class TAR> - struct Mutator<TI>::MutationPolicy<typename enable_if< is_sameType<TAR,Duration>, TI>::type, TAR> + struct Mutator<TI>::MutationPolicy< typename enable_if< isDuration<TAR>, + TI>::type, TAR> { static function<TI(TI const&)> buildChangeHandler (Duration& target) @@ -323,7 +423,7 @@ namespace time { void Mutator<TI>::bind_to (TAR& target) const { - setVal_ = MutationPolicy<TI,TAR> ::buildChangeHandler (target); + setVal_ = Policy<TI,TI,TAR>::buildChangeHandler(target); //MutationPolicy<TI,TAR> ::buildChangeHandler (target); offset_ = MutationPolicy<Offset,TAR>::buildChangeHandler (target); nudge_ = MutationPolicy<int,TAR> ::buildChangeHandler (target); } From 7c5f18643b264247da67bdf32448d272f553df77 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 6 Jun 2011 03:17:42 +0200 Subject: [PATCH 120/496] cont. reworking based on that idea; refactor mutation base cases --- src/lib/time/control.hpp | 76 +++++++++++++++++++++++++++++++++------ src/lib/time/mutation.cpp | 38 ++++++-------------- src/lib/time/mutation.hpp | 51 ++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 38 deletions(-) diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp index 6d7b3ad73..7f257880b 100644 --- a/src/lib/time/control.hpp +++ b/src/lib/time/control.hpp @@ -161,12 +161,13 @@ namespace time { } }; + template<class TI, class TAR> struct Builder { static TI - buildChangedValue (TAR& target, TI const&) + buildChangedValue (TAR& target) { return TI(target); } @@ -175,9 +176,18 @@ namespace time { struct Builder<TimeSpan, TAR> { static TimeSpan - buildChangedValue (TAR& target, TimeSpan const& newVal) + buildChangedValue (TAR& target) { - return TimeSpan (target, newVal.duration()); + return TimeSpan (target, Duration::ZERO); /////////////TODO how to feed the "new value" duration???? + } + }; + template<> + struct Builder<TimeSpan, TimeSpan> + { + static TimeSpan + buildChangedValue (TimeSpan& target) + { + return target; } }; #ifdef LIB_TIME_TIMEQUQNT_H @@ -185,18 +195,27 @@ namespace time { struct Builder<QuTime, TAR> { static QuTime - buildChangedValue (TAR& target, QuTime const&) + buildChangedValue (TAR& target) { return QuTime (target ,getDefaultGridFallback() //////////////////TICKET #810 ); } }; + template<> + struct Builder<QuTime, QuTime> + { + static QuTime + buildChangedValue (QuTime& target) + { + return target; + } + }; #endif template<class TI, class TAR> struct Adap - : Mutabor<TI> + : Mutator<TI> , Builder<TI,TAR> { @@ -204,12 +223,29 @@ namespace time { imposeValueChange (TAR& target, TI const& newVal) { imposeChange (target,newVal); - return buildChangedValue(target,newVal); + return buildChangedValue(target); + } + + static TI + imposeOffset (TAR& target, Offset const& off) + { + imposeChange (target,off); + return buildChangedValue(target); + } + + static TI + imposeNudge (TAR& target, int off_by_steps) + { + imposeChange (target,off_by_steps); + return buildChangedValue(target); } }; template<class TI, class SRC, class TAR> - struct Policy + struct Policy; + + template<class TI, class TAR> + struct Policy<TI,TI,TAR> { static function<TI(TI const&)> buildChangeHandler (TAR& target) @@ -218,6 +254,26 @@ namespace time { } }; + template<class TI, class TAR> + struct Policy<TI,Offset,TAR> + { + static function<TI(Offset const&)> + buildChangeHandler (TAR& target) + { + return bind (Adap<TI,TAR>::imposeOffset, ref(target), _1 ); + } + }; + + template<class TI, class TAR> + struct Policy<TI,int,TAR> + { + static function<TI(int)> + buildChangeHandler (TAR& target) + { + return bind (Adap<TI,TAR>::imposeNudge, ref(target), _1 ); + } + }; + template<class TI> TI @@ -423,9 +479,9 @@ namespace time { void Mutator<TI>::bind_to (TAR& target) const { - setVal_ = Policy<TI,TI,TAR>::buildChangeHandler(target); //MutationPolicy<TI,TAR> ::buildChangeHandler (target); - offset_ = MutationPolicy<Offset,TAR>::buildChangeHandler (target); - nudge_ = MutationPolicy<int,TAR> ::buildChangeHandler (target); + setVal_ = Policy<TI,TI,TAR> ::buildChangeHandler (target); + offset_ = Policy<TI,Offset,TAR>::buildChangeHandler (target); + nudge_ = Policy<TI,int,TAR> ::buildChangeHandler (target); } template<class TI> diff --git a/src/lib/time/mutation.cpp b/src/lib/time/mutation.cpp index d96c96419..a6fd96e50 100644 --- a/src/lib/time/mutation.cpp +++ b/src/lib/time/mutation.cpp @@ -48,17 +48,6 @@ namespace time { Mutation::~Mutation() { } // emit VTable here.... - /** @internal actually force a change - * into a target time entity to mutate. - * Mutation is declared fried to TimeValue - * and thus is allowed to influence the basic - * value stored in each time entity - */ - TimeValue& - Mutation::imposeChange (TimeValue& target, TimeValue const& valueToSet) - { - return target = valueToSet; - } @@ -157,14 +146,14 @@ namespace time { virtual void change (Duration& target) const { - imposeChange (target, TimeVar(target)+=adjustment_); + imposeChange (target, adjustment_); } virtual void change (TimeSpan& target) const { - imposeChange (target, TimeVar(target)+=adjustment_); + imposeChange (target, adjustment_); } @@ -173,7 +162,7 @@ namespace time { virtual void change (QuTime& target) const { - imposeChange (target, TimeVar(target)+=adjustment_); + imposeChange (target, adjustment_); } @@ -205,7 +194,7 @@ namespace time { /** * concrete time value mutation: * nudge target value by the given number of 'steps', - * relative to either the given grid. + * relative to the given grid. */ class NudgeMutation : public ImposeOffsetMutation @@ -234,6 +223,8 @@ namespace time { * @note currently the natural grid is hard wired, * just interpreting the step parameter as * offset in seconds. + * @see mutation#imposeChange (TimeValue, int) + * @see mutation#imposeChange (QuTime, int) */ class NaturalNudgeMutation : public ClonableMutation @@ -243,31 +234,22 @@ namespace time { virtual void change (Duration& target) const { - imposeChange (target, TimeVar(target)+=Time(FSecs(steps_))); + imposeChange (target, steps_); } virtual void change (TimeSpan& target) const { - imposeChange (target, TimeVar(target)+=Time(FSecs(steps_))); + imposeChange (target, steps_); } - /** Special treatment: use the quantised time's own grid; - * retrieve the corresponding grid point, offset it by the step-parameter, - * then retrieve the corresponding time from the quantised time's - * underlying quantiser (grid). - * @note when the #steps_ parameter is zero, what happens here effectively - * is the materialisation of the quantised target time, i.e. making - * the quantisation explicit and storing the resulting value. */ + /** @note special treatment: use the quantised time's own grid */ virtual void change (QuTime& target) const { - PQuant const& grid (target); - int64_t originalGridPoint = grid->gridPoint(target); - int64_t adjustedGridPoint = originalGridPoint + steps_; - imposeChange (target, grid->timeOf (adjustedGridPoint)); + imposeChange (target, steps_); } diff --git a/src/lib/time/mutation.hpp b/src/lib/time/mutation.hpp index bbd3f9fe9..f6e2f3f5f 100644 --- a/src/lib/time/mutation.hpp +++ b/src/lib/time/mutation.hpp @@ -113,6 +113,9 @@ namespace time { protected: static TimeValue& imposeChange (TimeValue&, TimeValue const&); + static TimeValue& imposeChange (TimeValue&, Offset const&); + static TimeValue& imposeChange (TimeValue&, int); + static TimeValue& imposeChange (QuTime&, int); }; @@ -126,5 +129,53 @@ namespace time { #endif + + /* === implementing the actual changes === */ + + /** @internal actually force a change into a target time entity to mutate. + * Mutation is declared fried to TimeValue and thus is allowed to influence + * the basic value stored in each time entity + */ + inline TimeValue& + Mutation::imposeChange (TimeValue& target, TimeValue const& valueToSet) + { + return target = valueToSet; + } + + /** @internal variation to mutate a target time value by applying an offset */ + inline TimeValue& + Mutation::imposeChange (TimeValue& target, Offset const& offset) + { + return imposeChange (target, TimeVar(target) += offset); + } + + /** @internal nudge a target time value by a step wise offset. + * The standard case uses a fixed offset of 1 second per step //////////////////TICKET #810 + */ + inline TimeValue& + Mutation::imposeChange (TimeValue& target, int steps) + { + return imposeChange (target, TimeVar(target) += Time(FSecs(steps))); + } + +#ifdef LIB_TIME_TIMEQUQNT_H + /** @internal Special treatment for quantised target values: + * use the quantised time's own grid; retrieve the corresponding grid point, + * offset it by the step-parameter, then retrieve the corresponding time from + * the quantised time's underlying quantiser (grid) and impose that as change. + * @note when the #steps parameter is zero, what happens here effectively + * is the materialisation of the quantised target time, i.e. making + * the quantisation explicit and storing the resulting value. */ + inline TimeValue& + Mutation::imposeChange (QuTime& target, int steps) + { + PQuant const& grid (target); + int64_t originalGridPoint = grid->gridPoint(target); + int64_t adjustedGridPoint = originalGridPoint + steps; + return imposeChange (target, grid->timeOf (adjustedGridPoint)); + } +#endif + + }} // lib::time #endif From 9bd96a1b192ed2fd83e606ea073bd969848619b3 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 8 Jun 2011 02:12:38 +0200 Subject: [PATCH 121/496] time-entities refactoring: Duration isn't an Offset removed that inheritance relation; it was a typical example of abusing inheritance and violated the Liscov substitution principle. It is sufficient to allow promotion of an offset into a Duration. Note: Duration is the time metric --- doc/devel/uml/fig142725.png | Bin 19348 -> 19835 bytes src/lib/time/control.hpp | 6 ++--- src/lib/time/lumitime.cpp | 4 +-- src/lib/time/quantiser.cpp | 2 +- src/lib/time/timevalue.hpp | 40 +++++++++++++++++++++++++---- uml/lumiera/128517 | 19 ++++---------- uml/lumiera/142725.diagram | 50 +++++++++++++++++++----------------- uml/lumiera/5.session | 3 +++ uml/lumiera/lumiera.prj | 2 +- wiki/renderengine.html | 19 +++++++++----- 10 files changed, 88 insertions(+), 57 deletions(-) diff --git a/doc/devel/uml/fig142725.png b/doc/devel/uml/fig142725.png index 6e8c570d81b164a4ab1fc290b50e592f5e7dca80..91c7958764689a72584a4104d39229483dce8765 100644 GIT binary patch literal 19835 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 QCNs!uF=^2v5#9Iy7uN@ltN;K2 literal 19348 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 W>J+|Q0G_r5c_}6*S|F_d{{H~`be&KD diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp index 7f257880b..69ae4ec02 100644 --- a/src/lib/time/control.hpp +++ b/src/lib/time/control.hpp @@ -178,7 +178,7 @@ namespace time { static TimeSpan buildChangedValue (TAR& target) { - return TimeSpan (target, Duration::ZERO); /////////////TODO how to feed the "new value" duration???? + return TimeSpan (target, Duration::NIL); /////////////TODO how to feed the "new value" duration???? } }; template<> @@ -327,13 +327,13 @@ namespace time { TimeSpan Mutator<TimeSpan>::imposeOffset (TimeValue& target, Offset const& off) { - return TimeSpan (Mutation::imposeChange (target, TimeVar(target)+off), Duration::ZERO); + return TimeSpan (Mutation::imposeChange (target, TimeVar(target)+off), Duration::NIL); } template<> TimeSpan Mutator<TimeSpan>::imposeNudge (TimeValue& target, int off_by_steps) { - return TimeSpan (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps))), Duration::ZERO); + return TimeSpan (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps))), Duration::NIL); } diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index 00d3e72b9..978e8190f 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -121,12 +121,12 @@ namespace time { /** duration of the given number of frames */ Duration::Duration (ulong count, FrameRate const& fps) - : Offset(TimeValue (count? lumiera_frame_duration (fps/count) : _raw(Duration::NIL))) + : TimeValue (count? lumiera_frame_duration (fps/count) : _raw(Duration::NIL)) { } /** constant to indicate "no duration" */ - const Duration Duration::NIL = Offset(TimeValue(0)); + const Duration Duration::NIL (Time::ZERO); diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index 61103008c..5651df3b2 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -200,7 +200,7 @@ namespace time { { Time gt(gridTime); TimeVar timePoint = gt + origin_; - timePoint += gridOffset * raster_; + timePoint += gridOffset * Offset(raster_); return timePoint; } diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index fc5c9d467..5535beed1 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -317,24 +317,24 @@ namespace time { * possibility to send a \em Mutation message. */ class Duration - : public Offset + : public TimeValue { /// direct assignment prohibited Duration& operator= (Duration const&); public: Duration (Offset const& distance) - : Offset(distance.abs()) + : TimeValue(distance.abs()) { } explicit Duration (TimeValue const& timeSpec) - : Offset(Offset(timeSpec).abs()) + : TimeValue(Offset(timeSpec).abs()) { } explicit Duration (FSecs const& timeSpan_in_secs) - : Offset(Offset(Time(timeSpan_in_secs)).abs()) + : TimeValue(Offset(Time(timeSpan_in_secs)).abs()) { } Duration (TimeSpan const& interval); @@ -343,7 +343,37 @@ namespace time { static const Duration NIL; void accept (Mutation const&); + + /// Supporting backwards use as offset + Offset operator- () const; + }; + + //-- support using a Duration to build offsets --------------- + + inline Duration + operator+ (Duration const& base, Duration const& toAdd) + { + return Offset(base) + Offset(toAdd); + } + + inline Offset + operator* (int factor, Duration const& dur) + { + return factor * Offset(dur); + } + + inline Offset + operator* (Duration const& dur, int factor) + { + return factor*dur; + } + + inline Offset + Duration::operator- () const + { + return -1 * (*this); + } @@ -495,7 +525,7 @@ namespace time { inline Duration::Duration (TimeSpan const& interval) - : Offset(interval.duration()) + : TimeValue(interval.duration()) { } inline diff --git a/uml/lumiera/128517 b/uml/lumiera/128517 index 21aba95a2..9a4a726c7 100644 --- a/uml/lumiera/128517 +++ b/uml/lumiera/128517 @@ -1,6 +1,6 @@ format 58 "CommonLib" // CommonLib - revision 24 + revision 25 modified_by 5 "hiv" // class settings //class diagram settings @@ -916,12 +916,12 @@ ${inlines} idl_decl "" explicit_switch_type "" - classrelation 210437 // <generalisation> - relation 199557 ---|> + classrelation 215429 // <generalisation> + relation 204293 ---|> a public cpp default "${type}" - classrelation_ref 210437 // <generalisation> - b parent class_ref 172165 // Offset + classrelation_ref 215429 // <generalisation> + b parent class_ref 168709 // TimeValue end end @@ -1074,14 +1074,6 @@ ${inlines} b parent class_ref 168709 // TimeValue end - classrelation 208389 // val (<unidirectional association>) - relation 197509 ---> - a role_name "val" multiplicity "1" protected - cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; -" - classrelation_ref 208389 // val (<unidirectional association>) - b multiplicity "*" parent class_ref 134917 // Time - end end class 170501 "QuTimeSpan" @@ -1126,7 +1118,6 @@ ${inlines} classrelation_ref 208645 // <dependency> b parent class_ref 170629 // TCode end - end class 170757 "Format" diff --git a/uml/lumiera/142725.diagram b/uml/lumiera/142725.diagram index 18a84f06e..57a840345 100644 --- a/uml/lumiera/142725.diagram +++ b/uml/lumiera/142725.diagram @@ -30,11 +30,7 @@ classcanvas 130565 class_ref 169221 // TimeGrid end classcanvas 130949 class_ref 170373 // TimeVar 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 114 118 2000 -end -classcanvas 131589 class_ref 170501 // QuTimeSpan - 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 103 361 2000 + xyz 170 118 2000 end classcanvas 131973 class_ref 170629 // TCode 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 @@ -54,7 +50,7 @@ classcanvas 133253 class_ref 171013 // CompoundGrid end classcanvas 133509 class_ref 172165 // Offset 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 46 118 2000 + xyz 102 118 2000 end classcanvas 135045 class_ref 137093 // Meta 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 @@ -82,6 +78,14 @@ classcanvas 137349 class_ref 173829 // Digxel 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 191 548 2000 end +classcanvas 138117 class_ref 170501 // QuTimeSpan + 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 104 361 2000 +end +textcanvas 138373 "time metric" + xyzwh 97 182 2000 50 13 +textcanvas 138501 "mutable" + xyzwh 178 157 2000 38 13 relationcanvas 128517 relation_ref 195205 // <generalisation> geometry VHV unfixed from ref 128005 z 1999 to point 269 88 @@ -108,22 +112,12 @@ relationcanvas 130821 relation_ref 197253 // <generalisation> end relationcanvas 131077 relation_ref 197381 // <generalisation> geometry VHV - from ref 130949 z 1999 to point 138 88 + from ref 130949 z 1999 to point 194 88 line 131205 z 1999 to point 66 88 line 131333 z 1999 to ref 128261 no_role_a no_role_b no_multiplicity_a no_multiplicity_b end -relationcanvas 131461 relation_ref 197509 // <unidirectional association> - from ref 130949 z 1999 to ref 128005 - role_a_pos 225 120 3000 no_role_b - multiplicity_a_pos 232 143 3000 multiplicity_b_pos 176 143 3000 -end -relationcanvas 131717 relation_ref 197637 // <generalisation> - from ref 131589 z 1999 to ref 128133 - no_role_a no_role_b - no_multiplicity_a no_multiplicity_b -end relationcanvas 132741 relation_ref 198149 // <directional aggregation> decenter_end 595 from ref 130181 z 1999 stereotype "<<provide>>" xyz 360 413 3000 to ref 132101 @@ -141,13 +135,11 @@ relationcanvas 133381 relation_ref 198405 // <generalisation> no_role_a no_role_b no_multiplicity_a no_multiplicity_b end -relationcanvas 133637 relation_ref 199557 // <generalisation> - from ref 128389 z 1999 to ref 133509 - no_role_a no_role_b - no_multiplicity_a no_multiplicity_b -end relationcanvas 133765 relation_ref 199685 // <generalisation> - from ref 133509 z 1999 to ref 128261 + geometry VHV + from ref 133509 z 1999 to point 122 88 + line 137861 z 1999 to point 66 88 + line 137989 z 1999 to ref 128261 no_role_a no_role_b no_multiplicity_a no_multiplicity_b end @@ -162,7 +154,7 @@ relationcanvas 134661 relation_ref 199941 // <unidirectional association> geometry VHr from ref 128133 z 1999 to point 66 284 line 134789 z 1999 to ref 128389 - role_a_pos 75 269 3000 no_role_b + role_a_pos 83 270 3000 no_role_b multiplicity_a_pos 52 226 3000 no_multiplicity_b end relationcanvas 135301 relation_ref 200069 // <generalisation> @@ -212,4 +204,14 @@ relationcanvas 137477 relation_ref 201989 // <directional composition> role_a_pos 245 554 3000 no_role_b multiplicity_a_pos 245 573 3000 no_multiplicity_b end +relationcanvas 137733 relation_ref 204293 // <generalisation> + from ref 128389 z 1999 to ref 128261 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 138245 relation_ref 197637 // <generalisation> + from ref 138117 z 1999 to ref 128133 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end end diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session index b61f08080..4e22d4c88 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -42,7 +42,10 @@ open 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 end diff --git a/uml/lumiera/lumiera.prj b/uml/lumiera/lumiera.prj index f1dc5de9f..35fe3a3e2 100644 --- a/uml/lumiera/lumiera.prj +++ b/uml/lumiera/lumiera.prj @@ -1,6 +1,6 @@ format 58 "lumiera" - revision 68 + revision 69 modified_by 5 "hiv" cpp_root_dir "../../src/" diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 2105f0406..1c1558952 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6870,23 +6870,28 @@ Thus the possibly mutalble time entities get an {{{accept(time::Mutation&)}} 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. </pre> </div> -<div title="TimeQuant" modifier="Ichthyostega" modified="201101232031" created="201012181753" tags="Concepts Player spec img discuss draft" changecount="50"> +<div title="TimeQuant" modifier="Ichthyostega" modified="201106080000" created="201012181753" tags="Concepts Player spec img discuss draft" changecount="52"> <pre>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 <prg@ichthyostega.de> Date: Wed, 8 Jun 2011 03:35:48 +0200 Subject: [PATCH 122/496] 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<class T> + struct isDurationZ + { + static const bool value = is_sameType<T,Duration>::value; + }; + template<class T> + struct isTimeSpanZ + { + static const bool value = is_sameType<T,TimeSpan>::value; + }; + template<class T> + struct isQuTime + { + static const bool value = is_sameType<T,QuTime>::value; + }; + + template<class T> + struct isSpecialCase + { + static const bool value = isDurationZ<T>::value; + }; + + template<class T> + inline bool + isDuration() + { + return is_sameType<T,Duration>::value; + } + + template<class T> + inline bool + isTimeSpan() + { + return is_sameType<T,TimeSpan>::value; + } + } + + template<class TI, class TAR> struct Builder { static TI - buildChangedValue (TAR& target) + buildChangedValue (TAR const& target) { return TI(target); } @@ -176,12 +217,21 @@ namespace time { struct Builder<TimeSpan, TAR> { 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<TimeSpan, Duration> + { + static TimeSpan + buildChangedValue (Duration const& targetDuration) + { + return TimeSpan (Time::ZERO, targetDuration); + } + }; + template<> struct Builder<TimeSpan, TimeSpan> { static TimeSpan @@ -219,41 +269,112 @@ namespace time { , Builder<TI,TAR> { + template<class SRC> 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<TimeSpan>::imposeChange (target.duration(), change); + return Builder<TI,TimeSpan>::buildChangedValue(target); + } + + static TimeSpan + mutateTimeSpan (TimeSpan& target, TimeSpan const& change) + { + Mutator<TimeSpan>::imposeChange (target.duration(), change.duration()); + Mutator<TimeSpan>::imposeChange (target,change.start()); + return Builder<TimeSpan,TimeSpan>::buildChangedValue(target); + } + + static TI + dontChange (TAR& target) + { + // note: not touching the target return buildChangedValue(target); } }; + + template<class TI, class SRC, class TAR> - struct Policy; - - template<class TI, class TAR> - struct Policy<TI,TI,TAR> + struct Policy { - static function<TI(TI const&)> + static function<TI(SRC const&)> buildChangeHandler (TAR& target) { - return bind (Adap<TI,TAR>::imposeValueChange, ref(target), _1 ); + return bind (Adap<TI,TAR>::template processValueChange<SRC>, ref(target), _1 ); } }; + + // special treatment of Durations as target... + + template<class TI, class SRC> + struct Policy<TI,SRC,Duration> + { + static function<TI(SRC const&)> + buildChangeHandler (Duration& target) + { + return bind (Adap<TI,Duration>::dontChange, ref(target) ); + } + }; + + template<class TI> + struct Policy<TI,Duration,Duration> + { + static function<TI(Duration const&)> + buildChangeHandler (Duration& target) + { + return bind (Adap<TI,Duration>::template processValueChange<Duration>, ref(target), _1 ); + } + }; + + template<class TI> + struct Policy<TI,TimeSpan,Duration> + { + static function<TI(TimeSpan const&)> + buildChangeHandler (Duration& target) + { + return bind (Adap<TI,Duration>::useLengthAsChange, ref(target), _1 ); + } + }; + + // special treatment for TimeSpan values... + + template<class TI> + struct Policy<TI,Duration,TimeSpan> + { + static function<TI(Duration const&)> + buildChangeHandler (TimeSpan& target) + { + return bind (Adap<TI,TimeSpan>::mutateLength, ref(target), _1 ); + } + }; + + template<> + struct Policy<TimeSpan,TimeSpan,TimeSpan> + { + static function<TimeSpan(TimeSpan const&)> + buildChangeHandler (TimeSpan& target) + { + return bind (Adap<TimeSpan,TimeSpan>::mutateTimeSpan, ref(target), _1 ); + } + }; + + +/* template<class TI, class TAR> struct Policy<TI,Offset,TAR> { @@ -273,7 +394,7 @@ namespace time { return bind (Adap<TI,TAR>::imposeNudge, ref(target), _1 ); } }; - +*/ template<class TI> TI @@ -371,33 +492,6 @@ namespace time { } #endif //(End)quantisation special case - - namespace { // metaprogramming helpers to pick the suitable Instantiation... - - template<class T> - struct isDuration - { - static const bool value = is_sameType<T,Duration>::value; - }; - template<class T> - struct isTimeSpan - { - static const bool value = is_sameType<T,TimeSpan>::value; - }; - template<class T> - struct isQuTime - { - static const bool value = is_sameType<T,QuTime>::value; - }; - - template<class T> - struct isSpecialCase - { - static const bool value = isDuration<T>::value; - }; - - } - template<class TI> template<class TAR> struct Mutator<TI>::MutationPolicy< typename disable_if< isSpecialCase<TAR>, @@ -437,7 +531,7 @@ namespace time { template<class TI> template<class TAR> - struct Mutator<TI>::MutationPolicy< typename enable_if< isDuration<TAR>, + struct Mutator<TI>::MutationPolicy< typename enable_if< isDurationZ<TAR>, TI>::type, TAR> { static function<TI(TI const&)> 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 <prg@ichthyostega.de> Date: Fri, 10 Jun 2011 18:19:02 +0200 Subject: [PATCH 123/496] 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<TI> 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<class TAR, class SRC> 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<class SRC> 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<class TAR> 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<class TAR> 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<class TAR> 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<class TAR, class SRC> + void + ____verify_notification (TAR const& target, TestListener<SRC> const& follower) + { + CHECK (target == follower.receivedValue()); + } + void + ____verify_notification (TimeSpan const& targetTimeSpan, TestListener<Duration> const& follower) + { + CHECK (follower.receivedValue() == targetTimeSpan.duration()); + } + void + ____verify_notification (Duration const& targetDuration, TestListener<TimeSpan> const& follower) + { + CHECK (Time::ZERO == follower.receivedValue()); + CHECK (targetDuration == follower.receivedValue().duration()); } @@ -313,34 +352,47 @@ namespace test{ Control<SRC> controller; TAR target = TestTarget<TAR>::build(org); + SRC change = TestChange<SRC>::prepareChangeValue(c); + TestListener<SRC> follower(change); + + controller.connectChangeNotification(follower); target.accept (controller); - SRC change = TestChange<SRC>::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<int>::max(); int minInt = std::numeric_limits<int>::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 <prg@ichthyostega.de> Date: Sat, 11 Jun 2011 20:20:20 +0200 Subject: [PATCH 124/496] 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<class T> + struct canMutateDuration + { + static const bool value = is_sameType<T,Duration>::value + || is_sameType<T,Offset>::value + || is_sameType<T,int>::value; + }; + } + template<class TI, class SRC> - struct Policy<TI,SRC,Duration> + struct Policy<TI,SRC, typename disable_if< canMutateDuration<SRC>, + Duration>::type> { static function<TI(SRC const&)> buildChangeHandler (Duration& target) @@ -331,15 +342,15 @@ namespace time { } }; - template<class TI> - struct Policy<TI,Duration,Duration> - { - static function<TI(Duration const&)> - buildChangeHandler (Duration& target) - { - return bind (Adap<TI,Duration>::template processValueChange<Duration>, ref(target), _1 ); - } - }; +// template<class TI> +// struct Policy<TI,Duration,Duration> +// { +// static function<TI(Duration const&)> +// buildChangeHandler (Duration& target) +// { +// return bind (Adap<TI,Duration>::template processValueChange<Duration>, ref(target), _1 ); +// } +// }; template<class TI> struct Policy<TI,TimeSpan,Duration> @@ -573,9 +584,9 @@ namespace time { void Mutator<TI>::bind_to (TAR& target) const { - setVal_ = Policy<TI,TI,TAR> ::buildChangeHandler (target); + setVal_ = Policy<TI,TI, TAR>::buildChangeHandler (target); offset_ = Policy<TI,Offset,TAR>::buildChangeHandler (target); - nudge_ = Policy<TI,int,TAR> ::buildChangeHandler (target); + nudge_ = Policy<TI,int, TAR>::buildChangeHandler (target); } template<class TI> 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<long> 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 <prg@ichthyostega.de> Date: Sat, 11 Jun 2011 21:10:47 +0200 Subject: [PATCH 125/496] 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<class T> + inline bool + isDuration() + { + return is_sameType<T,Duration>::value; + } + + template<class T> + inline bool + isQuTime() + { + return is_sameType<T,QuTime>::value; + } + + template<class T> + 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<class TAR> 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<TAR>()) + { + CHECK (target == org, "Logic error: Duration was changed by time value"); + } + else + if (isQuTime<SRC>()) + { + CHECK (target != org); + CHECK (target == materialise(change)); + } + else + { + CHECK (target != org); + CHECK (target == change); + } } - template<class SRC> - 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<SRC> const& follower) { - CHECK (target == follower.receivedValue()); + if (isDuration<SRC>()) + { + CHECK (Duration::NIL == follower.receivedValue()); + } + else + if (isQuTime<TAR>()) + { + CHECK (materialise (target) == follower.receivedValue()); + } + else + { + CHECK (target == follower.receivedValue()); + } } void ____verify_notification (TimeSpan const& targetTimeSpan, TestListener<Duration> const& follower) @@ -326,6 +376,11 @@ namespace test{ CHECK (follower.receivedValue() == targetTimeSpan.duration()); } void + ____verify_notification (Duration const& target, TestListener<Duration> const& follower) + { + CHECK (target == follower.receivedValue()); + } + void ____verify_notification (Duration const& targetDuration, TestListener<TimeSpan> const& follower) { CHECK (Time::ZERO == follower.receivedValue()); From fec2d25b52d596c12bfdcd884b85369ccf91ffa8 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 12 Jun 2011 01:11:15 +0200 Subject: [PATCH 126/496] 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<T,TimeSpan>::value; } + + template<class T> + inline T const& + maybeMaterialise (T const& non_grid_aligned_TimeValue) + { + return non_grid_aligned_TimeValue; + } +#ifdef LIB_TIME_TIMEQUQNT_H + inline QuTime + maybeMaterialise (QuTime const& alignedTime) + { + PQuant const& grid(alignedTime); + return QuTime(grid->materialise(alignedTime), grid); + } +#endif } template<class TI, class TAR> struct Builder { - static TI buildChangedValue (TAR const& target) { @@ -235,7 +249,7 @@ namespace time { struct Builder<TimeSpan, TimeSpan> { static TimeSpan - buildChangedValue (TimeSpan& target) + buildChangedValue (TimeSpan const& target) { return target; } @@ -245,7 +259,7 @@ namespace time { struct Builder<QuTime, TAR> { static QuTime - buildChangedValue (TAR& target) + buildChangedValue (TAR const& target) { return QuTime (target ,getDefaultGridFallback() //////////////////TICKET #810 @@ -256,7 +270,7 @@ namespace time { struct Builder<QuTime, QuTime> { 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<T,Offset>::value || is_sameType<T,int>::value; }; + + template<class T> + struct canReceiveDuration + { + static const bool value = is_sameType<T,Duration>::value + || is_sameType<T,TimeSpan>::value; + }; + } template<class TI, class SRC> @@ -342,6 +364,23 @@ namespace time { } }; + template<class TAR> + struct Policy<Duration, typename disable_if< canReceiveDuration<TAR>, + Duration>::type, TAR> + { + static function<Duration(Duration const&)> + buildChangeHandler (TAR&) + { + return bind ( ignore_change_and_return_Zero ); + } + + static Duration + ignore_change_and_return_Zero() + { + return Duration::NIL; + } + }; + // template<class TI> // struct Policy<TI,Duration,Duration> // { 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<class T> 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<SRC>()) + { + CHECK (target == org, "Logic error: Duration used to change time value"); + } + else if (isQuTime<SRC>()) { 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<SRC>()) { - CHECK (Duration::NIL == follower.receivedValue()); + CHECK (materialise(target) == follower.receivedValue() + || Duration::NIL == follower.receivedValue() ); } else if (isQuTime<TAR>()) From f4d0d23e4800a3fd1932ce372871aecaf321fc0f Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 12 Jun 2011 02:05:24 +0200 Subject: [PATCH 127/496] 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 <boost/noncopyable.hpp> -//#include <iostream> -//#include <boost/operators.hpp> #include <boost/utility/enable_if.hpp> #include <tr1/functional> #include <vector> -//#include <string> 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<class TI> - 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 TI> class Mutator @@ -114,18 +108,6 @@ namespace time { typedef function<TI(Offset const&)> Ofsetter; typedef function<TI(int)> 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<class SRC, class TAR> - struct MutationPolicy; - - friend class Mutabor<TI>; - protected: mutable ValueSetter setVal_; mutable Ofsetter offset_; @@ -140,66 +122,84 @@ namespace time { ,error::LUMIERA_ERROR_UNCONNECTED); } - public: - // using default construction and copy template<class TAR> void bind_to (TAR& target) const; void unbind(); + // using default construction and copy }; + + + /** + * Implementation building block: propagate changes to listeners. + * The Propagator manages a set of callback signals, allowing to + * propagate notifications for changed Time values. + * + * There are no specific requirements on the acceptable listeners, + * besides exposing a function-call operator to feed the changed + * time value to. Both Mutator and Propagator employ one primary + * template parameter, which is the type of the time values + * to be fed in and propagated. + */ template<class TI> - struct Mutabor + class Propagator { + typedef function<void(TI const&)> ChangeSignal; + typedef std::vector<ChangeSignal> ListenerList; - static void - imposeChange (TimeValue& target, TI const& newVal) + ListenerList listeners_; + + public: + /** install notification receiver */ + template<class SIG> + void + attach (SIG const& toNotify) { - Mutator<TI>::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<class T> - struct isDurationZ - { - static const bool value = is_sameType<T,Duration>::value; - }; - template<class T> - struct isTimeSpanZ - { - static const bool value = is_sameType<T,TimeSpan>::value; - }; - template<class T> - struct isQuTime - { - static const bool value = is_sameType<T,QuTime>::value; - }; - - template<class T> - struct isSpecialCase - { - static const bool value = isDurationZ<T>::value; - }; + namespace { // metaprogramming helpers to pick suitable implementation branch... template<class T> inline bool isDuration() - { - return is_sameType<T,Duration>::value; - } + { + return is_sameType<T,Duration>::value; + } template<class T> inline bool isTimeSpan() - { - return is_sameType<T,TimeSpan>::value; - } + { + return is_sameType<T,TimeSpan>::value; + } template<class T> 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<class TI, class TAR> - struct Adap + struct Link : Mutator<TI> , Builder<TI,TAR> { @@ -328,7 +329,7 @@ namespace time { static function<TI(SRC const&)> buildChangeHandler (TAR& target) { - return bind (Adap<TI,TAR>::template processValueChange<SRC>, ref(target), _1 ); + return bind (Link<TI,TAR>::template processValueChange<SRC>, ref(target), _1 ); } }; @@ -350,9 +351,9 @@ namespace time { static const bool value = is_sameType<T,Duration>::value || is_sameType<T,TimeSpan>::value; }; - } + template<class TI, class SRC> struct Policy<TI,SRC, typename disable_if< canMutateDuration<SRC>, Duration>::type> @@ -360,7 +361,7 @@ namespace time { static function<TI(SRC const&)> buildChangeHandler (Duration& target) { - return bind (Adap<TI,Duration>::dontChange, ref(target) ); + return bind (Link<TI,Duration>::dontChange, ref(target) ); } }; @@ -381,23 +382,13 @@ namespace time { } }; -// template<class TI> -// struct Policy<TI,Duration,Duration> -// { -// static function<TI(Duration const&)> -// buildChangeHandler (Duration& target) -// { -// return bind (Adap<TI,Duration>::template processValueChange<Duration>, ref(target), _1 ); -// } -// }; - template<class TI> struct Policy<TI,TimeSpan,Duration> { static function<TI(TimeSpan const&)> buildChangeHandler (Duration& target) { - return bind (Adap<TI,Duration>::useLengthAsChange, ref(target), _1 ); + return bind (Link<TI,Duration>::useLengthAsChange, ref(target), _1 ); } }; @@ -409,7 +400,7 @@ namespace time { static function<TI(Duration const&)> buildChangeHandler (TimeSpan& target) { - return bind (Adap<TI,TimeSpan>::mutateLength, ref(target), _1 ); + return bind (Link<TI,TimeSpan>::mutateLength, ref(target), _1 ); } }; @@ -419,201 +410,11 @@ namespace time { static function<TimeSpan(TimeSpan const&)> buildChangeHandler (TimeSpan& target) { - return bind (Adap<TimeSpan,TimeSpan>::mutateTimeSpan, ref(target), _1 ); + return bind (Link<TimeSpan,TimeSpan>::mutateTimeSpan, ref(target), _1 ); } }; -/* - template<class TI, class TAR> - struct Policy<TI,Offset,TAR> - { - static function<TI(Offset const&)> - buildChangeHandler (TAR& target) - { - return bind (Adap<TI,TAR>::imposeOffset, ref(target), _1 ); - } - }; - - template<class TI, class TAR> - struct Policy<TI,int,TAR> - { - static function<TI(int)> - buildChangeHandler (TAR& target) - { - return bind (Adap<TI,TAR>::imposeNudge, ref(target), _1 ); - } - }; -*/ - - template<class TI> - TI - Mutator<TI>::imposeValueChange (TimeValue& target, TI const& newVal) - { - return TI (Mutation::imposeChange (target,newVal)); - } - template<class TI> - TI - Mutator<TI>::imposeOffset (TimeValue& target, Offset const& off) - { - return TI (Mutation::imposeChange (target, TimeVar(target)+off)); - } - template<class TI> - TI - Mutator<TI>::imposeNudge (TimeValue& target, int off_by_steps) - { - return TI (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps)))); //////////////////TICKET #810 - } - - // special cases... - template<class TI> - TI - Mutator<TI>::changeDuration (Duration&, TI const&) - { - return TI (Time::ZERO); - } - template<> - Duration - Mutator<Duration>::changeDuration (Duration& target, Duration const& changedDur) - { - return Duration (Mutation::imposeChange (target,changedDur)); - } - template<> - TimeSpan - Mutator<TimeSpan>::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<TimeSpan>::imposeValueChange (TimeValue& target, TimeSpan const& newVal) - { - Mutation::imposeChange (target,newVal); - return newVal; - } - template<> - TimeSpan - Mutator<TimeSpan>::imposeOffset (TimeValue& target, Offset const& off) - { - return TimeSpan (Mutation::imposeChange (target, TimeVar(target)+off), Duration::NIL); - } - template<> - TimeSpan - Mutator<TimeSpan>::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<QuTime>::changeDuration (Duration&, QuTime const& irrelevantChange) - { - return QuTime (Time::ZERO, PQuant(irrelevantChange)); - } - template<> - QuTime - Mutator<QuTime>::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<QuTime>::imposeOffset (TimeValue& target, Offset const& off) - { - return QuTime (Mutation::imposeChange (target, TimeVar(target)+off) - ,getDefaultGridFallback() //////////////////TICKET #810 - ); - } - template<> - QuTime - Mutator<QuTime>::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<class TI> - template<class TAR> - struct Mutator<TI>::MutationPolicy< typename disable_if< isSpecialCase<TAR>, - TI>::type, TAR> - { - static function<TI(TI const&)> - buildChangeHandler (TAR& target) - { - return bind (Mutator<TI>::imposeValueChange, ref(target), _1 ); - } - }; - - template<class TI> - template<class TAR> - struct Mutator<TI>::MutationPolicy<Offset, TAR> - { - static function<TI(Offset const&)> - buildChangeHandler (TAR& target) - { - return bind (Mutator<TI>::imposeOffset, ref(target), _1 ); - } - }; - - template<class TI> - template<class TAR> - struct Mutator<TI>::MutationPolicy<int, TAR> - { - static function<TI(int)> - buildChangeHandler (TAR& target) - { - return bind (Mutator<TI>::imposeNudge, ref(target), _1 ); - } - }; - - - //special cases for changing Durations.... - - template<class TI> - template<class TAR> - struct Mutator<TI>::MutationPolicy< typename enable_if< isDurationZ<TAR>, - TI>::type, TAR> - { - static function<TI(TI const&)> - buildChangeHandler (Duration& target) - { - return bind (Mutator<TI>::changeDuration, ref(target), _1 ); - } - }; - - // -// template<class TI> -// template<class TAR> -// struct Mutator<TI>::MutationPolicy<TI, TAR> -// { -// static function<TimeValue(TimeValue const&)> -// buildChangeHandler (Duration& target) -// { -// return bind (Mutator<TI>::changeDuration, ref(target), _1 ); -// } -// }; - -// template<class TI> -// template<> -// struct Mutator<TI>::MutationPolicy<int, Duration> -// { -// static function<TI(int)> -// buildChangeHandler (Duration& target) -// { -// return bind (Mutator<TI>::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 TI> - class Propagator - { - typedef function<void(TI const&)> ChangeSignal; - typedef std::vector<ChangeSignal> ListenerList; - - ListenerList listeners_; - - public: - /** install notification receiver */ - template<class SIG> - void - attach (SIG const& toNotify) - { - ChangeSignal newListener (ref(toNotify)); - listeners_.push_back (newListener); - } - - /** disconnect any observers */ - void - disconnnect() - { - listeners_.clear(); - } - - /** publish a change */ - TI - operator() (TI const& changedVal) const - { - typedef typename ListenerList::const_iterator Iter; - Iter p = listeners_.begin(); - Iter e = listeners_.end(); - - for ( ; p!=e; ++p ) - (*p) (changedVal); - return changedVal; - } - }; } /** - * 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<class TI> void From 2099ecbcac6d09a58ed19753db6a7b29cd929b23 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 12 Jun 2011 19:03:12 +0200 Subject: [PATCH 128/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file control-impl.hpp + ** Implementation building blocks for time modification and propagation. + ** The time::Control element allows to impose modifications to a connected + ** time value entity and at the same time publish the changes to registered + ** listeners. Due to the various flavours of actual time value entities, this + ** is a complex undertaking, which is implemented here based on policies and + ** template metaprogramming. This header/include defines two building blocks: + ** - the actual Mutator to apply the changes to the target entity + ** - a Propagator to register listeners and forward the changes. + ** + ** \par implementation technique + ** + ** The Mutator uses functor objects to encapsulate the actual modification + ** operations. When attaching to a target time entity to be manipulated, these + ** functor objects will be configured by binding them to the appropriate + ** implementation function. And picking this actual implementation is done + ** through a time::mutation::Policy element, using the concrete time entity + ** types as template parameter. Thus, the actual implementation to be used + ** is determined by the compiler, through the template specialisations + ** contained in control-policy.hpp + ** + ** @note the header control-policy.hpp with the template specialisations + ** is included way down, after the class definitions. This is done + ** so for sake of readability + ** + ** @see TimeControl_test + ** + */ + +#ifndef LIB_TIME_CONTROL_IMPL_H +#define LIB_TIME_CONTROL_IMPL_H + +#include "lib/error.hpp" +#include "lib/time/mutation.hpp" +#include "lib/time/timevalue.hpp" + +#include <vector> + + +namespace lib { +namespace time { +namespace mutation { + + + + + /** + * Implementation building block: impose changes to a Time element. + * The purpose of the Mutator is to attach a target time entity, + * which then will be subject to any received value changes, + * offsets and grid nudging. The actual attachment is to be + * performed in a subclass, by using the Mutation interface. + * When attaching to a target, the Mutator will be outfitted + * with a set of suitable functors, incorporating the specific + * behaviour for the concrete combination of input changes + * ("source values") and target object type. This works by + * binding to the appropriate implementation functionality, + * guided by a templated policy class. After installing + * these functors, these decisions remains opaque and + * encapsulated within the functor objects, so the + * mutator object doesn't need to carry this + * type information on the interface + */ + template<class TI> + class Mutator + : public Mutation + { + typedef function<TI(TI const&)> ValueSetter; + typedef function<TI(Offset const&)> Ofsetter; + typedef function<TI(int)> Nudger; + + protected: + mutable ValueSetter setVal_; + mutable Ofsetter offset_; + mutable Nudger nudge_; + + void + ensure_isArmed() const + { + if (!setVal_) + throw error::State("feeding time/value change " + "while not (yet) connected to any target to change" + ,error::LUMIERA_ERROR_UNCONNECTED); + } + + + template<class TAR> + void bind_to (TAR& target) const; + + void unbind(); + + // using default construction and copy + }; + + + + /** + * Implementation building block: propagate changes to listeners. + * The Propagator manages a set of callback signals, allowing to + * propagate notifications for changed Time values. + * + * There are no specific requirements on the acceptable listeners, + * besides exposing a function-call operator to feed the changed + * time value to. Both Mutator and Propagator employ one primary + * template parameter, which is the type of the time values + * to be fed in and propagated. + */ + template<class TI> + class Propagator + { + typedef function<void(TI const&)> ChangeSignal; + typedef std::vector<ChangeSignal> ListenerList; + + ListenerList listeners_; + + public: + /** install notification receiver */ + template<class SIG> + void + attach (SIG const& toNotify) + { + ChangeSignal newListener (ref(toNotify)); + listeners_.push_back (newListener); + } + + /** disconnect any observers */ + void + disconnnect() + { + listeners_.clear(); + } + + /** publish a change */ + TI + operator() (TI const& changedVal) const + { + typedef typename ListenerList::const_iterator Iter; + Iter p = listeners_.begin(); + Iter e = listeners_.end(); + + for ( ; p!=e; ++p ) + (*p) (changedVal); + return changedVal; + } + + // using default construction and copy + }; + +}}} // lib::time::mutation + + +/* ===== Definition of actual operations ===== */ +#include "lib/time/control-policy.hpp" + + + + +template<class TI> +template<class TAR> +void +lib::time::mutation::Mutator<TI>::bind_to (TAR& target) const +{ + using lib::time::mutation::Policy; + + setVal_ = Policy<TI,TI, TAR>::buildChangeHandler (target); + offset_ = Policy<TI,Offset,TAR>::buildChangeHandler (target); + nudge_ = Policy<TI,int, TAR>::buildChangeHandler (target); +} + +template<class TI> +void +lib::time::mutation::Mutator<TI>::unbind() +{ + setVal_ = ValueSetter(); + offset_ = Ofsetter(); + nudge_ = Nudger(); +} + + +#endif 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file control-policy.hpp + ** Definition of special cases when imposing a change onto concrete time values. + ** The time::Control element allows to impose modifications to a connected + ** time value entity and at the same time publish the changes to registered + ** listeners. Due to the various flavours of actual time value entities, this + ** is a complex undertaking, which is implemented here based on policies and + ** template metaprogramming. + ** + ** The header control-impl.hpp defines the building blocks for time::Control + ** and then includes this header here to get the concrete template specialisations + ** for time::mutation::Policy. This policy class is templated by time entity types + ** - for \c TI, the \em nominal value type used on the time::Control interface + ** - for \c SRC, the actual type of values to impose as \em change + ** - for \c TAR, the target time value's type, receiving those changes. + ** + ** \par mutating a time value entity + ** + ** Actually imposing a change to the attached time value entity involves several + ** steps. Each of these steps might be adapted specifically, in accordance to + ** the concrete time value types involved. + ** - TimeValue, Time + ** - Offset + ** - Duration + ** - TimeSpan + ** - QuTime (grid aligned time value) + ** - QuTimeSpan (planned as of 6/2011) + ** + ** Moreover, the combination of types needs to be taken into account. For example, + ** it doesn't make sense to apply a Duration value as change to a TimeValue, which + ** has no duration (temporal extension). While a TimeSpan might receive a Duration + ** change, but behaves differently when imposing a Time to manipulate the starting + ** point of the time interval given by the TimeSpan. + ** + ** Incoming changes might be of any of the aforementioned types, and in addition, + ** we might receive \em nudging, which means to increment or decrement the target + ** time value in discrete steps. After maybe adapting these incoming change values, + ** they may be actually \em imposed to the target. In all cases, this is delegated + ** to the time::Mutation base class, which is declared fried to TimeValue and thus + ** has the exceptional ability to manipulate time values, which otherwise are defined + ** to be immutable. Additionally, these protected functions in the time::Mutation + ** baseclass also know how to handle \em nudge values, either by using the native + ** (embedded) time grid of a quantised time value, or by falling back to a standard + ** nudging grid, defined in the session context (TODO as of 6/2011). //////////////////////TICKET #810 + ** + ** After (maybe) imposing a change to the target, the change \em notification value + ** needs to be built. This is the time value entity to be forwarded to registered + ** listeners. This notification value has to be given as the type \c TI, in accordance + ** to the \c time::Control<TI> frontend definition used in the concrete usage situation. + ** As this type \c TI might be different to the actual target type, and again different + ** to the type of the change handed in, in some cases this involves a second conversion + ** step, to represent the current state of the target \c TAR in terms of the interface + ** type \c TI. + ** + ** \par changing quantised (grid aligned) time entities + ** + ** The time::Control element includes the capability to handle grid aligned time values, + ** both as target and as change/notification value. This ability is compiled in conditionally, + ** as including mutation.hpp causes several additional includes, which isn't desirable when + ** it comes just to changing plain time values. Thus, to get these additional specialisations, + ** the LIB_TIME_TIMEQUQNT_H header guard needs to be defined, which happens automatically + ** if lib/time/mutation.hpp is included prior to lib/time/control.hpp. + ** + ** As a special convention, any \em quantised (grid aligned) types involved in these + ** time changes will be \em materialised, whenever a type conversion happens. Generally + ** speaking, a quantised time value contains an (opaque) raw time value, plus a reference + ** to a time grid definition to apply. In this context \em materialising means actually + ** to apply this time grid to yield a grid aligned value. Thus, when using a quantised + ** value to impose as change (or to receive a change), its grid aligning nature + ** becomes effective, by applying the \em current definition of the grid to + ** create a fixed (materialised) time value, aligned to that current grid. + ** + ** @todo 6/2011 include all the special cases for QuTimeSpan ////////////////////TICKET #760 + ** + ** @see TimeControl_test + ** + */ + +#ifndef LIB_TIME_CONTROL_POLICY_H +#define LIB_TIME_CONTROL_POLICY_H + +#include "lib/meta/util.hpp" +#include "lib/time/mutation.hpp" +#include "lib/time/timevalue.hpp" + +#include <boost/utility/enable_if.hpp> +#include <tr1/functional> + + +namespace lib { +namespace time { + +namespace mutation { + + using boost::disable_if; + using lumiera::typelist::is_sameType; + using std::tr1::placeholders::_1; + using std::tr1::function; + using std::tr1::bind; + using std::tr1::ref; + + + + namespace { // metaprogramming helpers to pick a suitable implementation branch... + + template<class T> + inline bool + isDuration() + { + return is_sameType<T,Duration>::value; + } + + template<class T> + inline bool + isTimeSpan() + { + return is_sameType<T,TimeSpan>::value; + } + + template<class T> + inline T const& + maybeMaterialise (T const& non_grid_aligned_TimeValue) + { + return non_grid_aligned_TimeValue; + } + +#ifdef LIB_TIME_TIMEQUQNT_H + inline QuTime + maybeMaterialise (QuTime const& alignedTime) + { + PQuant const& grid(alignedTime); + return QuTime(grid->materialise(alignedTime), grid); + } +#endif //--quantised-time-support + } + + + /** + * Implementation policy: how to build a new + * notification value of type \c TI, given a + * target time value entity of type \c TAR + */ + template<class TI, class TAR> + struct Builder + { + static TI + buildChangedValue (TAR const& target) + { + return TI(target); + } + }; + template<class TAR> + struct Builder<TimeSpan, TAR> + { + static TimeSpan + buildChangedValue (TAR const& target) + { + return TimeSpan (target, Duration::NIL); + } + }; + template<> + struct Builder<TimeSpan, Duration> + { + static TimeSpan + buildChangedValue (Duration const& targetDuration) + { + return TimeSpan (Time::ZERO, targetDuration); + } + }; + template<> + struct Builder<TimeSpan, TimeSpan> + { + static TimeSpan + buildChangedValue (TimeSpan const& target) + { + return target; + } + }; +#ifdef LIB_TIME_TIMEQUQNT_H + template<class TAR> + struct Builder<QuTime, TAR> + { + static QuTime + buildChangedValue (TAR const& target) + { + return QuTime (target + ,getDefaultGridFallback() //////////////////TICKET #810 + ); + } + }; + template<> + struct Builder<QuTime, QuTime> + { + static QuTime + buildChangedValue (QuTime const& target) + { + return target; + } + }; +#endif //--quantised-time-support + + + + + /** + * Policy to tie the various detail policies together + * for providing actual value change operations. + * The standard case uses the (inherited) time::Mutation + * base implementation to impose a new value onto the + * target entity and then uses the Builder policy to + * create a notification value reflecting this change. + */ + template<class TI, class TAR> + struct Link + : Mutator<TI> + , Builder<TI,TAR> + { + + template<class SRC> + static TI + processValueChange (TAR& target, SRC const& change) ///< standard case: plain value change + { + imposeChange (target, maybeMaterialise(change)); + return buildChangedValue (maybeMaterialise(target)); + } + + static TI + useLengthAsChange (TAR& target, TimeSpan const& change) + { + return processValueChange(target, change.duration()); + } + + static TI + mutateLength (TimeSpan& target, Duration const& change) + { + Mutator<TimeSpan>::imposeChange (target.duration(), change); + return Builder<TI,TimeSpan>::buildChangedValue(target); + } + + static TimeSpan + mutateTimeSpan (TimeSpan& target, TimeSpan const& change) + { + Mutator<TimeSpan>::imposeChange (target.duration(), change.duration()); + Mutator<TimeSpan>::imposeChange (target,change.start()); + return Builder<TimeSpan,TimeSpan>::buildChangedValue(target); + } + + static TI + dontChange (TAR& target) ///< @note: not touching the target + { + return buildChangedValue(target); + } + }; + + + + /** + * Policy how to impose changes onto a connected target time value entity + * This policy will be parametrised with the concrete time entity types + * involved in the usage situation of time::Control. The purpose of the + * policy is to \em bind a functor object to the concrete implementation + * of the value change applicable for this combination of types. + * This functor will then be stored within time::Control and + * invoked for each actual value change. + * @param TI the nominal (interface) type of the change, propagated to listeners + * @param SRC the actual type of the change to be imposed + * @param TAR the actual type of the target entity to receive the changes + * @note typically either SRC is identical to TI, or it is an + * time::Offset, or an int for \em nudging the target + */ + template<class TI, class SRC, class TAR> + struct Policy + { + static function<TI(SRC const&)> + buildChangeHandler (TAR& target) + { + return bind (Link<TI,TAR>::template processValueChange<SRC>, ref(target), _1 ); + } + }; + + + // special treatment of Durations as target------------------------------------ + + namespace { + template<class T> + struct canMutateDuration + { + static const bool value = is_sameType<T,Duration>::value + || is_sameType<T,Offset>::value + || is_sameType<T,int>::value; + }; + + template<class T> + struct canReceiveDuration + { + static const bool value = is_sameType<T,Duration>::value + || is_sameType<T,TimeSpan>::value; + }; + } + + + /** + * special case: a Duration target value can't be changed by plain time values. + * This specialisation is \em not used (\c disable_if ) when the given change (SRC) + * is applicable to a Duration in a sensible way. We either define explicit + * specialisations (for TimeSpan) or fall back to the default in these cases. + */ + template<class TI, class SRC> + struct Policy<TI,SRC, typename disable_if< canMutateDuration<SRC>, + Duration>::type> + { + static function<TI(SRC const&)> + buildChangeHandler (Duration& target) + { + return bind (Link<TI,Duration>::dontChange, ref(target) ); + } + }; + + /** + * special case: a Duration change value can't be imposed to a plain time value. + * In these cases, we even propagate a Duration::ZERO to the listeners. + * As above, there are exceptions to this behaviour, where a Duration change + * can sensibly be applied. + */ + template<class TAR> + struct Policy<Duration, typename disable_if< canReceiveDuration<TAR>, + Duration>::type, TAR> + { + static function<Duration(Duration const&)> + buildChangeHandler (TAR&) + { + return bind ( ignore_change_and_return_Zero ); + } + + static Duration + ignore_change_and_return_Zero() + { + return Duration::NIL; + } + }; + + template<class TI> + struct Policy<TI,TimeSpan,Duration> + { + static function<TI(TimeSpan const&)> + buildChangeHandler (Duration& target) + { + return bind (Link<TI,Duration>::useLengthAsChange, ref(target), _1 ); + } + }; + + + // special treatment for TimeSpan values--------------------------------------- + + template<class TI> + struct Policy<TI,Duration,TimeSpan> + { + static function<TI(Duration const&)> + buildChangeHandler (TimeSpan& target) + { + return bind (Link<TI,TimeSpan>::mutateLength, ref(target), _1 ); + } + }; + + template<> + struct Policy<TimeSpan,TimeSpan,TimeSpan> + { + static function<TimeSpan(TimeSpan const&)> + buildChangeHandler (TimeSpan& target) + { + return bind (Link<TimeSpan,TimeSpan>::mutateTimeSpan, ref(target), _1 ); + } + }; + + + +}}} // namespace lib::time::mutation +#endif 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 <boost/utility/enable_if.hpp> -#include <tr1/functional> -#include <vector> 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 TI> - class Mutator - : public Mutation - { - typedef function<TI(TI const&)> ValueSetter; - typedef function<TI(Offset const&)> Ofsetter; - typedef function<TI(int)> Nudger; - - protected: - mutable ValueSetter setVal_; - mutable Ofsetter offset_; - mutable Nudger nudge_; - - void - ensure_isArmed() - { - if (!setVal_) - throw error::State("feeding time/value change " - "while not (yet) connected to any target to change" - ,error::LUMIERA_ERROR_UNCONNECTED); - } - - - template<class TAR> - void bind_to (TAR& target) const; - - void unbind(); - - // using default construction and copy - }; - - - - /** - * Implementation building block: propagate changes to listeners. - * The Propagator manages a set of callback signals, allowing to - * propagate notifications for changed Time values. - * - * There are no specific requirements on the acceptable listeners, - * besides exposing a function-call operator to feed the changed - * time value to. Both Mutator and Propagator employ one primary - * template parameter, which is the type of the time values - * to be fed in and propagated. - */ - template<class TI> - class Propagator - { - typedef function<void(TI const&)> ChangeSignal; - typedef std::vector<ChangeSignal> ListenerList; - - ListenerList listeners_; - - public: - /** install notification receiver */ - template<class SIG> - void - attach (SIG const& toNotify) - { - ChangeSignal newListener (ref(toNotify)); - listeners_.push_back (newListener); - } - - /** disconnect any observers */ - void - disconnnect() - { - listeners_.clear(); - } - - /** publish a change */ - TI - operator() (TI const& changedVal) const - { - typedef typename ListenerList::const_iterator Iter; - Iter p = listeners_.begin(); - Iter e = listeners_.end(); - - for ( ; p!=e; ++p ) - (*p) (changedVal); - return changedVal; - } - }; - - - - namespace { // metaprogramming helpers to pick suitable implementation branch... - - template<class T> - inline bool - isDuration() - { - return is_sameType<T,Duration>::value; - } - - template<class T> - inline bool - isTimeSpan() - { - return is_sameType<T,TimeSpan>::value; - } - - template<class T> - inline T const& - maybeMaterialise (T const& non_grid_aligned_TimeValue) - { - return non_grid_aligned_TimeValue; - } - -#ifdef LIB_TIME_TIMEQUQNT_H - inline QuTime - maybeMaterialise (QuTime const& alignedTime) - { - PQuant const& grid(alignedTime); - return QuTime(grid->materialise(alignedTime), grid); - } -#endif - } - - - template<class TI, class TAR> - struct Builder - { - static TI - buildChangedValue (TAR const& target) - { - return TI(target); - } - }; - template<class TAR> - struct Builder<TimeSpan, TAR> - { - static TimeSpan - buildChangedValue (TAR const& target) - { - return TimeSpan (target, Duration::NIL); - } - }; - template<> - struct Builder<TimeSpan, Duration> - { - static TimeSpan - buildChangedValue (Duration const& targetDuration) - { - return TimeSpan (Time::ZERO, targetDuration); - } - }; - template<> - struct Builder<TimeSpan, TimeSpan> - { - static TimeSpan - buildChangedValue (TimeSpan const& target) - { - return target; - } - }; -#ifdef LIB_TIME_TIMEQUQNT_H - template<class TAR> - struct Builder<QuTime, TAR> - { - static QuTime - buildChangedValue (TAR const& target) - { - return QuTime (target - ,getDefaultGridFallback() //////////////////TICKET #810 - ); - } - }; - template<> - struct Builder<QuTime, QuTime> - { - static QuTime - buildChangedValue (QuTime const& target) - { - return target; - } - }; -#endif - - template<class TI, class TAR> - struct Link - : Mutator<TI> - , Builder<TI,TAR> - { - - template<class SRC> - 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<TimeSpan>::imposeChange (target.duration(), change); - return Builder<TI,TimeSpan>::buildChangedValue(target); - } - - static TimeSpan - mutateTimeSpan (TimeSpan& target, TimeSpan const& change) - { - Mutator<TimeSpan>::imposeChange (target.duration(), change.duration()); - Mutator<TimeSpan>::imposeChange (target,change.start()); - return Builder<TimeSpan,TimeSpan>::buildChangedValue(target); - } - - static TI - dontChange (TAR& target) - { - // note: not touching the target - return buildChangedValue(target); - } - }; - - - - template<class TI, class SRC, class TAR> - struct Policy - { - static function<TI(SRC const&)> - buildChangeHandler (TAR& target) - { - return bind (Link<TI,TAR>::template processValueChange<SRC>, ref(target), _1 ); - } - }; - - - // special treatment of Durations as target... - - namespace { - template<class T> - struct canMutateDuration - { - static const bool value = is_sameType<T,Duration>::value - || is_sameType<T,Offset>::value - || is_sameType<T,int>::value; - }; - - template<class T> - struct canReceiveDuration - { - static const bool value = is_sameType<T,Duration>::value - || is_sameType<T,TimeSpan>::value; - }; - } - - - template<class TI, class SRC> - struct Policy<TI,SRC, typename disable_if< canMutateDuration<SRC>, - Duration>::type> - { - static function<TI(SRC const&)> - buildChangeHandler (Duration& target) - { - return bind (Link<TI,Duration>::dontChange, ref(target) ); - } - }; - - template<class TAR> - struct Policy<Duration, typename disable_if< canReceiveDuration<TAR>, - Duration>::type, TAR> - { - static function<Duration(Duration const&)> - buildChangeHandler (TAR&) - { - return bind ( ignore_change_and_return_Zero ); - } - - static Duration - ignore_change_and_return_Zero() - { - return Duration::NIL; - } - }; - - template<class TI> - struct Policy<TI,TimeSpan,Duration> - { - static function<TI(TimeSpan const&)> - buildChangeHandler (Duration& target) - { - return bind (Link<TI,Duration>::useLengthAsChange, ref(target), _1 ); - } - }; - - // special treatment for TimeSpan values... - - template<class TI> - struct Policy<TI,Duration,TimeSpan> - { - static function<TI(Duration const&)> - buildChangeHandler (TimeSpan& target) - { - return bind (Link<TI,TimeSpan>::mutateLength, ref(target), _1 ); - } - }; - - template<> - struct Policy<TimeSpan,TimeSpan,TimeSpan> - { - static function<TimeSpan(TimeSpan const&)> - buildChangeHandler (TimeSpan& target) - { - return bind (Link<TimeSpan,TimeSpan>::mutateTimeSpan, ref(target), _1 ); - } - }; - - - - - - - template<class TI> - template<class TAR> - void - Mutator<TI>::bind_to (TAR& target) const - { - setVal_ = Policy<TI,TI, TAR>::buildChangeHandler (target); - offset_ = Policy<TI,Offset,TAR>::buildChangeHandler (target); - nudge_ = Policy<TI,int, TAR>::buildChangeHandler (target); - } - - template<class TI> - void - Mutator<TI>::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 TI> 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<class TI> void - Control<TI>::operator () (TI const& newValue) + Control<TI>::operator () (TI const& newValue) const { this->ensure_isArmed(); notifyListeners_( this->setVal_(newValue)); } + /** impose an offset to the connected target. + * If applicable, the target will be adjusted by the + * time offset, and listeners will be notified. + * @throw error::State if not connected to a target + */ template<class TI> void - Control<TI>::operator () (Offset const& adjustment) + Control<TI>::operator () (Offset const& adjustment) const { this->ensure_isArmed(); notifyListeners_( this->offset_(adjustment)); } + /** nudge the connected target by the given offset steps, + * using either the target's own grid (when quantised), + * or a 'natural' nudge grid + * @throw error::State if not connected to a target + */ template<class TI> void - Control<TI>::operator () (int offset_by_steps) + Control<TI>::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<class TI> void Control<TI>::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 <<END +TEST "Life changing time specifications with feedback" TimeControl_test <<END +out: Test-Case. Target=.+time.Duration. <--feed--- .+time.TimeValue.$ +out: Test-Case. Target=.+time.Duration. <--feed--- .+time.Time.$ +out: Test-Case. Target=.+time.Duration. <--feed--- .+time.Duration.$ +out: Test-Case. Target=.+time.Duration. <--feed--- .+time.TimeSpan.$ +out: Test-Case. Target=.+time.Duration. <--feed--- .+time.QuTime.$ +out: Test-Case. Target=.+time.TimeSpan. <--feed--- .+time.TimeValue.$ +out: Test-Case. Target=.+time.TimeSpan. <--feed--- .+time.Time.$ +out: Test-Case. Target=.+time.TimeSpan. <--feed--- .+time.Duration.$ +out: Test-Case. Target=.+time.TimeSpan. <--feed--- .+time.TimeSpan.$ +out: Test-Case. Target=.+time.TimeSpan. <--feed--- .+time.QuTime.$ +out: Test-Case. Target=.+time.QuTime. <--feed--- .+time.TimeValue.$ +out: Test-Case. Target=.+time.QuTime. <--feed--- .+time.Time.$ +out: Test-Case. Target=.+time.QuTime. <--feed--- .+time.Duration.$ +out: Test-Case. Target=.+time.QuTime. <--feed--- .+time.TimeSpan.$ +out: Test-Case. Target=.+time.QuTime. <--feed--- .+time.QuTime.$ return: 0 END diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp index ab12a2591..c40ff1d54 100644 --- a/tests/lib/time/time-control-test.cpp +++ b/tests/lib/time/time-control-test.cpp @@ -26,10 +26,11 @@ #include "lib/time/timevalue.hpp" #include "lib/time/timequant.hpp" #include "lib/time/control.hpp" -#include "proc/asset/meta/time-grid.hpp" + #include "lib/meta/generator-combinations.hpp" -#include "lib/meta/util.hpp" +#include "proc/asset/meta/time-grid.hpp" #include "lib/scoped-holder.hpp" +#include "lib/meta/util.hpp" #include "lib/util.hpp" #include <boost/lexical_cast.hpp> @@ -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<class SRC> struct TestChange @@ -395,17 +408,21 @@ namespace test{ - template<class TAR, class SRC, class BASE> + 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<TAR>() - <<" <--feed--- " << showType<SRC>() - <<endl; + << "\t <--feed--- " << showType<SRC>() + << endl; + // test subject Control<SRC> controller; TAR target = TestTarget<TAR>::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<Duration,TimeSpan,QuTime> KindsOfTarget; - typedef Types<Time,Duration,TimeSpan,QuTime> KindsOfSource; + typedef Types<Duration,TimeSpan,QuTime> KindsOfTarget; // time entities to receive value changes + typedef Types<TimeValue,Time,Duration,TimeSpan,QuTime> 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 <prg@ichthyostega.de> Date: Mon, 13 Jun 2011 20:07:30 +0200 Subject: [PATCH 129/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file interface-facade-link.hpp + ** Opening, accessing and closing the service access through a facade interface. + ** Client code is assumed to access an application level service through an facade + ** interface, while the actual implementation object remains an opaque internal detail. + ** Moreover, services may come up and shut down, so the implementation might change + ** during the Lifecycle. The facility defined here in this header provides a basic + ** implementation for this access mechanism, but without any adaptation, binding + ** or plugin access layer. It works only under the assumption that both the + ** interface and the actual service implementation coexist in the same + ** executable and are written in C++, so any invocation of an + ** interface method boils down to a language-level call. + ** + ** Usually, client code doesn't need to include this header. Clients are assumed + ** to use the facade interface of the service in question. This facade interface + ** contains a static member of type \c lumiera::facade::Accessor<I> (where I is + ** the type of the facade interface). The Accessor baseclass is defined in + ** interfaceproxy.hpp and typically included through the facade header. + ** + ** @note there is a way more elaborate implementation of the same mechanism + ** for use with the Lumiera Interface/Plugin system. + ** + ** @see interfaceproxy.hpp description of the more general use case + ** @see PlayService example for the simple use case + */ + + +#ifndef LUMIERA_FACADE_INTERFACE_FACADE_LINK_H +#define LUMIERA_FACADE_INTERFACE_FACADE_LINK_H + + +#include "lib/error.hpp" +#include "lib/test/test-helper.hpp" +#include "include/interfaceproxy.hpp" +#include "lib/symbol.hpp" + +#include <boost/noncopyable.hpp> + + + +namespace lumiera { +namespace facade { + + using lib::Literal; + + + /************************************************************************ + * simple access-frontend to the implementation of a service (C++ only). + * Usually, an instance of Accessor is placed as static member right into + * the facade interface used to access the service. This implementation + * of the access mechanism handles the simple case that both the facade + * and the service implementation are written in C++ and calls happen + * within the main executable as direct language calls, without an + * binding layer and without involving the Interface/Plugin system. + * + * Typically, the InterfaceFacadeLink becomes a member of the service + * implementation class and is directly tied into the constructor of + * the latter. Being a subclass of lumiera::facade::Accessor, it is + * allowed to "open" the facade access just by setting the static + * protected pointer Accessor::implProxy_ + */ + template<class FA> + class InterfaceFacadeLink + : protected Accessor<FA> + , boost::noncopyable + { + Literal displayName_; + + void + __checkLifecycle () + { + if (Accessor<FA>::implProxy_) + throw error::State("Attempt to open an already opened Facade interface." + , error::LUMIERA_ERROR_LIFECYCLE); + } + + public: + InterfaceFacadeLink(FA& serviceImpl, Literal interfaceName_for_Log=0) + : displayName_(lib::test::showType<FA>(interfaceName_for_Log)) + { + __checkLifecycle(); + Accessor<FA>::implProxy_ = &serviceImpl; + INFO (interface, "interface %s opened", displayName_.c()); + } + + ~InterfaceFacadeLink() + { + INFO (interface, "closing interface %s...", displayName_.c()); + Accessor<FA>::implProxy_ = 0; + } + }; + + + + /** storage for the static access pointer */ + template<class FA> + FA* Accessor<FA>::implProxy_; + + +}} // namespace lumiera::facade + +#endif 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<FA> where FA is the + * facade interface. Being a subclass, allows the implementation + * to set that pointer when the service comes up, and to clear + * it when the service goes down and the access needs to + * be closed. */ template<class FA> class Accessor @@ -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<mobject::ModelPort> ModelPorts; - typedef lib::IterSource<mobject::OutputDesignation> Pipes; + typedef lib::IterSource<mobject::ModelPort>& ModelPorts; + typedef lib::IterSource<mobject::OutputDesignation>& 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 <boost/noncopyable.hpp> //#include <boost/scoped_ptr.hpp> @@ -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<lumiera::Play> 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. </pre> </div> -<div title="DesignPlayerSubsystem" modifier="Ichthyostega" modified="201105230122" created="201105220216" tags="Player design draft" changecount="12"> +<div title="DesignPlayerSubsystem" modifier="Ichthyostega" modified="201106122319" created="201105220216" tags="Player design draft" changecount="13"> <pre>//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. </pre> </div> @@ -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. </pre> </div> -<div title="OutputManagement" modifier="Ichthyostega" modified="201105220022" created="201007090155" tags="Model Rendering Player spec draft" changecount="19"> +<div title="OutputManagement" modifier="Ichthyostega" modified="201106130008" created="201007090155" tags="Model Rendering Player spec draft" changecount="21"> <pre>//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. </pre> </div> +<div title="OutputManager" modifier="Ichthyostega" modified="201106130000" created="201106122359" tags="Player Model def" changecount="2"> +<pre>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</pre> +</div> <div title="OutputMapping" modifier="Ichthyostega" modified="201105221814" created="201011080238" tags="Model spec draft" changecount="25"> <pre>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.</pre> </div> -<div title="ViewerPlayConnection" modifier="Ichthyostega" modified="201105221838" created="201007110305" tags="Model Player spec draft" changecount="8"> +<div title="ViewerPlayConnection" modifier="Ichthyostega" modified="201106122343" created="201007110305" tags="Model Player spec draft" changecount="10"> <pre>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. </pre> </div> <div title="VirtualClip" modifier="Ichthyostega" modified="201011220152" created="200804110321" tags="def Model" changecount="17"> From 1b0cb56dccd88789084c0e1995edd303ecf46d02 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 14 Jun 2011 05:41:02 +0200 Subject: [PATCH 130/496] 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<Subsys*> @@ -114,7 +117,7 @@ namespace lumiera { private: - /** weather this subsystem is actually operational. + /** whether this subsystem is actually operational. * When returning \c false here, the application may * terminate at any point without further notice * Note further, that a subsystem must not be in 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<void> instance?? */ class ExecResult : public lib::BoolCheckable<ExecResult> 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 <string> @@ -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<SessionImplAPI> pImpl_; + scoped_ptr<SessionImplAPI> pSess_; scoped_ptr<LifecycleAdvisor> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/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> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file 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 <boost/noncopyable.hpp> +//#include <string> +//#include <vector> +//#include <tr1/memory> +#include <boost/scoped_ptr.hpp> + + +namespace proc { +namespace play { + +//using std::string; +//using std::vector; +//using std::tr1::shared_ptr; + using boost::scoped_ptr; + + + class PlayService; + + +//typedef lib::ScopedPtrVect<DisplayerSlot> DisplayerTab; + + + + /****************************************************** + * Management of external Output connections. + * + * @todo write Type comment + */ + class OutputDirector + : boost::noncopyable + , public lib::Sync<> + { + typedef lumiera::Subsys::SigTerm SigTerm; + + scoped_ptr<PlayService> player_; + + public: + static lib::Singleton<OutputDirector> instance; + + bool connectUp() ; + void triggerDisconnect(SigTerm) throw(); + + bool isOperational() const; + + private: + OutputDirector() ; + ~OutputDirector() ; + friend class lib::singleton::StaticCreate<OutputDirector>; + + + 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 <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -* *****************************************************/ - - -#include "proc/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 <prg@ichthyostega.de> Date: Wed, 15 Jun 2011 03:51:06 +0200 Subject: [PATCH 131/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +/** @file 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 <string> +//#include <memory> +//#include <tr1/functional> +//#include <boost/scoped_ptr.hpp> + +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<Time> & ctrl) + { + UNIMPLEMENTED ("install a controller for influencing the 'playhead position'"); + } + + + /** + * + */ + void + Play::Controller::controlDuration (time::Control<Duration> & ctrl) + { + UNIMPLEMENTED ("install a controller for influencing the overall playback duration"); + } + + + /** + * + */ + void + Play::Controller::controlLooping (time::Control<TimeSpan> & ctrl) + { + UNIMPLEMENTED ("install a controller for adjusting looped playback"); + } + + + /** + * + */ + void + Play::Controller::useProxyMedia (bool) + { + UNIMPLEMENTED ("toggle the use of proxy(placeholder) media"); + } + + + /** + * + */ + void + Play::Controller::setQuality (uint) + { + UNIMPLEMENTED ("adjust the quality level of the current render"); + } + + + /** + * + */ + bool + Play::Controller::is_playing() const + { + UNIMPLEMENTED ("retrieve the playback state"); + } + + + /** + * + */ + bool + Play::Controller::is_scrubbing() const + { + UNIMPLEMENTED ("retrieve the state of scrubbing playback"); + } + + + /** + * + */ + double + Play::Controller::getSpeed() const + { + UNIMPLEMENTED ("retrieve the current playback speed"); + } + + + /** + * + */ + uint + Play::Controller::getQuality() const + { + UNIMPLEMENTED ("retrieve the current render quality setting"); + } + + + /** + * + */ + bool + Play::Controller::usesProxy() const + { + UNIMPLEMENTED ("determine if the current render/playback uses proxy media"); + } + + + +} + + +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.... + + + + } // (End) hidden service impl details + + + + + /** */ + + +}} // namespace proc::play diff --git a/src/proc/play/play-controller.hpp b/src/proc/play/play-controller.hpp index 239ffa0c5..03db62378 100644 --- a/src/proc/play/play-controller.hpp +++ b/src/proc/play/play-controller.hpp @@ -21,6 +21,7 @@ */ /** @file play-controller.hpp + ** Functionality for driving and controlling an ongoing playback or render. ** ** @see lumiera::DummyPlayer ** @see gui::PlaybackController usage example @@ -42,28 +43,28 @@ namespace proc { - namespace play { - - using std::string; - - - /******************************************************************** - */ - class PlayController - : boost::noncopyable - { - - public: - - private: - }; - - - - - - - } // namespace play +namespace play { -} // namespace proc + using std::string; + + + /******************************************************************** + * TODO do we still need that?????? + * Isn't lumiera::Play::Controller already sufficient? + */ + class PlayController + : boost::noncopyable + { + + public: + + private: + }; + + + + + + +}} // namespace proc::play #endif diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index 44a259c33..9207a3b10 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -52,14 +52,22 @@ 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. + * Playback/Render process within the Lumiera Player. + * This is a top-level implementation entity, created + * by the PlayService when \em performing a timeline + * or similar model object. A PlayProcess doesn't + * perform calculations; rather it serves to group + * all the handles, registrations, calculation contexts + * and similar entries necessary to make the calculations + * happen within the renderengine and to send the generated + * data to an output sink. + * + * @note PlayProcess entities are never directly visible. + * Client code handles and controls these processes + * through a lumiera::Play::Controller returned + * from the PlayService. */ - class PlayerProcess + class PlayProcess : boost::noncopyable { diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index 52d0ac789..50077545a 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -22,7 +22,8 @@ #include "proc/play/play-service.hpp" -#include "lib/singleton.hpp" +#include "proc/play/play-process.hpp" +#include "lib/scoped-ptrvect.hpp" #include <string> @@ -50,6 +51,8 @@ namespace play { //using std::auto_ptr; //using boost::scoped_ptr; //using std::tr1::bind; + using lib::Sync; + using lib::RecursiveLock_NoWait; namespace { // hidden local details of the service implementation.... @@ -65,9 +68,24 @@ namespace play { } // (End) hidden service impl details + class ProcessTable + : public Sync<RecursiveLock_NoWait> + { + typedef lib::ScopedPtrVect<PlayProcess> ProcTable; + + ProcTable processes_; + + public: + + }; + + using lumiera::Play; + PlayService::~PlayService() { } + + /** 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 @@ -76,6 +94,7 @@ namespace play { */ PlayService::PlayService() /////TODO Subsys::SigTerm terminationHandle); : facadeAccess_(*this, "Player") + , pTable_(new ProcessTable) { } @@ -96,6 +115,64 @@ namespace play { { UNIMPLEMENTED ("build a PlayProcess"); } + }} // namespace proc::play + + +namespace lumiera { + + /* ==== convenience shortcuts for creating a PlayProcess ==== */ + + /** + * + */ + Play::Controller + Play::perform(Pipes, Output) + { + UNIMPLEMENTED ("build PlayProcess based on a set of pipes"); + } + + + /** + * + */ + Play::Controller + Play::perform(Timeline) + { + UNIMPLEMENTED ("build PlayProcess for a Timeline"); + } + + + /** + * + */ + Play::Controller + Play::perform(Viewer) + { + UNIMPLEMENTED ("build PlayProcess directly for a Viwer element"); + } + + + /** + * + */ + Play::Controller + Play::perform(Track) + { + UNIMPLEMENTED ("build PlayProcess for a single Track"); + } + + + /** + * + */ + Play::Controller + Play::perform(Clip) + { + UNIMPLEMENTED ("build virtual Timeline and PlayProcess to show a single Clip"); + } + + +} diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp index 29db10187..b0b39e4a1 100644 --- a/src/proc/play/play-service.hpp +++ b/src/proc/play/play-service.hpp @@ -37,7 +37,7 @@ #include "common/interface-facade-link.hpp" #include <boost/noncopyable.hpp> -//#include <boost/scoped_ptr.hpp> +#include <boost/scoped_ptr.hpp> //#include <string> @@ -49,21 +49,36 @@ namespace play { //using lumiera::Subsys; //using lumiera::Display; //using lumiera::DummyPlayer; + using boost::scoped_ptr; // class DummyImageGenerator; // class TickService; + class ProcessTable; /****************************************************** - * Interface: Player subsystem. + * Implementation access point: Player subsystem. + * The PlayService is the primary way for clients to + * get into using Lumiera's Play/Output Subsystem. + * It allows to \em perform a timeline or model object. + * Behind the scenes, this will create and outfit a + * PlayProcess, which is accessible through the + * Play::Controller returned as frontend/handle. + * + * @internal the PlayService is instantiated and owned + * by the OutputDirector, which acts as a central hub + * for the Player subsystem. Clients should always + * access this functionality through the + * lumiera::Play facade interface. */ class PlayService : public lumiera::Play , boost::noncopyable { InterfaceFacadeLink<lumiera::Play> facadeAccess_; + scoped_ptr<ProcessTable> pTable_; /** Implementation: build a PlayProcess */ @@ -73,7 +88,7 @@ namespace play { public: PlayService(); /////TODO Subsys::SigTerm terminationHandle); - ~PlayService() { }/////TODO notifyTermination_(&error_); } + ~PlayService(); /////TODO notifyTermination_(&error_); } }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index d5ee8e508..3f9043064 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -4179,8 +4179,8 @@ This is the core service provided by the player subsystem. The purpose is to cre :any details of this processing remain opaque for the clients; even the player subsystem just accesses the EngineFaçade </pre> </div> -<div title="Player" modifier="Ichthyostega" modified="201105220214" created="201012181700" tags="def overview" changecount="9"> -<pre>Within Lumiera, &raquo;Player&laquo; denotes the [[Subsystem]] responsible for organising and tracking //ongoing playback and render processes.// &rarr; [[PlayProcess]] +<div title="Player" modifier="Ichthyostega" modified="201106131443" created="201012181700" tags="def overview" changecount="10"> +<pre>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 <prg@ichthyostega.de> Date: Fri, 17 Jun 2011 04:07:07 +0200 Subject: [PATCH 132/496] 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<class MX> @@ -99,8 +101,8 @@ namespace mobject { private: enum { - SPEC_SIZ = lumiera::typelist::maxSize< - lumiera::typelist::Types<PID,lumiera_uid,uint>::List>::value + SPEC_SIZ = mp::maxSize< + mp::Types< PID, lumiera_uid, uint>::List>::value }; typedef lib::OpaqueHolder<TargetSpec, SPEC_SIZ> 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. </pre> </div> -<div title="OutputDesignation" modifier="Ichthyostega" modified="201011190258" created="201006220126" tags="Model draft design discuss" changecount="50"> +<div title="OutputDesignation" modifier="Ichthyostega" modified="201106162303" created="201006220126" tags="Model draft design discuss" changecount="59"> <pre>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) </pre> </div> -<div title="OutputManagement" modifier="Ichthyostega" modified="201106130008" created="201007090155" tags="Model Rendering Player spec draft" changecount="21"> +<div title="OutputManagement" modifier="Ichthyostega" modified="201106162128" created="201007090155" tags="Model Rendering Player spec draft" changecount="23"> <pre>//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. </pre> </div> -<div title="OutputManager" modifier="Ichthyostega" modified="201106130000" created="201106122359" tags="Player Model def" changecount="2"> -<pre>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. +<div title="OutputManager" modifier="Ichthyostega" modified="201106162346" created="201106122359" tags="Player Model def" changecount="3"> +<pre>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</pre> </div> -<div title="OutputMapping" modifier="Ichthyostega" modified="201105221814" created="201011080238" tags="Model spec draft" changecount="25"> +<div title="OutputMapping" modifier="Ichthyostega" modified="201106162204" created="201011080238" tags="Model spec draft" changecount="28"> <pre>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. +</pre> +</div> +<div title="OutputSlot" modifier="Ichthyostega" modified="201106170206" created="201106162339" tags="def Concepts Player spec" changecount="4"> +<pre>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. + </pre> </div> <div title="Overview" modifier="Ichthyostega" modified="200906071810" created="200706190300" tags="overview img" changecount="13"> From 4ece1352572902de34e548bf3f3022a60583c60e Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 18 Jun 2011 03:34:27 +0200 Subject: [PATCH 133/496] 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 <boost/noncopyable.hpp> //#include <string> @@ -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<DisplayerSlot> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file 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 <boost/noncopyable.hpp> +//#include <string> +//#include <vector> +//#include <tr1/memory> +//#include <boost/scoped_ptr.hpp> + + +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<Connection> + { + + public: + void emit(Time); + }; + + + class SharedBufferSink + : public lib::Handle<Connection> + { + + 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) </pre> </div> -<div title="OutputManagement" modifier="Ichthyostega" modified="201106162128" created="201007090155" tags="Model Rendering Player spec draft" changecount="23"> +<div title="OutputManagement" modifier="Ichthyostega" modified="201106180118" created="201007090155" tags="Model Rendering Player spec draft" changecount="25"> <pre>//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?}}} </pre> </div> <div title="OutputManager" modifier="Ichthyostega" modified="201106162346" created="201106122359" tags="Player Model def" changecount="3"> @@ -3257,13 +3259,13 @@ While mostly the model just denotes the destination for output as OutputDesignat &rarr; see OutputSlot &rarr; see ViewerPlayConnection</pre> </div> -<div title="OutputMapping" modifier="Ichthyostega" modified="201106162204" created="201011080238" tags="Model spec draft" changecount="28"> +<div title="OutputMapping" modifier="Ichthyostega" modified="201106180044" created="201011080238" tags="Model spec draft" changecount="29"> <pre>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. </pre> </div> -<div title="OutputSlot" modifier="Ichthyostega" modified="201106170206" created="201106162339" tags="def Concepts Player spec" changecount="4"> +<div title="OutputSlot" modifier="Ichthyostega" modified="201106180039" created="201106162339" tags="def Concepts Player spec" changecount="7"> <pre>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 <prg@ichthyostega.de> Date: Tue, 21 Jun 2011 04:24:01 +0200 Subject: [PATCH 134/496] 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<mobject::ModelPort>& ModelPorts; - typedef lib::IterSource<mobject::OutputDesignation>& Pipes; + typedef lib::IterSource<mobject::ModelPort>::iterator ModelPorts; + typedef lib::IterSource<mobject::OutputDesignation>::iterator Pipes; typedef proc::play::POutputManager Output; typedef mobject::session::PClipMO Clip; typedef mobject::PTrack Track; 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 <boost/noncopyable.hpp> @@ -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]] </pre> </div> -<div title="ExitNode" modifier="Ichthyostega" created="200706220322" tags="def" changecount="1"> +<div title="ExitNode" modifier="Ichthyostega" modified="201106210120" created="200706220322" tags="def" changecount="3"> <pre>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") </pre> </div> <div title="ExplicitPlacement" modifier="Ichthyostega" modified="201012122052" created="200706220304" tags="def" changecount="6"> @@ -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) </pre> </div> -<div title="OutputManagement" modifier="Ichthyostega" modified="201106180118" created="201007090155" tags="Model Rendering Player spec draft" changecount="25"> +<div title="OutputManagement" modifier="Ichthyostega" modified="201106210136" created="201007090155" tags="Model Rendering Player spec draft" changecount="31"> <pre>//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?}}} </pre> </div> -<div title="OutputManager" modifier="Ichthyostega" modified="201106162346" created="201106122359" tags="Player Model def" changecount="3"> -<pre>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. +<div title="OutputManager" modifier="Ichthyostega" modified="201106210222" created="201106122359" tags="Player Model def" changecount="6"> +<pre>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</pre> +&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 +</pre> </div> <div title="OutputMapping" modifier="Ichthyostega" modified="201106180044" created="201011080238" tags="Model spec draft" changecount="29"> <pre>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. </pre> </div> -<div title="StreamPrototype" modifier="Ichthyostega" modified="200809120021" created="200808152042" tags="def spec" changecount="10"> +<div title="StreamPrototype" modifier="Ichthyostega" modified="201106210145" created="200808152042" tags="def spec" changecount="11"> <pre>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... </pre> </div> <div title="StreamType" modifier="Ichthyostega" modified="201011071741" created="200808060244" tags="spec draft" changecount="21"> From dea1fa57a21ac2b0478b763cf45532bf6a647685 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 22 Jun 2011 02:42:50 +0200 Subject: [PATCH 135/496] 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<BuffHandle> @@ -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<class NEXT> struct ReleaseBuffers : NEXT /////////////////TODO: couldn't this be done automatically by BuffTab's dtor?? - { ///////////////// this would require BuffHandle to be a smart ref.... + { ///////////////// this would require BuffHandle to be a smart ref.... --> ///TICKET #249 BuffHandle step (Invocation& ivo) { 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 <boost/noncopyable.hpp> @@ -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<BufferHandoverSink>::iterator Opened_BufferHandoverSinks; + typedef lib::IterSource<SharedBufferSink>::iterator Opened_SharedBufferSinks; + + template<class SINK> + struct Allocation + { + typedef typename lib::IterSource<SINK>::iterator OpenedSinks; + + OpenedSinks getOpenedSinks(); + + bool isActive(); + + /////TODO add here the getters for timing constraints + }; + + + bool isFree() const; + + Allocation<BufferHandoverSink> + 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 // <unidirectional association> - classinstance_ref 131333 // ouput end end @@ -350,8 +350,6 @@ format 58 attributes end relations - relation_ref 135429 // <unidirectional association> - classinstance_ref 131589 // end end @@ -360,8 +358,6 @@ format 58 attributes end relations - relation_ref 135429 // <unidirectional association> - classinstance_ref 131717 // vid_a end end @@ -370,8 +366,6 @@ format 58 attributes end relations - relation_ref 135429 // <unidirectional association> - classinstance_ref 131461 // input end end @@ -430,8 +424,6 @@ format 58 attributes end relations - relation_ref 135429 // <unidirectional association> - classinstance_ref 133893 // HUE end end @@ -440,8 +432,6 @@ format 58 attributes end relations - relation_ref 135429 // <unidirectional association> - classinstance_ref 134021 // vid_a end end @@ -464,8 +454,6 @@ format 58 attributes end relations - relation_ref 135429 // <unidirectional association> - classinstance_ref 133253 // HUE end end @@ -474,8 +462,6 @@ format 58 attributes end relations - relation_ref 135429 // <unidirectional association> - classinstance_ref 132869 // input end end @@ -506,8 +492,6 @@ format 58 attributes end relations - relation_ref 135429 // <unidirectional association> - classinstance_ref 133125 // ouput end end @@ -516,8 +500,6 @@ format 58 attributes end relations - relation_ref 135429 // <unidirectional association> - classinstance_ref 132613 // devnull end end @@ -534,8 +516,6 @@ format 58 attributes end relations - relation_ref 135429 // <unidirectional association> - classinstance_ref 134021 // vid_a end end @@ -558,8 +538,6 @@ format 58 attributes end relations - relation_ref 135429 // <unidirectional association> - classinstance_ref 134149 // input end end @@ -568,8 +546,6 @@ format 58 attributes end relations - relation_ref 135429 // <unidirectional association> - 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 // <directional composition> + relation 205701 *--> + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type} ${name}${value}; +" + classrelation_ref 216837 // <directional composition> + 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 // <directional aggregation> + relation 205573 o--> + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 216709 // <directional aggregation> + 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 // <realization> + relation 205445 -_-|> + a public + cpp default "${type}" + classrelation_ref 216581 // <realization> + 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 // <unidirectional association> + relation 205957 ---> + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 217093 // <unidirectional association> + 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 // <directional composition> + relation 205829 *--> + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type} ${name}${value}; +" + classrelation_ref 216965 // <directional composition> + 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 // <realization> + 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 // <directional aggregation> + 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 // <directional composition> + 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 // <directional composition> + 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 // <unidirectional association> + 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 // <realization> + 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 </pre> </div> -<div title="MultichannelMedia" modifier="Ichthyostega" modified="201003140238" created="200709200255" tags="design img" changecount="12"> +<div title="MultichannelMedia" modifier="Ichthyostega" modified="201106212255" created="200709200255" tags="design img" changecount="14"> <pre>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 + + </pre> </div> <div title="NodeConfiguration" modifier="Ichthyostega" modified="200909041807" created="200909041806" tags="spec Builder Rendering" changecount="2"> @@ -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?}}} </pre> </div> -<div title="OutputManager" modifier="Ichthyostega" modified="201106210222" created="201106122359" tags="Player Model def" changecount="6"> +<div title="OutputManager" modifier="Ichthyostega" modified="201106212317" created="201106122359" tags="Player Model def" changecount="7"> <pre>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) </pre> </div> <div title="OutputMapping" modifier="Ichthyostega" modified="201106180044" created="201011080238" tags="Model spec draft" changecount="29"> @@ -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. </pre> </div> -<div title="OutputSlot" modifier="Ichthyostega" modified="201106180039" created="201106162339" tags="def Concepts Player spec" changecount="7"> +<div title="OutputSlot" modifier="Ichthyostega" modified="201106212331" created="201106162339" tags="def Concepts Player spec" changecount="8"> <pre>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). + </pre> </div> <div title="Overview" modifier="Ichthyostega" modified="200906071810" created="200706190300" tags="overview img" changecount="13"> @@ -4188,14 +4201,21 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T <<tasksum end>> </pre> </div> -<div title="PlayProcess" modifier="Ichthyostega" modified="201105222321" created="201012181714" tags="def spec Player" changecount="5"> +<div title="PlayProcess" modifier="Ichthyostega" modified="201106220026" created="201012181714" tags="def spec Player img" changecount="10"> <pre>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</pre> +* 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. +</pre> </div> <div title="PlayService" modifier="Ichthyostega" modified="201105221918" created="201105221900" tags="Player spec draft" changecount="9"> <pre>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 <prg@ichthyostega.de> Date: Thu, 23 Jun 2011 17:54:28 +0200 Subject: [PATCH 136/496] 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<class CONS> + 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 <string> //#include <memory> -//#include <tr1/functional> +#include <tr1/functional> +#include <tr1/memory> //#include <boost/scoped_ptr.hpp> @@ -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<RecursiveLock_NoWait> { - typedef lib::ScopedPtrVect<PlayProcess> ProcTable; + typedef std::vector<weak_ptr<PlayProcess> > 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. </pre> </div> -<div title="OutputSlot" modifier="Ichthyostega" modified="201106212331" created="201106162339" tags="def Concepts Player spec" changecount="8"> +<div title="OutputSlot" modifier="Ichthyostega" modified="201106230057" created="201106162339" tags="def Concepts Player spec" changecount="10"> <pre>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!// + </pre> </div> <div title="Overview" modifier="Ichthyostega" modified="200906071810" created="200706190300" tags="overview img" changecount="13"> @@ -4201,7 +4208,7 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T <<tasksum end>> </pre> </div> -<div title="PlayProcess" modifier="Ichthyostega" modified="201106220026" created="201012181714" tags="def spec Player img" changecount="10"> +<div title="PlayProcess" modifier="Ichthyostega" modified="201106231449" created="201012181714" tags="def spec Player img" changecount="12"> <pre>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. </pre> </div> <div title="PlayService" modifier="Ichthyostega" modified="201105221918" created="201105221900" tags="Player spec draft" changecount="9"> From 511d08adad4c8f47389319e8e375c2d67980f6d3 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 24 Jun 2011 18:40:19 +0200 Subject: [PATCH 137/496] 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<proc::play::PlayProcess>(); }; 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 <boost/noncopyable.hpp> //#include <string> @@ -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<OutputManager> 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 <boost/noncopyable.hpp> //#include <boost/scoped_ptr.hpp> @@ -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 <string> @@ -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<RecursiveLock_NoWait> { - typedef std::vector<weak_ptr<PlayProcess> > ProcTable; + typedef weak_ptr<PlayProcess> Entry; + typedef std::vector<Entry> 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<OutputSlot&(ModelPort)> + 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 <prg@ichthyostega.de> Date: Sat, 25 Jun 2011 17:44:28 +0200 Subject: [PATCH 138/496] 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<proc::play::PlayProcess>(); + operator weak_ptr<proc::play::PlayProcess>() 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<PlayProcess>() const + { + return weak_ptr<PlayProcess> (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 <string> //#include <memory> -//#include <tr1/functional> +#include <tr1/functional> //#include <boost/scoped_ptr.hpp> -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<Feed(ModelPort)> 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 <boost/noncopyable.hpp> //#include <boost/scoped_ptr.hpp> //#include <string> +#include <vector> 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<ModelPort>::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<Feed>::iterator Connections; + + Feed (ModelPort, OutputSlot&); }; + /****************************************************** * Playback/Render process within the Lumiera Player. @@ -107,17 +118,13 @@ namespace play { class PlayProcess : boost::noncopyable { + std::vector<Feed> outputFeeds_; + + PlayProcess (Feed::Connections pipeConnections); public: - template<class CONS> - 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 <string> //#include <memory> -#include <tr1/functional> +//#include <tr1/functional> #include <tr1/memory> //#include <boost/scoped_ptr.hpp> @@ -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<OutputSlot&(ModelPort)> - 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 <prg@ichthyostega.de> Date: Sun, 26 Jun 2011 03:59:03 +0200 Subject: [PATCH 139/496] 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<Val>::iterator Iter; }; + template<class IT, class FUN> + struct _TransformIterT + { + typedef typename _ProducedOutput<FUN>::Type ResVal; + typedef TransformIter<IT,ResVal> TransIter; + typedef typename IterSource<ResVal>::iterator Iter; + }; + template<class IT> struct _PairIterT { @@ -329,6 +337,28 @@ namespace lib { } + /** pipes a given Lumiera Forward Iterator through + * a transformation function and wraps the resulting + * transforming Iterator, exposing just a IterSource. + * This convenience shortcut can be used to build a + * processing chain; the resulting IterSource will + * hide any involved detail types. + * @note as with any IterSource, there is one virtual + * function call for every fetched element. + */ + template<class IT, class FUN> + typename _TransformIterT<IT,FUN>::Iter + transform (IT const& source, FUN processingFunc) + { + typedef typename _TransformIterT<IT,FUN>::ResVal ValType; + typedef typename _TransformIterT<IT,FUN>::TransIter TransIT; + + return IterSource<ValType>::build ( + new WrappedLumieraIterator<TransIT> ( + transformIterator (source, processingFunc))); + } + + /** @return a Lumiera Forward Iterator to yield * all the keys of the given Map or Hashtable */ @@ -424,6 +454,7 @@ namespace lib { } using iter_source::wrapIter; + using iter_source::transform; using iter_source::eachMapKey; using iter_source::eachDistinctKey; using iter_source::eachValForKey; 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 <<END out: ::13::12::11::10::9::8::7::6::5::4::3::2::1 out: ::.............::............::...........::..........::.........::........::.......::......::.....::....::...::..::.$ +out-lit: ::0:00:03.250::0:00:03.000::0:00:02.750::0:00:02.500::0:00:02.250::0:00:02.000::0:00:01.750::0:00:01.500::0:00:01.250::0:00:01.000::0:00:00.750::0:00:00.500::0:00:00.250 out: (::X...........){13}$ out: (::0:0.:..\..00){13}$ out: (::X...........){13}$ diff --git a/tests/lib/iter-source-test.cpp b/tests/lib/iter-source-test.cpp index cd1ef447e..046333fbf 100644 --- a/tests/lib/iter-source-test.cpp +++ b/tests/lib/iter-source-test.cpp @@ -168,6 +168,7 @@ namespace test{ if (0 < arg.size()) NUM_ELMS = lexical_cast<uint> (arg[0]); verify_simpleIters(); + verify_transformIter(); verify_MapWrappers<TreeMap>(); verify_MapWrappers<HashMap>(); @@ -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<class IT> 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 <prg@ichthyostega.de> Date: Sun, 26 Jun 2011 04:02:39 +0200 Subject: [PATCH 140/496] 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 <prg@ichthyostega.de> Date: Mon, 27 Jun 2011 03:09:38 +0200 Subject: [PATCH 141/496] 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. </pre> </div> -<div title="OutputSlot" modifier="Ichthyostega" modified="201106230057" created="201106162339" tags="def Concepts Player spec" changecount="10"> +<div title="OutputSlot" modifier="Ichthyostega" modified="201106270108" created="201106162339" tags="def Concepts Player spec" changecount="14"> <pre>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! </pre> </div> <div title="Overview" modifier="Ichthyostega" modified="200906071810" created="200706190300" tags="overview img" changecount="13"> From 650e73c45455b765307e2f1329a588cf25b25487 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 5 Jul 2011 00:56:57 +0200 Subject: [PATCH 142/496] 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<Connection> + class BufferProvider { public: - void emit(Time, BuffHandle); + ~BufferProvider() { } + + BuffHandle lockBufferFor(Time); }; - class SharedBufferSink + class DataSink : public lib::Handle<Connection> { @@ -96,14 +97,10 @@ namespace play { public: virtual ~OutputSlot(); - typedef lib::IterSource<BufferHandoverSink>::iterator Opened_BufferHandoverSinks; - typedef lib::IterSource<SharedBufferSink>::iterator Opened_SharedBufferSinks; + typedef lib::IterSource<DataSink>::iterator OpenedSinks; - template<class SINK> struct Allocation { - typedef typename lib::IterSource<SINK>::iterator OpenedSinks; - OpenedSinks getOpenedSinks(); bool isActive(); @@ -114,7 +111,7 @@ namespace play { bool isFree() const; - Allocation<BufferHandoverSink> + 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. </pre> </div> -<div title="OutputSlot" modifier="Ichthyostega" modified="201106270108" created="201106162339" tags="def Concepts Player spec" changecount="14"> +<div title="OutputSlot" modifier="Ichthyostega" modified="201107042247" created="201106162339" tags="def Concepts Player spec" changecount="18"> <pre>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. </pre> </div> <div title="Overview" modifier="Ichthyostega" modified="200906071810" created="200706190300" tags="overview img" changecount="13"> From 7e3054df18384cd7c531929fc221d638ef652a43 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 8 Jul 2011 03:54:26 +0200 Subject: [PATCH 143/496] 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. </pre> </div> -<div title="OutputSlot" modifier="Ichthyostega" modified="201107042247" created="201106162339" tags="def Concepts Player spec" changecount="18"> +<div title="OutputSlot" modifier="Ichthyostega" modified="201107080131" created="201106162339" tags="def Concepts Player spec" changecount="23"> <pre>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. -</pre> + +!!!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</pre> </div> <div title="Overview" modifier="Ichthyostega" modified="200906071810" created="200706190300" tags="overview img" changecount="13"> <pre>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</pre> </div> +<div title="SchedulerRequirements" modifier="Ichthyostega" created="201107080145" tags="Rendering spec draft discuss" changecount="1"> +<pre>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</pre> +</div> <div title="ScopeLocator" modifier="Ichthyostega" modified="200911202035" created="200911192145" tags="def SessionLogic" changecount="10"> <pre>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 <prg@ichthyostega.de> Date: Sat, 9 Jul 2011 02:17:37 +0200 Subject: [PATCH 144/496] 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 <Ichthyostega@web.de> @@ -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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file buffer-provider.hpp + ** Abstraction to represent buffer management and lifecycle within the render engine. + ** It turns out that -- throughout the render engine implementation -- we never need + ** direct access to the buffers holding media data. Buffers are just some entity to be \em managed, + ** i.e. "allocated", "locked" and "released"; the actual meaning of these operations is an 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 <boost/noncopyable.hpp> + + +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.// </pre> </div> +<div title="BufferProvider" modifier="Ichthyostega" modified="201107090002" created="201107082330" tags="Rendering spec draft" changecount="5"> +<pre>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 +</pre> +</div> <div title="BuildFixture" modifier="Ichthyostega" modified="201011290504" created="201011282003" tags="Builder spec operational" changecount="32"> <pre>//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. </pre> </div> -<div title="OutputSlot" modifier="Ichthyostega" modified="201107080131" created="201106162339" tags="def Concepts Player spec" changecount="23"> +<div title="OutputSlot" modifier="Ichthyostega" modified="201107082305" created="201106162339" tags="def Concepts Player spec" changecount="24"> <pre>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]] </pre> </div> -<div title="RenderImplDetails" modifier="Ichthyostega" modified="200810140245" created="200806220211" tags="Rendering impl img" changecount="22"> +<div title="RenderImplDetails" modifier="Ichthyostega" modified="201107082336" created="200806220211" tags="Rendering impl img" changecount="23"> <pre>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) <div title="StateAdapter" modifier="Ichthyostega" modified="200807132344" created="200806261912" tags="Rendering impl def" changecount="10"> <pre>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.</pre> </div> -<div title="StateProxy" modifier="Ichthyostega" modified="200806010404" created="200706220352" tags="def" changecount="4"> +<div title="StateProxy" modifier="Ichthyostega" modified="201107082334" created="200706220352" tags="def" changecount="5"> <pre>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 </pre> </div> From 7adb8149db3036b752e3ffb0874aeda2f324e3f9 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 9 Jul 2011 02:44:24 +0200 Subject: [PATCH 145/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file diagnostic-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 <boost/noncopyable.hpp> +//#include <string> +//#include <vector> +//#include <tr1/memory> +//#include <boost/scoped_ptr.hpp> + + +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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +//#include "lib/util.hpp" +#include "proc/play/diagnostic-output-slot.hpp" + +//#include <boost/format.hpp> +//#include <iostream> + +//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 <prg@ichthyostega.de> Date: Sun, 10 Jul 2011 18:18:20 +0200 Subject: [PATCH 146/496] 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 <boost/format.hpp> //#include <iostream> @@ -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<TestFrame>(); + buff10.create<TestFrame>(); + + // rendering process calculates content.... + buff00.access<TestFrame>() = testData[0,0]; + + // while further frames might be processed in parallel + BuffHandle buff11 = sink2.lockBufferFor (++frameNr); + buff11.create<TestFrame>(); + buff11.access<TestFrame>() = testData[1,1]; + buff10.access<TestFrame>() = 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 <prg@ichthyostega.de> Date: Mon, 11 Jul 2011 00:12:18 +0200 Subject: [PATCH 147/496] 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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 VO3}^(F5ZIPh|59?U+R7Qe*lj&)5`z= literal 15144 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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 <prg@ichthyostega.de> Date: Mon, 11 Jul 2011 02:57:16 +0200 Subject: [PATCH 148/496] 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. </pre> </div> -<div title="OutputSlot" modifier="Ichthyostega" modified="201107082305" created="201106162339" tags="def Concepts Player spec" changecount="24"> +<div title="OutputSlot" modifier="Ichthyostega" modified="201107102318" created="201106162339" tags="def Concepts Player spec" changecount="26"> <pre>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</pre> +* 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</pre> +</div> +<div title="OutputSlotImpl" modifier="Ichthyostega" modified="201107110047" created="201107102343" tags="spec operational" changecount="25"> +<pre>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. +</pre> </div> <div title="Overview" modifier="Ichthyostega" modified="200906071810" created="200706190300" tags="overview img" changecount="13"> <pre>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 <prg@ichthyostega.de> Date: Sun, 14 Aug 2011 03:09:05 +0200 Subject: [PATCH 149/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + + +#ifndef PROC_ENGINE_CALC_STREAM_H +#define PROC_ENGINE_CALC_STREAM_H + + +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +// +//#include <boost/noncopyable.hpp> +//#include <boost/scoped_ptr.hpp> +//#include <string> + + +namespace proc { +namespace 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 <i>calculation stream</i>. 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]] </pre> </div> -<div title="ExitNode" modifier="Ichthyostega" modified="201106210120" created="200706220322" tags="def" changecount="3"> +<div title="ExitNode" modifier="Ichthyostega" modified="201108140105" created="200706220322" tags="def" changecount="4"> <pre>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") </pre> </div> <div title="ExplicitPlacement" modifier="Ichthyostega" modified="201012122052" created="200706220304" tags="def" changecount="6"> @@ -3397,7 +3397,7 @@ Thus there are two serious problem situations &rarr; SchedulerRequirements &rarr; OutputSlotImpl</pre> </div> -<div title="OutputSlotImpl" modifier="Ichthyostega" modified="201107110047" created="201107102343" tags="spec operational" changecount="25"> +<div title="OutputSlotImpl" modifier="Ichthyostega" modified="201108112339" created="201107102343" tags="spec operational" changecount="26"> <pre>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 <prg@ichthyostega.de> Date: Mon, 15 Aug 2011 00:11:12 +0200 Subject: [PATCH 150/496] 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<class TI, class TAR> struct Link - : Mutator<TI> + : Mutation , Builder<TI,TAR> { @@ -255,15 +255,15 @@ namespace mutation { static TI mutateLength (TimeSpan& target, Duration const& change) { - Mutator<TimeSpan>::imposeChange (target.duration(), change); + imposeChange (target.duration(), change); return Builder<TI,TimeSpan>::buildChangedValue(target); } static TimeSpan mutateTimeSpan (TimeSpan& target, TimeSpan const& change) { - Mutator<TimeSpan>::imposeChange (target.duration(), change.duration()); - Mutator<TimeSpan>::imposeChange (target,change.start()); + imposeChange (target.duration(), change.duration()); + imposeChange (target,change.start()); return Builder<TimeSpan,TimeSpan>::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 <prg@ichthyostega.de> Date: Mon, 15 Aug 2011 00:12:13 +0200 Subject: [PATCH 151/496] 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 <prg@ichthyostega.de> Date: Mon, 15 Aug 2011 00:52:12 +0200 Subject: [PATCH 152/496] 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<class IMP, typename A1> - PolymorphicValue (IMP*, A1& a1) + PolymorphicValue (IMP*, A1 a1) { REQUIRE (siz >= sizeof(IMP)); new(&buf_) IMP (a1); } template<class IMP, typename A1, typename A2> - PolymorphicValue (IMP*, A1& a1, A2& a2) + PolymorphicValue (IMP*, A1 a1, A2 a2) { REQUIRE (siz >= sizeof(IMP)); new(&buf_) IMP (a1,a2); } template<class IMP, typename A1, typename A2, typename A3> - PolymorphicValue (IMP*, A1& a1, A2& a2, A3& a3) + PolymorphicValue (IMP*, A1 a1, A2 a2, A3 a3) { REQUIRE (siz >= sizeof(IMP)); new(&buf_) IMP (a1,a2,a3); @@ -444,13 +444,13 @@ namespace lib { Adapter() : IMP() { } template<typename A1> - Adapter (A1& a1) : IMP(a1) { } + Adapter (A1 a1) : IMP(a1) { } template<typename A1, typename A2> - Adapter (A1& a1, A2& a2) : IMP(a1,a2) { } + Adapter (A1 a1, A2 a2) : IMP(a1,a2) { } template<typename A1, typename A2, typename A3> - Adapter (A1& a1, A2& a2, A3& a3) : IMP(a1,a2,a3) { } + Adapter (A1 a1, A2 a2, A3 a3) : IMP(a1,a2,a3) { } /* using default copy and assignment */ }; @@ -505,31 +505,31 @@ namespace lib { static PolymorphicValue build () { - Adapter<IMP>* type_to_build_in_buffer; + Adapter<IMP>* type_to_build_in_buffer(0); return PolymorphicValue (type_to_build_in_buffer); } template<class IMP, typename A1> static PolymorphicValue - build (A1& a1) + build (A1 a1) { - Adapter<IMP>* type_to_build_in_buffer; + Adapter<IMP>* type_to_build_in_buffer(0); return PolymorphicValue (type_to_build_in_buffer, a1); } template<class IMP, typename A1, typename A2> static PolymorphicValue - build (A1& a1, A2& a2) + build (A1 a1, A2 a2) { - Adapter<IMP>* type_to_build_in_buffer; + Adapter<IMP>* type_to_build_in_buffer(0); return PolymorphicValue (type_to_build_in_buffer, a1,a2); } template<class IMP, typename A1, typename A2, typename A3> static PolymorphicValue - build (A1& a1, A2& a2, A3& a3) + build (A1 a1, A2 a2, A3 a3) { - Adapter<IMP>* type_to_build_in_buffer; + Adapter<IMP>* type_to_build_in_buffer(0); return PolymorphicValue (type_to_build_in_buffer, a1,a2,a3); } From 3125d1c5735139bea31d51bbe4d344cde986ae69 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 15 Aug 2011 00:52:56 +0200 Subject: [PATCH 153/496] 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 <string> -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<DefaultQoS> (PLAYBACK); + EngineService::QoS_Definition EngineService::QoS_BACKGROUND = QoS_Definition::build<DefaultQoS> (BACKGROUND); + EngineService::QoS_Definition EngineService::QoS_COMPROMISE = QoS_Definition::build<Compromise> (PLAYBACK); + EngineService::QoS_Definition EngineService::QoS_PERFECT_RESULT = QoS_Definition::build<DefaultQoS> (RENDER); + EngineService::QoS_Definition EngineService::QoS_SYNC_PRIORITY = QoS_Definition::build<PriorityQoS>(); + }} // namespace proc::engine 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 <boost/noncopyable.hpp> //#include <boost/scoped_ptr.hpp> //#include <string> -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<DummyPlayerService> 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<Quality> _ClonableQoS_Strategy; + typedef lib::PolymorphicValue<Quality, QoS_IMPL_SIZE> 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file 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 <boost/noncopyable.hpp> +//#include <string> +//#include <vector> +//#include <tr1/memory> +//#include <boost/scoped_ptr.hpp> + + +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. </pre> </div> -<div title="DesignPlayerSubsystem" modifier="Ichthyostega" modified="201106122319" created="201105220216" tags="Player design draft" changecount="13"> +<div title="DesignPlayerSubsystem" modifier="Ichthyostega" modified="201108141649" created="201105220216" tags="Player design draft" changecount="14"> <pre>//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. </pre> </div> -<div title="EngineFaçade" modifier="Ichthyostega" modified="201105222330" created="201105222328" tags="Rendering overview spec" changecount="2"> -<pre>The primary interface used by the upper application layers to interact with the render engine, to create and manage ongoing [[calculation streams|CalculationStream]]. +<div title="EngineFaçade" modifier="Ichthyostega" modified="201108141650" created="201105222328" tags="Rendering overview spec" changecount="5"> +<pre>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 </pre> @@ -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 </pre> </div> -<div title="FixtureStorage" modifier="Ichthyostega" modified="201105222258" created="201012140231" tags="Builder impl operational draft" changecount="33"> +<div title="FixtureStorage" modifier="Ichthyostega" modified="201108141647" created="201012140231" tags="Builder impl operational draft" changecount="34"> <pre>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) </pre> </div> -<div title="FrameDispatcher" modifier="Ichthyostega" modified="201105222346" created="201105222330" tags="def Rendering" changecount="2"> -<pre>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</pre> +<div title="FrameDispatcher" modifier="Ichthyostega" modified="201108141651" created="201105222330" tags="def Rendering" changecount="3"> +<pre>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</pre> </div> <div title="FullScreenPlugin" modifier="CehTeh" modified="200706110313" created="200607241016" tags="systemConfig lewcidExtension" server.type="file" server.host="file:///home/ct/.homepage/home.html" server.page.revision="200706110313"> <pre>/*** @@ -4283,12 +4283,12 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T <<tasksum end>> </pre> </div> -<div title="PlayProcess" modifier="Ichthyostega" modified="201106231449" created="201012181714" tags="def spec Player img" changecount="12"> +<div title="PlayProcess" modifier="Ichthyostega" modified="201108141652" created="201012181714" tags="def spec Player img" changecount="14"> <pre>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. </pre> </div> <div title="PlayService" modifier="Ichthyostega" modified="201105221918" created="201105221900" tags="Player spec draft" changecount="9"> From 737765260d9a4e609a41441ddbb4ac73b06a9b58 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 16 Aug 2011 03:02:55 +0200 Subject: [PATCH 154/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file engine-diagnostics.hpp + ** An facility to check and monitor engine operations. + ** Once created, an EngineDiagnostics object connects to the EngineService + ** interface to activate additional tracing facilities within the engine, + ** allowing to watch and verify the creation of individual jobs and further + ** engine state parameters. + ** + ** @see EngineInterface + ** @see engine-interface-test.cpp + ** @see calc-stream-test.cpp + */ + + +#ifndef PROC_ENGINE_ENGINE_DIAGNOSTICS_H +#define PROC_ENGINE_ENGINE_DIAGNOSTICS_H + + +#include "lib/error.hpp" +#include "proc/engine/engine-service.hpp" +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "proc/engine/calc-stream.hpp" +//#include "proc/mobject/model-port.hpp" +//#include "proc/play/timings.hpp" +//#include "proc/play/output-slot.hpp" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +//#include "lib/polymorphic-value.hpp" +//#include "lib/singleton.hpp" +// +#include <boost/noncopyable.hpp> +//#include <boost/scoped_ptr.hpp> +//#include <string> + + +namespace proc { +namespace engine{ + +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// using lumiera::DummyPlayer; + + + + + + /****************************************************** + * 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> 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 <i>calculation stream</i>. 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 <i>calculation stream</i>. + ** This represents a series of calculations, expected to happen in a timely fashion and in order + ** to deliver a frame data stream onto an opened output connection. On the implementation side, + ** a calculation stream will be translated into a series of jobs invoking render nodes, + ** to be executed through the scheduler in the backend layer. + ** + ** While the individual CalcStram is simple, linear and unmodifiable, any CalcStream may be + ** \em superseded by a new definition. In this case, the engine will care for a seamless + ** switch and continuation; under the hood, there is a mechanism to discard resources + ** tied to the original CalcStream, once the switch to the new definition is complete. + ** + ** @see EngineInterface_test + ** @see CalcStream_test ** @see proc::play::PlayerService */ @@ -46,6 +58,7 @@ //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" #include "lib/polymorphic-value.hpp" +#include "lib/singleton.hpp" // #include <boost/noncopyable.hpp> //#include <boost/scoped_ptr.hpp> @@ -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<DummyPlayerService> 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<Quality> _ClonableQoS_Strategy; - typedef lib::PolymorphicValue<Quality, QoS_IMPL_SIZE> QoS_Definition; + typedef lib::polyvalue::CloneValueSupport<Quality> _Clonable_QoS_Strategy; + typedef lib::PolymorphicValue<Quality, QoS_IMPL_SIZE, _Clonable_QoS_Strategy> QoS_Definition; static QoS_Definition QoS_DEFAULT; static QoS_Definition QoS_BACKGROUND; @@ -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<EngineService> 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 <<END +END + + +PLANNED "Engine calculation streams" CalcStream_test <<END +END + + PLANNED "Proc Node basics" NodeBasic_test <<END END diff --git a/tests/47playout.tests b/tests/47playout.tests new file mode 100644 index 000000000..d59689f3b --- /dev/null +++ b/tests/47playout.tests @@ -0,0 +1,7 @@ +TESTING "Component Test Suite: Player and Output" ./test-components --group=player + + + +PLANNED "Output Slot Protocol" OutputSlotProtocol_test <<END +return: 0 +END diff --git a/tests/components/proc/engine/calc-stream-test.cpp b/tests/components/proc/engine/calc-stream-test.cpp new file mode 100644 index 000000000..9175d1fb2 --- /dev/null +++ b/tests/components/proc/engine/calc-stream-test.cpp @@ -0,0 +1,76 @@ +/* + CalcStream(Test) - detailed coverage of engine calculation streams + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/error.hpp" + +#include "proc/engine/engine-service.hpp" + +//#include <boost/scoped_ptr.hpp> +//#include <iostream> +//#include <ctime> + +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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/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 <boost/scoped_ptr.hpp> +//#include <iostream> +//#include <ctime> + +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<Pipe> 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 </pre> </div> -<div title="FixtureStorage" modifier="Ichthyostega" modified="201108141647" created="201012140231" tags="Builder impl operational draft" changecount="34"> +<div title="FixtureStorage" modifier="Ichthyostega" modified="201108142259" created="201012140231" tags="Builder impl operational draft" changecount="35"> <pre>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 <prg@ichthyostega.de> Date: Thu, 18 Aug 2011 12:31:26 +0200 Subject: [PATCH 155/496] 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<Pipe> 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 <prg@ichthyostega.de> Date: Wed, 24 Aug 2011 01:26:36 +0200 Subject: [PATCH 156/496] 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 <prg@ichthyostega.de> Date: Thu, 25 Aug 2011 15:08:27 +0200 Subject: [PATCH 157/496] 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 <prg@ichthyostega.de> Date: Sun, 4 Sep 2011 01:54:36 +0200 Subject: [PATCH 158/496] 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 <glibmm.h> #include <sigc++/sigc++.h> 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<BuffHandle> + class BufferDescriptor { - typedef lumiera::StreamType::ImplFacade::DataBuffer Buff; - typedef Buff* PBuff; + BufferProvider* provider_; + uint64_t subClassification_; - PBuff - operator->() const - { - return pBuffer_; - } - Buff& - operator* () const - { - ENSURE (pBuffer_); - return *pBuffer_; - } + BufferDescriptor(BufferProvider& manager, uint64_t detail) + : provider_(&manager) + , subClassification_(detail) + { } - bool - isValid() const - { - return pBuffer_; - } + friend class BufferProvider; + public: + // using standard copy operations - //////////////////////TODO: the whole logic how to create a BuffHandle needs to be solved in a more clever way. --> TICKET #249 - BuffHandle() - : pBuffer_(0), - sourceID_(0) - { } - - /** - * @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<BuffHandle> + { + 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.// </pre> </div> -<div title="BufferProvider" modifier="Ichthyostega" modified="201107090002" created="201107082330" tags="Rendering spec draft" changecount="5"> -<pre>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... +<div title="BuffHandle" modifier="Ichthyostega" modified="201109032244" created="201109021617" tags="Rendering spec draft" changecount="6"> +<pre>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. +</pre> +</div> +<div title="BufferProvider" modifier="Ichthyostega" modified="201109031555" created="201107082330" tags="Rendering spec draft" changecount="11"> +<pre>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 </pre> </div> -<div title="Fixture" modifier="Ichthyostega" modified="201012162259" created="200706220324" tags="def spec Builder Model" changecount="47"> -<pre>a specially configured view -- joining together high-level and low-level model +<div title="Fixture" modifier="Ichthyostega" modified="201109031601" created="200706220324" tags="def spec Builder Model" changecount="48"> +<pre>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.// </pre> </div> -<div title="FixtureDatastructure" modifier="Ichthyostega" modified="201105222242" created="201012162304" tags="spec Builder" changecount="9"> -<pre>Generally speaking, the [[here|Fixture]] comprised of a ModelPortRegistry and a set of [[segmentations|Segmentation]] per Timeline. +<div title="FixtureDatastructure" modifier="Ichthyostega" modified="201109031600" created="201012162304" tags="spec Builder" changecount="11"> +<pre>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) </pre> </div> -<div title="OutputManagement" modifier="Ichthyostega" modified="201106210136" created="201007090155" tags="Model Rendering Player spec draft" changecount="31"> +<div title="OutputManagement" modifier="Ichthyostega" modified="201108251240" created="201007090155" tags="Model Rendering Player spec draft" changecount="37"> <pre>//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. </pre> </div> -<div title="OutputSlot" modifier="Ichthyostega" modified="201107102318" created="201106162339" tags="def Concepts Player spec" changecount="26"> +<div title="OutputSlot" modifier="Ichthyostega" modified="201109021531" created="201106162339" tags="def Concepts Player spec" changecount="27"> <pre>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 <prg@ichthyostega.de> Date: Fri, 16 Sep 2011 01:58:13 +0200 Subject: [PATCH 159/496] 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<BuffHandle> 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<BuffHaXXXX> hTab_; vector<BuffHandle::PBuff> 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. </pre> </div> -<div title="BufferProvider" modifier="Ichthyostega" modified="201109031555" created="201107082330" tags="Rendering spec draft" changecount="11"> +<div title="BufferManagement" modifier="Ichthyostega" modified="201109151442" created="201109151420" tags="Rendering Player spec draft" changecount="9"> +<pre>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. + +</pre> +</div> +<div title="BufferProvider" modifier="Ichthyostega" modified="201109151410" created="201107082330" tags="Rendering spec draft" changecount="12"> <pre>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 </pre> </div> @@ -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}}} </pre> </div> -<div title="NodeOperationProtocol" modifier="Ichthyostega" modified="200909031217" created="200806010251" tags="Rendering operational" changecount="16"> -<pre>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. +<div title="NodeOperationProtocol" modifier="Ichthyostega" modified="201109151517" created="200806010251" tags="Rendering operational" changecount="24"> +<pre>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]] </pre> </div> -<div title="OverviewRenderEngine" modifier="Ichthyostega" modified="200906071810" created="200706190647" tags="Rendering overview img" changecount="20"> +<div title="OverviewRenderEngine" modifier="Ichthyostega" modified="201109151519" created="200706190647" tags="Rendering overview img" changecount="21"> <pre>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]] </pre> </div> @@ -4961,23 +4996,26 @@ At first sight the link between asset and clip-MO is a simple logical relation b &rarr; EngineFaçade </pre> </div> -<div title="RenderEntities" modifier="Ichthyostega" modified="200906071810" created="200706190715" tags="Rendering classes img" changecount="10"> +<div title="RenderEntities" modifier="Ichthyostega" modified="201109112229" created="200706190715" tags="Rendering classes img" changecount="13"> <pre>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]] </pre> </div> -<div title="RenderImplDetails" modifier="Ichthyostega" modified="201107082336" created="200806220211" tags="Rendering impl img" changecount="23"> +<div title="RenderImplDetails" modifier="Ichthyostega" modified="201109151533" created="200806220211" tags="Rendering impl img" changecount="29"> <pre>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):@@ </pre> </div> -<div title="RenderMechanics" modifier="Ichthyostega" modified="200906071809" created="200806030230" tags="Rendering operational impl img" changecount="28"> +<div title="RenderMechanics" modifier="Ichthyostega" modified="201109151533" created="200806030230" tags="Rendering operational impl img" changecount="29"> <pre>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) } //}}}</pre> </div> -<div title="StateAdapter" modifier="Ichthyostega" modified="200807132344" created="200806261912" tags="Rendering impl def" changecount="10"> -<pre>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.</pre> +<div title="StateAdapter" modifier="Ichthyostega" modified="201109151535" created="200806261912" tags="Rendering impl def" changecount="11"> +<pre>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.</pre> </div> <div title="StateProxy" modifier="Ichthyostega" modified="201107082334" created="200706220352" tags="def" changecount="5"> <pre>An Object representing a //Render Process// and containing associated state information. From 1f13931640fe09f84b798722a6edd2aa95ebe7ea Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 17 Sep 2011 01:50:11 +0200 Subject: [PATCH 160/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file diagnostic-buffer-provider.hpp + ** An facility for writing unit-tests targetting the BufferProvider interface. + ** + ** @see buffer-provider-protocol-test.cpp + */ + +#ifndef PROC_ENGINE_DIAGNOSTIC_BUFFR_PROVIDER_H +#define PROC_ENGINE_DIAGNOSTIC_BUFFR_PROVIDER_H + + +#include "lib/error.hpp" +#include "proc/engine/buffer-provider.hpp" + +#include <boost/noncopyable.hpp> + + +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 <<END +return: 0 +END + + PLANNED "BuffTable_test" BuffTable_test <<END return: 0 END diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp new file mode 100644 index 000000000..c3adb7f9e --- /dev/null +++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp @@ -0,0 +1,121 @@ +/* + BufferProviderProtocol(Test) - demonstration of buffer provider usage cycle + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/error.hpp" +#include "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 <boost/format.hpp> +//#include <iostream> + +//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<TestFrame>(); + CHECK (buff.isValid()); + CHECK (sizeof(TestFrame) <= buff.size()); + buff.create<TestFrame>() = 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<TestFrame> ()); + CHECK (checker.object_was_destroyed<TestFrame> ()); + + 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 </pre> </div> -<div title="BufferProvider" modifier="Ichthyostega" modified="201109151410" created="201107082330" tags="Rendering spec draft" changecount="12"> +<div title="BufferProvider" modifier="Ichthyostega" modified="201109162209" created="201107082330" tags="Rendering spec draft" changecount="13"> <pre>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 <prg@ichthyostega.de> Date: Sat, 17 Sep 2011 17:29:16 +0200 Subject: [PATCH 161/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef ENGINE_BUFFHTABLE_OBSOLETE_H +#define ENGINE_BUFFHTABLE_OBSOLETE_H + + +#include "lib/error.hpp" +#include "proc/engine/buffhandle.hpp" +#include "proc/engine/procnode.hpp" + +#include <boost/noncopyable.hpp> +#include <vector> +#include <utility> + + +////////////////////////////////WARNING: obsolete code +////////////////////////////////WARNING: ...just left in tree to keep it compiling +////////////////////////////////TICKET #826 need to be reworked entirely + +namespace engine { + + using std::pair; + using std::vector; + + + /** + * Obsolete, to be rewritten /////TICKET #826 + * + * Tables of buffer handles and corresponding dereferenced buffer pointers. + * Used within the invocation of a processing node to calculate data. + * The tables are further differentiated into input data buffers and output + * data buffers. The tables are supposed to be implemented as bare "C" arrays, + * thus the array of real buffer pointers can be fed directly to the + * processing function of the respective node. + * + * @todo this whole design is a first attempt and rather clumsy. It should be reworked + * to use a single contiguous memory area and just layer the object structure on top + * (by using placement new). Yet the idea of an stack-like organisation should be retained + */ + struct BuffTable + { + typedef BuffHandle * PHa; + typedef BuffHandle::PBuff * PBu; + typedef pair<PHa const,PBu const> Chunk; + + PHa outHandle; + PHa inHandle; + PBu outBuff; + PBu inBuff; + }; + + + /** Obsolete, to be rewritten /////TICKET #826 */ + class BuffTableStorage + { + /////////////////////////////////////////////////////////////////////////TICKET #826 need to be reworked entirely + /** just a placeholder to decouple the existing code + * from the reworked BuffHandle logic. The existing + * code in turn will be reworked rather fundamentally + */ + struct BuffHaXXXX + : BuffHandle + { + BuffHaXXXX() : BuffHandle(just_satisfy_the_compiler()) { /* wont work ever */ } + static BufferDescriptor const& + just_satisfy_the_compiler() { } + }; + + ////////////////////////////////////TICKET #825 should be backed by mpool and integrated with node invocation + vector<BuffHaXXXX> hTab_; + vector<BuffHandle::PBuff> pTab_; + size_t level_; + + public: + BuffTableStorage (const size_t maxSiz) + : hTab_(maxSiz), + pTab_(maxSiz), + level_(0) + { } + + ~BuffTableStorage() { ASSERT (0==level_, "buffer management logic broken."); } + + protected: + + friend class BuffTableChunk; + + /** allocate the given number of slots + * starting at current level to be used + * by the newly created BuffTableChunk + */ + BuffTable::Chunk + claim (uint slots) + { + ASSERT (pTab_.size() == hTab_.size()); + REQUIRE (level_+slots <= hTab_.size()); + + size_t prev_level (level_); + level_ += slots; + return std::make_pair (&hTab_[prev_level], + &pTab_[prev_level]); + } + + void + release (uint slots) + { + ASSERT (slots <= level_); + REQUIRE (level_ <= hTab_.size()); + REQUIRE (level_ <= pTab_.size()); + + level_ -= slots; + } + + bool + level_check (BuffTable::Chunk& prev_level) + { + return prev_level.first == &hTab_[level_] + && prev_level.second == &pTab_[level_]; + } + }; + + + /** Obsolete, to be rewritten /////TICKET #826 + * to be allocated on the stack while evaluating a ProcNode#pull() call. + * The "current" State (StateProxy) maintains a BuffTableStorage (=pool), + * which can be used to crate such chunks. The claiming and releasing of + * slots in the BuffTableStorage is automatically tied to BuffTableChunk + * object's lifecycle. + */ + class BuffTableChunk + : public BuffTable, + boost::noncopyable + { + const uint siz_; + BuffTable::Chunk tab_; + BuffTableStorage& sto_; + + public: + BuffTableChunk (WiringDescriptor const& wd, BuffTableStorage& storage) + : siz_(wd.nrI + wd.nrO), + tab_(storage.claim (siz_)), + sto_(storage) + { + const uint nrO(wd.nrO); + + // Setup the publicly visible table locations + this->outHandle = &tab_.first[ 0 ]; + this->inHandle = &tab_.first[nrO]; + this->outBuff = &tab_.second[ 0 ]; + this->inBuff = &tab_.second[nrO]; + } + + ~BuffTableChunk () + { + sto_.release (siz_); + ASSERT ( sto_.level_check (tab_), + "buffer management logic broken."); + } + }; + + + + + +} // namespace engine +#endif 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 <boost/scoped_ptr.hpp> @@ -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 <prg@ichthyostega.de> Date: Sat, 17 Sep 2011 18:25:45 +0200 Subject: [PATCH 162/496] 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 <boost/format.hpp> //#include <iostream> @@ -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<TestFrame>(); + 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 <prg@ichthyostega.de> Date: Sun, 18 Sep 2011 01:02:31 +0200 Subject: [PATCH 163/496] 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 </pre> </div> +<div title="BufferTable" modifier="Ichthyostega" created="201109172253" tags="def spec Rendering draft" changecount="1"> +<pre>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 +</pre> +</div> <div title="BuildFixture" modifier="Ichthyostega" modified="201011290504" created="201011282003" tags="Builder spec operational" changecount="32"> <pre>//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}}} </pre> </div> -<div title="NodeOperationProtocol" modifier="Ichthyostega" modified="201109151517" created="200806010251" tags="Rendering operational" changecount="24"> +<div title="NodeOperationProtocol" modifier="Ichthyostega" modified="201109172254" created="200806010251" tags="Rendering operational" changecount="25"> <pre>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):@@ </pre> </div> -<div title="RenderMechanics" modifier="Ichthyostega" modified="201109151533" created="200806030230" tags="Rendering operational impl img" changecount="29"> +<div title="RenderMechanics" modifier="Ichthyostega" modified="201109172221" created="200806030230" tags="Rendering operational impl img" changecount="32"> <pre>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 <prg@ichthyostega.de> Date: Sun, 18 Sep 2011 15:05:18 +0200 Subject: [PATCH 164/496] 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 <prg@ichthyostega.de> Date: Sun, 18 Sep 2011 15:28:58 +0200 Subject: [PATCH 165/496] 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<typename BU> + 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<typename BU> + 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<typename BU> + 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<typename BU> + 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<TestFrame>() = 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef PROC_ENGINE_TESTFRAME_H +#define PROC_ENGINE_TESTFRAME_H + + +//#include "lib/time/timevalue.hpp" + +//#include <string> + + +//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 <prg@ichthyostega.de> Date: Mon, 19 Sep 2011 01:42:37 +0200 Subject: [PATCH 166/496] 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<typename BU> BU& create(); + template<typename BU> + 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<typename BU> + 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<typename BU> + bool + object_was_attached (uint bufferID) + { + UNIMPLEMENTED ("verify object attachment status of a specific buffer"); + } + + + template<typename BU> + 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<TestFrame>() = testData(0); -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829 - TestFrame* storage = *buff; - CHECK (testData(0) == *storage); + TestFrame& storage = buff.accessAs<TestFrame>(); + CHECK (testData(0) == storage); buff.release(); CHECK (!buff.isValid()); - VERIFY_ERROR (LIFECYCLE, *buff ); + VERIFY_ERROR (LIFECYCLE, buff.accessAs<TestFrame>() ); - 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<TestFrame> ()); - CHECK (checker.object_was_destroyed<TestFrame> ()); + CHECK (checker.object_was_attached<TestFrame> (0)); + CHECK (checker.object_was_destroyed<TestFrame> (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 <prg@ichthyostega.de> Date: Tue, 20 Sep 2011 00:54:01 +0200 Subject: [PATCH 167/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/error.hpp" +#include "include/logging.h" +#include "lib/meta/function.hpp" +#include "lib/scoped-ptrvect.hpp" + +#include "proc/engine/diagnostic-buffer-provider.hpp" + +#include <boost/scoped_array.hpp> +//#include <vector> + +using lib::ScopedPtrVect; +using boost::scoped_array; + + + +namespace engine { + + + /** Storage for the diagnostics frontend */ + lib::Singleton<DiagnosticBufferProvider> DiagnosticBufferProvider::diagnostics; + + + class Block + : boost::noncopyable + { + size_t size_; + scoped_array<char> storage_; + + bool was_locked_; + + public: + Block() + : size_(0) + , storage_() + , was_locked_(false) + { } + + bool + was_used() const + { + return was_locked_; + } + }; + + + + 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<typename FUN> + struct _Query + { + typedef typename FunctionSignature<FUN>::Ret Result; + }; + + function<bool(Block&)> 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<Block> + { + 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 FUN> + typename _Query<FUN>::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 <boost/scoped_ptr.hpp> #include <boost/noncopyable.hpp> 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<HeapMemProvider> pImpl_; + static lib::Singleton<DiagnosticBufferProvider> 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<typename BU> 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<typename BU> 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 <prg@ichthyostega.de> Date: Wed, 21 Sep 2011 02:23:12 +0200 Subject: [PATCH 168/496] 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<class X> + 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<typename BU> + BufferDescriptor getDescriptor(); + /* === API for BuffHandle internal access === */ @@ -111,5 +118,13 @@ namespace engine { } + template<typename BU> + 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<typename FUN> - struct _Query - { - typedef typename FunctionSignature<FUN>::Ret Result; - }; - - function<bool(Block&)> 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 FUN> - typename _Query<FUN>::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<typename BU> 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<TestFrame> (0)); CHECK (checker.object_was_destroyed<TestFrame> (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<TestFrame>(); 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 <prg@ichthyostega.de> Date: Thu, 22 Sep 2011 18:57:06 +0200 Subject: [PATCH 169/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file buffer-metadate.hpp + ** Metadata for managing and accessing buffers. The Lumiera Engine uses the + ** Abstraction of an BufferProvider to handle various kinds of buffer organisation + ** and access in a uniform way. Actually, buffers can be exposed and provided by several + ** facilities, which might even be implemented through an external library. Thus the engine + ** and the abstraction placed in between needs a common set of control data, to be able to + ** expose the correct buffer for each request. Typically -- and independent of the actual + ** implementation -- the following properties need to be tracked + ** - that overall storage size available within the buffer + ** - a pair of custom \em creator and \em destructor functions to use together with this buffer + ** - an additional client key to distinguish otherwise otherwise identical client requests + ** These three distinctions are applied in sequence, thus forming a tree with 3 levels. + ** Only the first distinguishing level (the size) is mandatory. The others are provided, + ** because some of the foreseeable buffer providers allow to re-access the data placed + ** into the buffer, by assigning an internally managed ID to the buffer. The most + ** prominent example is the frame cache, which obviously needs to keep track of + ** the buffers after the render engine is finished, while the engine code + ** just accesses yet another buffer to place the results of calculations. + ** + ** These additional distinctions and properties are associated with the help of the + ** BufferDescriptor, embedded into each BuffHandle. While the engine just uses these + ** handles in the way of a pointer, the buffer descriptor acts as an additional tag + ** attached to the buffer access, allowing to re-access a context within the + ** buffer provider implementation. + ** + ** @see buffer-provider.hpp + ** @see BufferMetadata_test + ** @see BufferProviderProtocol_test + */ + +#ifndef PROC_ENGINE_BUFFR_METADATA_H +#define PROC_ENGINE_BUFFR_METADATA_H + + +#include "lib/error.hpp" + +//#include <boost/noncopyable.hpp> + + +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<class X> + 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<class X> - 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 <<END +PLANNED "BufferMetadata_test" buffer metadata and state transitions <<END +END + + +PLANNED "BuffTable_tsst" buffer table <<END return: 0 END diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp new file mode 100644 index 000000000..a8573bfd0 --- /dev/null +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -0,0 +1,146 @@ +/* + BufferMetadata(Test) - properties of internal data buffer metadata + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/error.hpp" +#include "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 <boost/format.hpp> +#include <cstdlib> +//#include <iostream> + +//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 <prg@ichthyostega.de> Date: Fri, 23 Sep 2011 15:31:20 +0200 Subject: [PATCH 170/496] 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 <boost/noncopyable.hpp> 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 <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> 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<Metadata> 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 <boost/format.hpp> +#include <boost/scoped_ptr.hpp> #include <cstdlib> //#include <iostream> @@ -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<Metadata> 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 <prg@ichthyostega.de> Date: Sat, 24 Sep 2011 02:27:12 +0200 Subject: [PATCH 171/496] 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.// </pre> </div> -<div title="BuffHandle" modifier="Ichthyostega" modified="201109032244" created="201109021617" tags="Rendering spec draft" changecount="6"> -<pre>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. +<div title="BuffHandle" modifier="Ichthyostega" modified="201109232348" created="201109021617" tags="Rendering spec draft" changecount="8"> +<pre>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. </pre> </div> -<div title="BufferManagement" modifier="Ichthyostega" modified="201109151442" created="201109151420" tags="Rendering Player spec draft" changecount="9"> +<div title="BufferManagement" modifier="Ichthyostega" modified="201109232234" created="201109151420" tags="Rendering Player spec draft" changecount="10"> <pre>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. </pre> </div> -<div title="BufferProvider" modifier="Ichthyostega" modified="201109162209" created="201107082330" tags="Rendering spec draft" changecount="13"> +<div title="BufferMetadata" modifier="Ichthyostega" modified="201109240026" created="201109240004" tags="spec Rendering draft" changecount="6"> +<pre>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. +</pre> +</div> +<div title="BufferProvider" modifier="Ichthyostega" modified="201109232347" created="201107082330" tags="Rendering spec draft" changecount="20"> <pre>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</pre> </div> -<div title="OutputSlotImpl" modifier="Ichthyostega" modified="201108112339" created="201107102343" tags="spec operational" changecount="26"> -<pre>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. +<div title="OutputSlotImpl" modifier="Ichthyostega" modified="201109232235" created="201107102343" tags="spec operational" changecount="27"> +<pre>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 <prg@ichthyostega.de> Date: Sat, 24 Sep 2011 15:27:58 +0200 Subject: [PATCH 172/496] 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 <<END +PLANNED "Buffer provider diagnostics" BufferProviderProtocol_test <<END return: 0 END -PLANNED "BufferMetadata_test" buffer metadata and state transitions <<END +PLANNED "buffer metadata and state transitions" BufferMetadata_test <<END END -PLANNED "BuffTable_tsst" buffer table <<END +PLANNED "buffer table" BuffTable_test <<END return: 0 END diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp index 839307aad..b6050b69a 100644 --- a/tests/components/proc/engine/buffer-metadata-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -66,6 +66,7 @@ namespace test { const size_t SIZE_B = 1 + rand() % TEST_MAX_SIZE; const HashVal JUST_SOMETHING = 123; + const void* const SOME_POINTER = &JUST_SOMETHING; // const uint TEST_SIZE = 1024*1024; // const uint TEST_ELMS = 20; @@ -131,12 +132,31 @@ namespace test { Metadata::Entry& m1 = meta_->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 <prg@ichthyostega.de> Date: Sun, 25 Sep 2011 04:07:30 +0200 Subject: [PATCH 173/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file simple-allocator.hpp + ** Frontend and marker interface for allocating small objects explicitly. + ** Contrary to the TypedAllocationManager, the SimpleAllocator doesn't provide any + ** ref-counting or tracking facilities, nor does he support bulk de-allocation. + ** Each object needs to be allocated and released by explicit call. + ** The advantage over using std::allocator directly is the shortcut for (placement) construction, + ** and -- of course -- the ability to exchange the memory model at one central location. + ** + ** SimpleAllocator instances will be defined for a specific collection of types; for each of those + ** types, there will be an embedded dedicated custom allocator (currently as of 9/2011, just + ** implemented as std::allocator<TY>). Objects of these preconfigured types can be constructed + ** and destroyed through this allocator instance. Each call needs to be done explicitly, with + ** the precise, concrete type to be created or destroyed. This is especially important for + ** the releasing of objects: there is \em no support for any kind of virtual destruction. + ** + ** @see engine::BufferMetadata + ** @see TypedAllocationManager (another more elaborate custom allocation scheme) + ** + */ + + + +#ifndef LIB_SIMPLE_ALLOCATOR_H +#define LIB_SIMPLE_ALLOCATOR_H + +//#include "pre.hpp" +#include "lib/error.hpp" +#include "lib/meta/generator.hpp" +#include "lib/format.hpp" +//#include "lib/typed-counter.hpp" +#include "include/logging.h" + + +//#include <tr1/memory> +#include <boost/static_assert.hpp> +#include <memory> + + + +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<typename TYPES, typename TY> + struct IsInList + { + enum{ value = false }; + }; + + template<typename TY, typename TYPES> + struct IsInList<Node<TY,TYPES>, TY> + { + enum{ value = true }; + }; + + template<typename XX, typename TYPES, typename TY> + struct IsInList<Node<XX,TYPES>, TY> + { + enum{ value = IsInList<TYPES,TY>::value }; + }; + + } + + + template<typename TY> + class CustomAllocator + : public std::allocator<TY> + { + + }; + + + + /** + * 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<typename TYPES> + class SimpleAllocator + : InstantiateForEach< typename TYPES::List // for each of those types... + , CustomAllocator // ...mix in the custom allocator + > + { + + /** forward plain memory allocation */ + template<class XX> + XX * + allocateSlot () + { + TRACE (memory, "allocate %s", util::tyStr<XX>().c_str()); + return CustomAllocator<XX>::allocate (1); + } + + template<class XX> + void + releaseSlot (XX* entry) + { + TRACE (memory, "release %s", util::tyStr<XX>().c_str()); + CustomAllocator<XX>::deallocate (entry, 1); + } + + + template<class XX> + void + ___assertSupportedType() + { + typedef typename TYPES::List PreconfiguredTypes; + typedef IsInList<PreconfiguredTypes,XX> IsPreconfiguredType; + + BOOST_STATIC_ASSERT (IsPreconfiguredType::value); + } + + + + + public: /* ==== build objects with managed allocation ==== */ + +#define _EXCEPTION_SAFE_INVOKE(_CTOR_) \ + \ + ___assertSupportedType<XX>(); \ + XX* storage = allocateSlot<XX>(); \ + try \ + { \ + return (new(storage) _CTOR_ ); \ + } \ + catch(...) \ + { \ + releaseSlot<XX>(storage); \ + throw; \ + } + + template< class XX> + XX* //_____________________ + create () ///< invoke default ctor + { + _EXCEPTION_SAFE_INVOKE ( XX() ) + } + + + template< class XX, typename P1> + XX* //___________________ + create (P1& p1) ///< invoke 1-arg ctor + { + _EXCEPTION_SAFE_INVOKE ( XX (p1) ) + } + + + template< class XX + , typename P1 + , typename P2 + > + XX* //___________________ + create (P1& p1, P2& p2) ///< invoke 2-arg ctor + { + _EXCEPTION_SAFE_INVOKE ( XX (p1,p2) ) + } + + + template< class XX + , typename P1 + , typename P2 + , typename P3 + > + XX* //___________________ + create (P1& p1, P2& p2, P3& p3) ///< invoke 3-arg ctor + { + _EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3) ) + } + + + template< class XX + , typename P1 + , typename P2 + , typename P3 + , typename P4 + > + XX* //___________________ + create (P1& p1, P2& p2, P3& p3, P4& p4) ///< invoke 4-arg ctor + { + _EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4) ) + } + + + template< class XX + , typename P1 + , typename P2 + , typename P3 + , typename P4 + , typename P5 + > + XX* //___________________ + create (P1& p1, P2& p2, P3& p3, P4& p4, P5& p5) ///< invoke 5-arg ctor + { + _EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4,p5) ) + } + +#undef _EXCEPTION_SAFE_INVOKE + + + + template<class XX> + void + destroy (XX* entry) + { + if (!entry) return; + ___assertSupportedType<XX>(); + try + { + entry->~XX(); + } + catch(...) + { + lumiera_err errorID = lumiera_error(); + WARN (common_dbg, "dtor of %s failed: %s", util::tyStr(entry).c_str() + , errorID ); + } + releaseSlot<XX> (entry); + } + + }; + + + + +} // 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 <<END +PLANNED "custom allocator(I)" SimpleAllocator_test <<END +return: 0 +END + + +TEST "custom allocator(II)" TypedAllocationManager_test <<END return: 0 END diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp index b6050b69a..132500fb1 100644 --- a/tests/components/proc/engine/buffer-metadata-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -87,10 +87,10 @@ namespace test { virtual void run (Arg) { - UNIMPLEMENTED ("cover all metadata properties"); CHECK (ensure_proper_fixture()); verifyBasicProperties(); verifyStandardCase(); + UNIMPLEMENTED ("cover all metadata properties"); } diff --git a/tests/lib/simple-allocator-test.cpp b/tests/lib/simple-allocator-test.cpp new file mode 100644 index 000000000..a9034e33a --- /dev/null +++ b/tests/lib/simple-allocator-test.cpp @@ -0,0 +1,160 @@ +/* + SimpleAllocator(Test) - check interface for simple custom allocations + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/simple-allocator.hpp" +#include "lib/util.hpp" + + +//#include <tr1/memory> +#include <cstdlib> +#include <string> + + +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<uint siz> + class DummyObj + { + char crap_[siz]; + + public: + DummyObj() + { + REQUIRE (siz); + for (uint i=0; i<siz; ++i) + checksum_ += (crap_[i] = rand() % 128); + } + + ~DummyObj() + { + for (uint i=0; i<siz; ++i) + checksum_ -= crap_[i]; + } + }; + + typedef SimpleAllocator<Types<DummyObj<1>,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<DummyObj<1> >(); + PD1 pD12 = allocator.create<DummyObj<1> >(); + PD23 pD21 = allocator.create<DummyObj<23> >(); + PD23 pD22 = allocator.create<DummyObj<23> >(); + PS pS11 = allocator.create<string> ("Lumiera"); + PS pS12 = allocator.create<string> ("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<DummyObj<23> > (*pD21); + PS pSxx = allocator.create<string> (*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 <prg@ichthyostega.de> Date: Sun, 25 Sep 2011 15:40:16 +0200 Subject: [PATCH 174/496] 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file typelist-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<class TYPES> + struct count; + template<> + struct count<NullType> + { + enum{ value = 0 }; + }; + template<class TY, class TYPES> + struct count<Node<TY,TYPES> > + { + enum{ value = 1 + count<TYPES>::value }; + }; + + /** + * Metafunction " max( sizeof(T) ) for T in TYPES " + */ + template<class TYPES> + struct maxSize; + template<> + struct maxSize<NullType> + { + enum{ value = 0 }; + }; + template<class TY, class TYPES> + struct maxSize<Node<TY,TYPES> > + { + enum{ thisval = sizeof(TY) + , nextval = maxSize<TYPES>::value + , value = nextval > thisval? nextval:thisval + }; + }; + + + /** pick the n-th element from a typelist */ + template<class TYPES, uint i> + struct Pick + { + typedef NullType Type; + }; + template<class TY, class TYPES> + struct Pick<Node<TY,TYPES>, 0> + { + typedef TY Type; + }; + template<class TY, class TYPES, uint i> + struct Pick<Node<TY,TYPES>, i> + { + typedef typename Pick<TYPES, i-1>::Type Type; + }; + + + + + /** apply a transformation (template) to each type in the list */ + template<class TY, template<class> class _TRANS_> + struct Apply { typedef TY List; }; + + template< class TY, class TYPES + , template<class> class _TRANS_ + > + struct Apply<Node<TY,TYPES>, _TRANS_ > { typedef Node< typename _TRANS_<TY>::Type + , typename Apply<TYPES,_TRANS_>::List + > List; }; + + + /** conditional node: skip an element based on evaluating a predicate */ + template<bool, class T, class TAIL> + struct CondNode { typedef TAIL Next; }; + + template<class T, class TAIL> + struct CondNode<true, T, TAIL> { typedef Node<T,TAIL> Next; }; + + /** filter away those types which don't fulfil a predicate metafunction */ + template< class TYPES + , template<class> class _P_ ///< a template providing a boolean member \c ::value + > + struct Filter; + + template<template<class> class _P_> + struct Filter<NullType,_P_> { typedef NullType List; }; + + template< class TY, class TYPES + , template<class> class _P_ + > + struct Filter<Node<TY,TYPES>,_P_> { typedef typename CondNode< _P_<TY>::value + , TY + , typename Filter<TYPES,_P_>::List + >::Next + List; }; + + + /** append lists-of-types */ + template<class TY1, class TY2> + struct Append { typedef Node<TY1, typename Append<TY2,NullType>::List> List; }; + + template< class TY, class TYPES + , class TAIL + > + struct Append<Node<TY,TYPES>, TAIL> { typedef Node<TY, typename Append<TYPES, TAIL>::List> List; }; + + template<class TY, class TYPES> + struct Append<NullType, Node<TY,TYPES> > { typedef Node<TY,TYPES> List; }; + + template<class TY, class TYPES> + struct Append<Node<TY,TYPES>, NullType> { typedef Node<TY,TYPES> List; }; + + template<class TY1> + struct Append<TY1,NullType> { typedef Node<TY1,NullType> List; }; + + template<class TY2> + struct Append<NullType,TY2> { typedef Node<TY2,NullType> List; }; + + template<> + struct Append<NullType,NullType> { typedef NullType List; }; + + + + + /** access the last list element */ + template<class TYPES> + struct SplitLast; + + template<> + struct SplitLast<NullType> { typedef NullType Type; + typedef NullType List; }; + template<class TY> + struct SplitLast<Node<TY,NullType> > { typedef TY Type; + typedef NullType List; }; + + template<class TY, class TYPES> + struct SplitLast<Node<TY,TYPES> > { typedef typename SplitLast<TYPES>::Type Type; + typedef typename Append< TY, + typename SplitLast<TYPES>::List + >::List + List; }; + + + + /** + * splice a typelist like an overlay + * into an base typelist, starting at given index. + * @return either the combined (spliced) List, or + * the Front/Back part before or after the Overlay + * @note using a NullType as OVERLAY allows to extract + * an arbitrary Front/Back part of the list + */ + template<class BASE, class OVERLAY, uint i=0> + struct Splice; + + template<class B, class BS, + class OVERLAY, uint i> + struct Splice<Node<B,BS>, OVERLAY, i> { typedef Node<B, typename Splice<BS, OVERLAY, i-1>::List> List; + typedef Node<B, typename Splice<BS, OVERLAY, i-1>::Front> Front; + typedef typename Splice<BS, OVERLAY, i-1>::Back Back; }; + + template<class B, class BS, + class O, class OS > + struct Splice<Node<B,BS>,Node<O,OS>,0> { typedef Node<O, typename Splice<BS,OS, 0>::List> List; + typedef NullType Front; + typedef typename Splice<BS,OS, 0>::Back Back; }; + + template<class B, class BS> + struct Splice<Node<B,BS>, NullType, 0> { typedef Node<B, BS> List; + typedef NullType Front; + typedef Node<B, BS> Back; }; + + template<class XX, uint i> + struct Splice<NullType, XX, i> { typedef NullType List; + typedef NullType Front; + typedef NullType Back; }; + + + + + /** + * Allows to access various parts of a given typelist: + * Start and End, Prefix and Tail.. + */ + template<class TYPES> + struct Dissect; + + template<class T, class TYPES> + struct Dissect<Node<T,TYPES> > + { + typedef Node<T,TYPES> List; ///< the complete list + typedef T Head; ///< first element + typedef Node<T,NullType> First; ///< a list containing the first element + typedef TYPES Tail; ///< remainder of the list starting with the second elm. + typedef typename SplitLast<List>::List Prefix;///< all of the list, up to but excluding the last element + typedef typename SplitLast<List>::Type End; ///< the last element + typedef Node<End,NullType> Last; ///< a list containing the last element + }; + + template<> + struct Dissect<NullType> + { + typedef NullType List; + typedef NullType Head; + typedef NullType First; + typedef NullType Tail; + typedef NullType Prefix; + typedef NullType End; + typedef NullType Last; + }; + + + + + /** + * prefix each of the elements, + * yielding a list-of lists-of-types + */ + template<class T, class TY> + struct PrefixAll { typedef Node< typename Append<T,TY>::List, NullType> List; }; + + template<class T> + struct PrefixAll<T, NullType> { typedef NullType List; }; + + template<class T> + struct PrefixAll<T, NodeNull> { typedef Node< typename Append<T,NodeNull>::List, NullType> List; }; + + template< class T + , class TY, class TYPES + > + struct PrefixAll<T, Node<TY,TYPES> > { typedef Node< typename Append<T,TY>::List + , typename PrefixAll<T,TYPES>::List + > List; }; + + + + + /** + * build a list-of lists, where each element of the first arg list + * gets in turn prepended to all elements of the second arg list. + * Can be used to build all possible combinations from two + * sources, i.e. the Cartesian product. + */ + template<class TY1,class TY2> + struct Distribute { typedef typename PrefixAll<TY1,TY2>::List List; }; + + template<class TY> + struct Distribute<NullType,TY> { typedef NullType List; }; + + template< class TY, class TYPES + , class TAIL + > + struct Distribute<Node<TY,TYPES>,TAIL> { typedef typename Append< typename PrefixAll<TY,TAIL>::List + , typename Distribute<TYPES,TAIL>::List + >::List + List; }; + + + + /** + * build all possible combinations, based on a enumeration of the basic cases. + * For each of the types in the argument list, an "enumeration generator" template is invoked, + * yielding a list of the possible base cases. These base cases are then combined with all the + * combinations of the rest, yielding all ordered combinations of all cases. Here, "ordered" + * means that the base cases of the n-th element will appear in the n-th position of the + * resulting lists, + * + * For the typical example, the "base cases" are {flag(on), flag(off)}, so we get a + * list-of-lists, featuring all possibilities to combine these distinct toggles. + */ + template< class X + , template<class> class _ENUM_> + struct Combine { typedef typename Distribute< typename _ENUM_<X>::List + , Node<NullType,NullType> + >::List List; }; + template< template<class> class _ENUM_> + struct Combine<NullType, _ENUM_ > { typedef NodeNull List; }; + + template< class TY, class TYPES + , template<class> class _ENUM_> + struct Combine<Node<TY,TYPES>,_ENUM_> { typedef typename Distribute< typename _ENUM_<TY>::List + , typename Combine<TYPES,_ENUM_>::List + >::List List; }; + + /** enumeration generator for the Combine metafunction, + * yielding an "on" and "off" case + */ + template<class F> + struct FlagOnOff + { + typedef Node<F, Node<NullType,NullType> > List; + }; + + + /** generate all possible on-off combinations of the given flags */ + template<class FLAGS> + struct CombineFlags + { + typedef typename Combine<FLAGS, FlagOnOff>::List List; + }; + + + + +}} // namespace lumiera::typelist +#endif 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 <boost/noncopyable.hpp> 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 <tr1/memory> #include <tr1/functional> 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 <iostream> 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 <boost/format.hpp> From 5350ef6dbe7512ca85473a9fac2181d0b8b373c5 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 25 Sep 2011 17:06:48 +0200 Subject: [PATCH 175/496] 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 <Ichthyostega@web.de> @@ -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<class TYPES> - struct count; - template<> - struct count<NullType> - { - enum{ value = 0 }; - }; - template<class TY, class TYPES> - struct count<Node<TY,TYPES> > - { - enum{ value = 1 + count<TYPES>::value }; - }; - - /** - * Metafunction " max( sizeof(T) ) for T in TYPES " - */ - template<class TYPES> - struct maxSize; - template<> - struct maxSize<NullType> - { - enum{ value = 0 }; - }; - template<class TY, class TYPES> - struct maxSize<Node<TY,TYPES> > - { - enum{ thisval = sizeof(TY) - , nextval = maxSize<TYPES>::value - , value = nextval > thisval? nextval:thisval - }; - }; - - /** pick the n-th element from a typelist */ template<class TYPES, uint i> struct Pick @@ -111,7 +74,7 @@ namespace typelist{ template<class TY, class TYPES, uint i> struct Pick<Node<TY,TYPES>, i> { - typedef typename Pick<TYPES, i-1>::Type Type; + typedef typename Pick<TYPES, i-1>::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<class TYPES> struct Dissect; @@ -356,7 +319,7 @@ namespace typelist{ template<class FLAGS> struct CombineFlags { - typedef typename Combine<FLAGS, FlagOnOff>::List List; + typedef typename Combine<FLAGS, FlagOnOff>::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 <Ichthyostega@web.de> @@ -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<TYPES>::value }; }; + /** * Metafunction " max( sizeof(T) ) for T in TYPES " */ @@ -97,268 +91,35 @@ namespace typelist{ }; - /** pick the n-th element from a typelist */ - template<class TYPES, uint i> - struct Pick - { - typedef NullType Type; - }; - template<class TY, class TYPES> - struct Pick<Node<TY,TYPES>, 0> - { - typedef TY Type; - }; - template<class TY, class TYPES, uint i> - struct Pick<Node<TY,TYPES>, i> - { - typedef typename Pick<TYPES, i-1>::Type Type; - }; - - - - - /** apply a transformation (template) to each type in the list */ - template<class TY, template<class> class _TRANS_> - struct Apply { typedef TY List; }; - - template< class TY, class TYPES - , template<class> class _TRANS_ - > - struct Apply<Node<TY,TYPES>, _TRANS_ > { typedef Node< typename _TRANS_<TY>::Type - , typename Apply<TYPES,_TRANS_>::List - > List; }; - - - /** conditional node: skip an element based on evaluating a predicate */ - template<bool, class T, class TAIL> - struct CondNode { typedef TAIL Next; }; - - template<class T, class TAIL> - struct CondNode<true, T, TAIL> { typedef Node<T,TAIL> Next; }; - - /** filter away those types which don't fulfil a predicate metafunction */ - template< class TYPES - , template<class> class _P_ ///< a template providing a boolean member \c ::value - > - struct Filter; - - template<template<class> class _P_> - struct Filter<NullType,_P_> { typedef NullType List; }; - - template< class TY, class TYPES - , template<class> class _P_ - > - struct Filter<Node<TY,TYPES>,_P_> { typedef typename CondNode< _P_<TY>::value - , TY - , typename Filter<TYPES,_P_>::List - >::Next - List; }; - - - /** append lists-of-types */ - template<class TY1, class TY2> - struct Append { typedef Node<TY1, typename Append<TY2,NullType>::List> List; }; - - template< class TY, class TYPES - , class TAIL - > - struct Append<Node<TY,TYPES>, TAIL> { typedef Node<TY, typename Append<TYPES, TAIL>::List> List; }; - - template<class TY, class TYPES> - struct Append<NullType, Node<TY,TYPES> > { typedef Node<TY,TYPES> List; }; - - template<class TY, class TYPES> - struct Append<Node<TY,TYPES>, NullType> { typedef Node<TY,TYPES> List; }; - - template<class TY1> - struct Append<TY1,NullType> { typedef Node<TY1,NullType> List; }; - - template<class TY2> - struct Append<NullType,TY2> { typedef Node<TY2,NullType> List; }; - - template<> - struct Append<NullType,NullType> { typedef NullType List; }; - - - - - /** access the last list element */ - template<class TYPES> - struct SplitLast; - - template<> - struct SplitLast<NullType> { typedef NullType Type; - typedef NullType List; }; - template<class TY> - struct SplitLast<Node<TY,NullType> > { typedef TY Type; - typedef NullType List; }; - - template<class TY, class TYPES> - struct SplitLast<Node<TY,TYPES> > { typedef typename SplitLast<TYPES>::Type Type; - typedef typename Append< TY, - typename SplitLast<TYPES>::List - >::List - List; }; - - - - /** - * splice a typelist like an overlay - * into an base typelist, starting at given index. - * @return either the combined (spliced) List, or - * the Front/Back part before or after the Overlay - * @note using a NullType as OVERLAY allows to extract - * an arbitrary Front/Back part of the list - */ - template<class BASE, class OVERLAY, uint i=0> - struct Splice; - - template<class B, class BS, - class OVERLAY, uint i> - struct Splice<Node<B,BS>, OVERLAY, i> { typedef Node<B, typename Splice<BS, OVERLAY, i-1>::List> List; - typedef Node<B, typename Splice<BS, OVERLAY, i-1>::Front> Front; - typedef typename Splice<BS, OVERLAY, i-1>::Back Back; }; - - template<class B, class BS, - class O, class OS > - struct Splice<Node<B,BS>,Node<O,OS>,0> { typedef Node<O, typename Splice<BS,OS, 0>::List> List; - typedef NullType Front; - typedef typename Splice<BS,OS, 0>::Back Back; }; - - template<class B, class BS> - struct Splice<Node<B,BS>, NullType, 0> { typedef Node<B, BS> List; - typedef NullType Front; - typedef Node<B, BS> Back; }; - - template<class XX, uint i> - struct Splice<NullType, XX, i> { typedef NullType List; - typedef NullType Front; - typedef NullType Back; }; - - - - /** - * Allows to access various parts of a given typelist: - * Start and End, Prefix and Tail.. + * Metafunction to check if a specific type is contained + * in a given typelist. Only exact match is detected. */ - template<class TYPES> - struct Dissect; - - template<class T, class TYPES> - struct Dissect<Node<T,TYPES> > + template<typename TY, typename TYPES> + struct IsInList { - typedef Node<T,TYPES> List; ///< the complete list - typedef T Head; ///< first element - typedef Node<T,NullType> First; ///< a list containing the first element - typedef TYPES Tail; ///< remainder of the list starting with the second elm. - typedef typename SplitLast<List>::List Prefix;///< all of the list, up to but excluding the last element - typedef typename SplitLast<List>::Type End; ///< the last element - typedef Node<End,NullType> Last; ///< a list containing the last element + enum{ value = false }; }; - template<> - struct Dissect<NullType> + template<typename TY, typename TYPES> + struct IsInList<TY, Node<TY,TYPES> > { - typedef NullType List; - typedef NullType Head; - typedef NullType First; - typedef NullType Tail; - typedef NullType Prefix; - typedef NullType End; - typedef NullType Last; + enum{ value = true }; }; - - - - /** - * prefix each of the elements, - * yielding a list-of lists-of-types - */ - template<class T, class TY> - struct PrefixAll { typedef Node< typename Append<T,TY>::List, NullType> List; }; - - template<class T> - struct PrefixAll<T, NullType> { typedef NullType List; }; - - template<class T> - struct PrefixAll<T, NodeNull> { typedef Node< typename Append<T,NodeNull>::List, NullType> List; }; - - template< class T - , class TY, class TYPES - > - struct PrefixAll<T, Node<TY,TYPES> > { typedef Node< typename Append<T,TY>::List - , typename PrefixAll<T,TYPES>::List - > List; }; - - - - - /** - * build a list-of lists, where each element of the first arg list - * gets in turn prepended to all elements of the second arg list. - * Can be used to build all possible combinations from two - * sources, i.e. the Cartesian product. - */ - template<class TY1,class TY2> - struct Distribute { typedef typename PrefixAll<TY1,TY2>::List List; }; - - template<class TY> - struct Distribute<NullType,TY> { typedef NullType List; }; - - template< class TY, class TYPES - , class TAIL - > - struct Distribute<Node<TY,TYPES>,TAIL> { typedef typename Append< typename PrefixAll<TY,TAIL>::List - , typename Distribute<TYPES,TAIL>::List - >::List - List; }; - - - - /** - * build all possible combinations, based on a enumeration of the basic cases. - * For each of the types in the argument list, an "enumeration generator" template is invoked, - * yielding a list of the possible base cases. These base cases are then combined with all the - * combinations of the rest, yielding all ordered combinations of all cases. Here, "ordered" - * means that the base cases of the n-th element will appear in the n-th position of the - * resulting lists, - * - * For the typical example, the "base cases" are {flag(on), flag(off)}, so we get a - * list-of-lists, featuring all possibilities to combine these distinct toggles. - */ - template< class X - , template<class> class _ENUM_> - struct Combine { typedef typename Distribute< typename _ENUM_<X>::List - , Node<NullType,NullType> - >::List List; }; - template< template<class> class _ENUM_> - struct Combine<NullType, _ENUM_ > { typedef NodeNull List; }; - - template< class TY, class TYPES - , template<class> class _ENUM_> - struct Combine<Node<TY,TYPES>,_ENUM_> { typedef typename Distribute< typename _ENUM_<TY>::List - , typename Combine<TYPES,_ENUM_>::List - >::List List; }; - - /** enumeration generator for the Combine metafunction, - * yielding an "on" and "off" case - */ - template<class F> - struct FlagOnOff - { - typedef Node<F, Node<NullType,NullType> > List; - }; - - - /** generate all possible on-off combinations of the given flags */ - template<class FLAGS> - struct CombineFlags - { - typedef typename Combine<FLAGS, FlagOnOff>::List List; + template<typename TY, typename XX, typename TYPES> + struct IsInList<TY, Node<XX,TYPES> > + { + enum{ value = IsInList<TY,TYPES>::value }; }; + /** convenience shortcut: query function */ + template<typename TY, typename TYPES> + bool + isInList() + { + return IsInList<TY,TYPES>::value; + } 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<TY,TYPES> + ** or NullType to terminate recursion. In C++ template metaprogramming, "pattern match" + ** is done by partial template specialisations (the compiler will pick up and thus + ** match the template parameters). A typedef acts like a declaration in normal + ** programming. Because such a "declaration" can't be changed after the fact, + ** effectively this is a flavour of functional programming. Just the + ** "execution environment" is the compiler, during compilation. + ** ** @see lumiera::visitor::Applicable usage example - ** @see typelisttest.cpp + ** @see control::CommandSignature more elaborate usage example (dissecting a functor signature) + ** @see TypeList_test + ** @see TypeListManip_test ** */ 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<typename TYPES, typename TY> - struct IsInList - { - enum{ value = false }; - }; - - template<typename TY, typename TYPES> - struct IsInList<Node<TY,TYPES>, TY> - { - enum{ value = true }; - }; - - template<typename XX, typename TYPES, typename TY> - struct IsInList<Node<XX,TYPES>, TY> - { - enum{ value = IsInList<TYPES,TY>::value }; - }; - } @@ -141,9 +122,9 @@ namespace lib { ___assertSupportedType() { typedef typename TYPES::List PreconfiguredTypes; - typedef IsInList<PreconfiguredTypes,XX> IsPreconfiguredType; + typedef IsInList<XX, PreconfiguredTypes> 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 <boost/noncopyable.hpp> 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<SIG,MEM> allows accepting suitable typed functions + * within CommandSignature<SIG,MEM> allows for accepting suitable typed functions * to implement the command in question. */ template<typename SIG, typename MEM> @@ -90,7 +86,7 @@ namespace control { public: typedef typename FunctionTypedef<void, Args>::Sig OperateSig; - typedef typename FunctionTypedef<MEM, Args>::Sig CaptureSig; + typedef typename FunctionTypedef<MEM, Args>::Sig CaptureSig; typedef typename FunctionTypedef<void, ExtendedArgs>::Sig UndoOp_Sig; typedef Args CmdArgs; typedef MEM Memento; @@ -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<typename SIG> class UndoSignature { + // preparation: dissect the function signature into arguments and result typedef typename FunctionSignature< function<SIG> >::Args Args; typedef typename FunctionSignature< function<SIG> >::Ret Ret; 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 <<END +return: 0 +END + + TEST "TypeListGenerator_test" TypeListGenerator_test <<END out: ctor DoIt<Block<13> > out: ctor DoIt<Block< 8> > 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 <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/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<TheList>::value); + CHECK (0 == count<EmptyList>::value); + + CHECK (sizeof(int64_t) == maxSize<TheList>::value); + CHECK (0 == maxSize<EmptyList>::value); + + CHECK ( bool(IsInList<int , TheList>::value)); + CHECK ( bool(IsInList<uint , TheList>::value)); + CHECK ( bool(IsInList<int64_t , TheList>::value)); + CHECK ( bool(IsInList<uint64_t, TheList>::value)); + + CHECK (!bool(IsInList<double , TheList>::value)); + CHECK (!bool(IsInList<int , EmptyList>::value)); + CHECK (!bool(IsInList<int , int>::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 <prg@ichthyostega.de> Date: Sun, 25 Sep 2011 19:23:45 +0200 Subject: [PATCH 176/496] 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 <tr1/memory> #include <boost/static_assert.hpp> #include <memory> @@ -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<typename TY> class CustomAllocator : public std::allocator<TY> + { }; + + + /** + * Policy: maintain explicit per type instance count + * @note this imposes additional locking + */ + struct UseInstantiationCounting { + template<class XX> + size_t + allocationCount() const + { + return allocCnt_.get<XX>(); + } + template<class XX> + void + incrementCount() + { + allocCnt_.inc<XX>(); + } + + template<class XX> + void + decrementCount() + { + allocCnt_.dec<XX>(); + } + + private: + lib::TypedCounter allocCnt_; }; + /** + * Policy: no additional instantiation accounting + */ + struct NoInstantiationCount + { + template<class XX> size_t allocationCount() const { return 0; } + template<class XX> void incrementCount() { /* NOP */ } + template<class XX> void decrementCount() { /* NOP */ } + }; + + + + + /* === Allocator frontend === */ + /** * Frontend for explicit allocations, using a custom allocator. * This template is to be instantiated for the collection of types @@ -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<typename TYPES> + template<typename TYPES + ,class COUNTER = NoInstantiationCount ///< Policy: support instance accounting? + > class SimpleAllocator - : InstantiateForEach< typename TYPES::List // for each of those types... - , CustomAllocator // ...mix in the custom allocator + : 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<XX>().c_str()); - return CustomAllocator<XX>::allocate (1); + XX * newStorage = CustomAllocator<XX>::allocate (1); + COUNTER::template incrementCount<XX>(); + return newStorage; } template<class XX> @@ -114,6 +164,7 @@ namespace lib { { TRACE (memory, "release %s", util::tyStr<XX>().c_str()); CustomAllocator<XX>::deallocate (entry, 1); + COUNTER::template decrementCount<XX>(); } @@ -126,8 +177,8 @@ namespace lib { BOOST_STATIC_ASSERT (IsSupportedType::value); } - - + + public: /* ==== build objects with managed allocation ==== */ @@ -234,10 +285,18 @@ namespace lib { releaseSlot<XX> (entry); } + + /** diagnostics */ + template<class XX> + size_t + numSlots() const + { + return COUNTER::template allocationCount<XX>(); + } }; - + } // 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 <<END +TEST "custom allocator(I)" SimpleAllocator_test <<END return: 0 END diff --git a/tests/lib/simple-allocator-test.cpp b/tests/lib/simple-allocator-test.cpp index a9034e33a..ea239d26d 100644 --- a/tests/lib/simple-allocator-test.cpp +++ b/tests/lib/simple-allocator-test.cpp @@ -25,8 +25,6 @@ #include "lib/simple-allocator.hpp" #include "lib/util.hpp" - -//#include <tr1/memory> #include <cstdlib> #include <string> @@ -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<siz; ++i) + checksum_ += (crap_[i] = o.crap_[i]); + } + ~DummyObj() { for (uint i=0; i<siz; ++i) @@ -69,7 +73,8 @@ namespace test{ } }; - typedef SimpleAllocator<Types<DummyObj<1>,DummyObj<23>,string> > TestAllocator; + typedef Types<DummyObj<1>,DummyObj<23>,string> SupportedTypes; + typedef SimpleAllocator<SupportedTypes, UseInstantiationCounting> TestAllocator; } @@ -148,6 +153,9 @@ namespace test{ allocator.destroy (pDxx); allocator.destroy (pSxx); + CHECK (0 == allocator.numSlots<DummyObj<1> >()); + CHECK (0 == allocator.numSlots<DummyObj<23> >()); + CHECK (0 == allocator.numSlots<string>()); CHECK (0 == checksum_); } }; From 7459c0a41fe947789a1b558ebdf30245af9ef771 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 26 Sep 2011 02:19:31 +0200 Subject: [PATCH 177/496] (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<class X> 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 <prg@ichthyostega.de> Date: Thu, 6 Oct 2011 21:55:00 +0200 Subject: [PATCH 178/496] 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" <mfisher31@gmail.com> Date: Thu, 6 Oct 2011 11:58:31 -0500 Subject: [PATCH 179/496] 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 <mfisher31@gmail.com> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + +#include "gui/widgets/timeline/timeline-zoom-scale.hpp" +#include "gui/widgets/timeline-widget.hpp" + +using namespace Gtk; + +namespace gui { +namespace widgets { + +class TimelineWidget; + +namespace timeline { + +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<void, int64_t> +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 <mfisher31@gmail.com> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +/** @file timeline-zoom-scale.hpp + ** This file contains the definition of the zoom scale widget + */ + +#ifndef TIMELINE_ZOOM_SCALE_HPP +#define TIMELINE_ZOOM_SCALE_HPP + +#include "gui/gtk-lumiera.hpp" +#include "gui/widgets/mini-button.hpp" + +using namespace Gtk; +using namespace gui::widgets; + +namespace gui { +namespace widgets { +namespace timeline { + +class TimelineZoomScale : public Gtk::HBox +{ +public: + /** + * Constructor + */ + TimelineZoomScale(); + + /** + * Accessor method to the zoomSignal + * @return the zoomSignal + */ + sigc::signal<void, int64_t> 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<void, int64_t> 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" <mfisher31@gmail.com> Date: Thu, 6 Oct 2011 12:38:10 -0500 Subject: [PATCH 180/496] 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 <boost/scoped_ptr.hpp> @@ -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<timeline::TimelineState> 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 <prg@ichthyostega.de> Date: Fri, 7 Oct 2011 03:49:51 +0200 Subject: [PATCH 181/496] 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> 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 <prg@ichthyostega.de> Date: Sat, 8 Oct 2011 02:21:29 +0200 Subject: [PATCH 182/496] 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 <boost/noncopyable.hpp> +#include <boost/noncopyable.hpp> 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 <prg@ichthyostega.de> Date: Sun, 9 Oct 2011 04:20:56 +0200 Subject: [PATCH 183/496] 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 <prg@ichthyostega.de> Date: Sun, 9 Oct 2011 14:52:58 +0200 Subject: [PATCH 184/496] 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 <nobug.h> +// 10/11 - simple demo using a pointer and a struct #include <iostream> -#include <string> -extern "C" { -#include <unistd.h> -} -#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; i<length_; ++i) + data_[i] = *(tmp+i); + } + }; + + +// define a global variable holding a MyStruct +MyStruct theStruct; + + + +void +printMyStruct(MyStruct* myPointer) + { + for (int i=0; i < myPointer->length_; ++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 <prg@ichthyostega.de> Date: Sun, 9 Oct 2011 16:03:51 +0200 Subject: [PATCH 185/496] 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 <boost/functional/hash.hpp> #include <boost/operators.hpp> #include <iostream> #include <string> 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 <boost/functional/hash.hpp> #include <boost/noncopyable.hpp> @@ -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<typename VAL> + 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" <mfisher31@gmail.com> Date: Sun, 9 Oct 2011 12:48:47 -0500 Subject: [PATCH 186/496] 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<void, int64_t> 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 <prg@ichthyostega.de> Date: Mon, 10 Oct 2011 00:08:50 +0200 Subject: [PATCH 187/496] 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<timeline::TimelineState> 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<void> +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<void, boost::shared_ptr<timeline::TimelineState> > TimelineStateChangeSignal; + typedef sigc::signal<void, boost::shared_ptr<timeline::Track> > HoveringTrackChangedSignal; + sigc::signal<void, lib::time::Time> mouse_hover_signal() const; sigc::signal<void> playback_period_drag_released_signal() const; - sigc::signal<void, boost::shared_ptr<timeline::Track> > - hovering_track_changed_signal() const; + HoveringTrackChangedSignal hovering_track_changed_signal() const; - sigc::signal<void> state_changed_signal() const; + TimelineStateChangeSignal state_changed_signal() const; /* ===== Events ===== */ protected: @@ -275,9 +277,8 @@ protected: // Signals sigc::signal<void, Time> mouseHoverSignal; sigc::signal<void> playbackPeriodDragReleasedSignal; - sigc::signal<void, boost::shared_ptr<timeline::Track> > - hoveringTrackChangedSignal; - sigc::signal<void> stateChangedSignal; + HoveringTrackChangedSignal hoveringTrackChangedSignal; + TimelineStateChangeSignal stateChangedSignal; bool update_tracks_frozen; 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<TimelineState> 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<TimelineState> 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<Cairo::SolidPattern> playbackPointColour; gui::widgets::TimelineWidget &timelineWidget; + boost::shared_ptr<TimelineState> timelineState; ////////////////////TICKET #796 : should use std::tr1::shared_ptr + friend class Tool; friend class ArrowTool; 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<TimelineState> newState) { - if(timelineWidget.get_state()) - { - // Connect up some events - view_window().changed_signal().connect( - sigc::mem_fun(this, &TimelineRuler::on_update_view) ); - } - + REQUIRE (newState); + timelineState = newState; + + propagateStateChange(); +} + +void +TimelineRuler::propagateStateChange() +{ + // Connect up some events + 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<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr private: /* ===== Internal Methods ===== */ @@ -161,7 +161,13 @@ private: */ void draw_playback_point(Cairo::RefPtr<Cairo::Context> cr, const Gdk::Rectangle ruler_rect); - + + /** + * After notification of a timeline state switch + * do any local adjustments to adapt to the new state + */ + void propagateStateChange(); + /** * Given the current zoom, this function calculates the preiod * between major graduations on the ruler scale. @@ -233,6 +239,11 @@ private: */ gui::widgets::TimelineWidget &timelineWidget; + /** + * the currently active timeline state object + */ + boost::shared_ptr<TimelineState> timelineState; ////////////////////TICKET #796 : should use std::tr1::shared_ptr + /** * The caches image of the ruler, over which the chevrons etc. will * be drawn. 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<TimelineState> 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 <boost/shared_ptr.hpp> + using namespace Gtk; using namespace gui::widgets; @@ -52,9 +55,15 @@ public: sigc::signal<void, int64_t> signal_zoom(); void set_view_window(TimelineViewWindow &view_window); + void wireTimelineState (TimelineWidget::TimelineStateChangeSignal); private: /* Event Handlers */ + + /** + * + */ + void on_timeline_state_changed (boost::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr /** * Event handler for when the zoomIn Button From 5fe1debd5beeefb8dfc671bdea9596cf49daec02 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 10 Oct 2011 01:07:10 +0200 Subject: [PATCH 188/496] 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> sequence ///////////////////////////////TICKET #796 + shared_ptr<Sequence> sequence ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr instead of boost = *get_project().get_sequences().begin(); timelineWidget.reset(new TimelineWidget(load_state(sequence))); pack_start(*timelineWidget, PACK_EXPAND_WIDGET); - // 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> timelineWidget; std::map< boost::weak_ptr<model::Sequence>, - boost::shared_ptr<widgets::timeline::TimelineState> > ///////////////////////////////TICKET #796 + boost::shared_ptr<widgets::timeline::TimelineState> > ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr timelineStates; // Toolbar Widgets 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<TimelineState> 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<TimelineState> 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 <boost/shared_ptr.hpp> @@ -54,8 +55,8 @@ public: */ sigc::signal<void, int64_t> signal_zoom(); - void set_view_window(TimelineViewWindow &view_window); - void wireTimelineState (TimelineWidget::TimelineStateChangeSignal); + void wireTimelineState (boost::shared_ptr<TimelineState> 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<void, int64_t> zoomSignal; const double smoothing_factor; const double button_step_size; - TimelineViewWindow *timelineViewWindow; + boost::shared_ptr<TimelineState> timelineState; }; } // namespace gui From fb28592082dc8917e19af7f025a3fd734e15f15b Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 10 Oct 2011 01:42:03 +0200 Subject: [PATCH 189/496] 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<Cairo::Context> 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<Cairo::Context> cr) // Prepare const Allocation allocation = get_allocation(); - shared_ptr<TimelineState> state = timelineWidget.get_state(); - REQUIRE(state); + REQUIRE(timelineState); - TimelineViewWindow const& window = state->get_view_window(); - const int start_x = window.time_to_x(state->getSelectionStart()); - const int end_x = window.time_to_x(state->getSelectionEnd()); + TimelineViewWindow const& window = timelineState->get_view_window(); + const int start_x = window.time_to_x(timelineState->getSelectionStart()); + const int end_x = window.time_to_x(timelineState->getSelectionEnd()); // Draw the cover if(end_x > 0 && start_x < allocation.get_width()) @@ -436,16 +441,13 @@ TimelineBody::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr) { REQUIRE(cr); - // Prepare - - shared_ptr<TimelineState> state = timelineWidget.get_state(); - if(state) + if (timelineState) { - if (!state->isPlaying()) return; + if (!timelineState->isPlaying()) return; const Allocation allocation = get_allocation(); - Time point = state->getPlaybackPoint(); - const int x = view_window().time_to_x(point); + Time point = timelineState->getPlaybackPoint(); + const int x = viewWindow().time_to_x(point); // Set source cr->set_source(playbackPointColour); @@ -464,10 +466,10 @@ TimelineBody::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr) void TimelineBody::begin_shift_drag() { - if(timelineWidget.get_state()) + if (timelineState) { dragType = Shift; - beginShiftTimeOffset = view_window().get_time_offset(); + beginShiftTimeOffset = viewWindow().get_time_offset(); beginShiftVerticalOffset = get_vertical_offset(); } } @@ -484,14 +486,6 @@ TimelineBody::set_vertical_offset(int offset) timelineWidget.verticalAdjustment.set_value(offset); } -TimelineViewWindow& -TimelineBody::view_window() const -{ - shared_ptr<TimelineState> state = timelineWidget.get_state(); - REQUIRE(state); - return state->get_view_window(); -} - void TimelineBody::register_styles() const { 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<TimelineState> state = timelineWidget.get_state(); - - if(state) + if (timelineState) { - TimeVar newStartPoint (view_window().x_to_time(x)); + TimeVar newStartPoint (viewWindow().x_to_time(x)); Offset selectionLength (pinnedDragTime, newStartPoint); if (newStartPoint > pinnedDragTime) newStartPoint=pinnedDragTime; // use the smaller one as selection start - state->setPlaybackPeriod (Mutation::changeTime(newStartPoint) ); - state->setPlaybackPeriod (Mutation::changeDuration(selectionLength)); + timelineState->setPlaybackPeriod (Mutation::changeTime(newStartPoint) ); + timelineState->setPlaybackPeriod (Mutation::changeDuration(selectionLength)); //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once ////////////////////TODO : code duplication with timeline-ibeam-tool 205 } @@ -268,7 +274,7 @@ TimelineRuler::draw_ruler(Cairo::RefPtr<Cairo::Context> cr, REQUIRE(ruler_rect.get_width() > 0); REQUIRE(ruler_rect.get_height() > 0); - const TimelineViewWindow &window = view_window(); + const TimelineViewWindow &window = viewWindow(); const gavl_time_t left_offset = _raw(window.get_time_offset()); const int64_t time_scale = window.get_time_scale(); @@ -369,16 +375,15 @@ TimelineRuler::draw_selection(Cairo::RefPtr<Cairo::Context> cr, REQUIRE(cr); REQUIRE(ruler_rect.get_width() > 0); REQUIRE(ruler_rect.get_height() > 0); + REQUIRE(timelineState); - shared_ptr<TimelineState> state = timelineWidget.get_state(); - REQUIRE(state); - const TimelineViewWindow &window = state->get_view_window(); + const TimelineViewWindow &window = timelineState->get_view_window(); Glib::RefPtr<Style> style = get_style(); Gdk::Cairo::set_source_color(cr, style->get_fg(STATE_NORMAL)); // Draw the selection start chevron - const int a = 1 + window.time_to_x(state->getSelectionStart()); + const int a = 1 + window.time_to_x(timelineState->getSelectionStart()); if(a >= 0 && a < ruler_rect.get_width()) { cr->move_to(a, ruler_rect.get_height()); @@ -388,7 +393,7 @@ TimelineRuler::draw_selection(Cairo::RefPtr<Cairo::Context> cr, } // Draw the selection end chevron - const int b = window.time_to_x(state->getSelectionEnd()); + const int b = window.time_to_x(timelineState->getSelectionEnd()); if(b >= 0 && b < ruler_rect.get_width()) { cr->move_to(b, ruler_rect.get_height()); @@ -405,18 +410,17 @@ TimelineRuler::draw_playback_period(Cairo::RefPtr<Cairo::Context> cr, REQUIRE(cr); REQUIRE(ruler_rect.get_width() > 0); REQUIRE(ruler_rect.get_height() > 0); + REQUIRE(timelineState); - shared_ptr<TimelineState> state = timelineWidget.get_state(); - REQUIRE(state); - const TimelineViewWindow &window = state->get_view_window(); + const TimelineViewWindow &window = timelineState->get_view_window(); // Calculate coordinates const float halfSize = playbackPeriodArrowSize / 2; - const float a = 1.5f + window.time_to_x(state->getPlaybackPeriodStart()); + const float a = 1.5f + window.time_to_x(timelineState->getPlaybackPeriodStart()); const float b = a + halfSize; - const float d = 0.5f + window.time_to_x(state->getPlaybackPeriodEnd()); + const float d = 0.5f + window.time_to_x(timelineState->getPlaybackPeriodEnd()); const float c = d - halfSize; @@ -473,14 +477,12 @@ TimelineRuler::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr, REQUIRE(cr); REQUIRE(ruler_rect.get_width() > 0); REQUIRE(ruler_rect.get_height() > 0); + REQUIRE(timelineState); - shared_ptr<TimelineState> state = timelineWidget.get_state(); - REQUIRE(state); - if (!state->isPlaying()) return; + if (!timelineState->isPlaying()) return; - TimelineViewWindow const& window = state->get_view_window(); - Time point = state->getPlaybackPoint(); - const int x = window.time_to_x(point); + Time point = timelineState->getPlaybackPoint(); + const int x = viewWindow().time_to_x(point); cr->move_to(x + 0.5, ruler_rect.get_height()); cr->rel_line_to(0, -playbackPointSize); @@ -502,7 +504,6 @@ TimelineRuler::calculate_major_spacing() const { unsigned int i; - const int64_t time_scale = view_window().get_time_scale(); const gavl_time_t major_spacings[] = { GAVL_TIME_SCALE / 1000, GAVL_TIME_SCALE / 400, @@ -528,6 +529,8 @@ TimelineRuler::calculate_major_spacing() const 60ll * 60ll * GAVL_TIME_SCALE }; + const int64_t time_scale = viewWindow().get_time_scale(); + for(i = 0; i < sizeof(major_spacings) / sizeof(gavl_time_t); i++) { const int64_t division_width = major_spacings[i] / time_scale; @@ -539,14 +542,6 @@ TimelineRuler::calculate_major_spacing() const return major_spacings[i]; } -TimelineViewWindow& -TimelineRuler::view_window() const -{ - shared_ptr<TimelineState> state = timelineWidget.get_state(); - REQUIRE(state); - return state->get_view_window(); -} - void TimelineRuler::register_styles() const { diff --git a/src/gui/widgets/timeline/timeline-ruler.hpp b/src/gui/widgets/timeline/timeline-ruler.hpp index 1c2cfbb74..a7266793f 100644 --- a/src/gui/widgets/timeline/timeline-ruler.hpp +++ b/src/gui/widgets/timeline/timeline-ruler.hpp @@ -176,9 +176,9 @@ private: gavl_time_t calculate_major_spacing() const; /** - * A helper function to get the view window + * Access current timeline view window */ - TimelineViewWindow& view_window() const; + TimelineViewWindow& viewWindow() const; /** * Registers all the styles that this class will respond to. From 91cc3f90a0d9c15d85192d778ca4bb8f3d36d74e Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Sun, 9 Oct 2011 20:54:55 -0500 Subject: [PATCH 190/496] TimelineZoomScale::on_timeline_state_changed method implemented. Other timeline components need fixing now (seems only the ruler is zooming now) --- src/gui/widgets/timeline/timeline-zoom-scale.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp index 8d084b525..ff5374fa4 100644 --- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp +++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp @@ -104,7 +104,13 @@ TimelineZoomScale::on_timeline_state_changed (boost::shared_ptr<TimelineState> n REQUIRE (newState); timelineState = newState; - UNIMPLEMENTED ("react on the timeline state change"); ///////////////////////////TODO + int64_t current_scale = + timelineState->get_view_window().get_time_scale(); + + double new_relative_scale = + (double) current_scale / (double) TimelineWidget::MaxScale; + + adjustment.set_value(new_relative_scale); } void From 540357de9fd99a091abb2f275c4c92250ca1617f Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Sun, 9 Oct 2011 21:00:31 -0500 Subject: [PATCH 191/496] Comments added --- src/gui/widgets/timeline/timeline-zoom-scale.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.hpp b/src/gui/widgets/timeline/timeline-zoom-scale.hpp index 9bf5dde9f..9306f95f2 100644 --- a/src/gui/widgets/timeline/timeline-zoom-scale.hpp +++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp @@ -62,7 +62,8 @@ private: /* Event Handlers */ /** - * + * Update the slider position when the timeline state + * is changed. */ void on_timeline_state_changed (boost::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr From d1f07d7088be0de61730ad256db017efeb30ef24 Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Mon, 10 Oct 2011 18:01:10 -0500 Subject: [PATCH 192/496] TimelineBody Is Zooming again now --- src/gui/widgets/timeline/timeline-body.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index 3bee2110f..3be2af378 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -306,6 +306,15 @@ TimelineBody::on_state_changed (shared_ptr<TimelineState> newState) { REQUIRE (newState); timelineState = newState; + if(timelineState) + { + // Connect up some events + viewWindow().changed_signal().connect( + sigc::mem_fun(this, &TimelineBody::on_update_view) ); + } + + // Redraw + queue_draw(); } void From 63ba43d4464c61563b70c41df9c9e1f0d98de0c8 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 11 Oct 2011 01:24:39 +0200 Subject: [PATCH 193/496] fix: TimelineBody not zooming was due to this omission in my change silly me! --- src/gui/widgets/timeline/timeline-body.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index 3be2af378..b64757477 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -306,15 +306,7 @@ TimelineBody::on_state_changed (shared_ptr<TimelineState> newState) { REQUIRE (newState); timelineState = newState; - if(timelineState) - { - // Connect up some events - viewWindow().changed_signal().connect( - sigc::mem_fun(this, &TimelineBody::on_update_view) ); - } - - // Redraw - queue_draw(); + propagateStateChange(); } void From f851c875ed354f11b583b554752df9e19e3e41e9 Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Tue, 11 Oct 2011 14:39:20 -0500 Subject: [PATCH 194/496] Zoom Slider Position remains after a sequence change --- src/gui/panels/timeline-panel.cpp | 17 ++++----- src/gui/panels/timeline-panel.hpp | 2 +- src/gui/widgets/timeline-widget.cpp | 11 +++--- src/gui/widgets/timeline-widget.hpp | 2 +- src/gui/widgets/timeline/timeline-state.cpp | 7 ++-- src/gui/widgets/timeline/timeline-state.hpp | 5 ++- .../widgets/timeline/timeline-view-window.cpp | 36 +++++++++++++++++-- .../widgets/timeline/timeline-view-window.hpp | 4 +-- .../widgets/timeline/timeline-zoom-scale.cpp | 33 +++++------------ .../widgets/timeline/timeline-zoom-scale.hpp | 12 ++----- 10 files changed, 69 insertions(+), 60 deletions(-) diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index 970aa21d4..6f755f25c 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -28,6 +28,7 @@ #include "gui/workspace/workspace-window.hpp" #include "gui/model/project.hpp" #include "gui/controller/controller.hpp" + #include "lib/util.hpp" #include <boost/foreach.hpp> @@ -139,7 +140,6 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager, zoomScale.wireTimelineState (timelineWidget->get_state(), timelineWidget->state_changed_signal()); - // Set the initial UI state update_sequence_chooser(); update_tool_buttons(); @@ -194,11 +194,10 @@ TimelinePanel::on_ibeam_tool() } void -TimelinePanel::on_zoom(int64_t zoom_scale) +TimelinePanel::on_zoom(double time_scale_ratio) { REQUIRE(timelineWidget); - timelineWidget->zoom_view(zoom_scale); - update_zoom_buttons(); + timelineWidget->zoom_view(time_scale_ratio); } void @@ -254,18 +253,19 @@ TimelinePanel::on_sequence_chosen() { weak_ptr<Sequence> sequence_ptr = (*iter)[sequenceChooserColumns.sequenceColumn]; + shared_ptr<Sequence> sequence(sequence_ptr.lock()); + if(sequence) { shared_ptr<timeline::TimelineState> old_state( timelineWidget->get_state()); REQUIRE(old_state); - if(sequence != old_state->get_sequence()) timelineWidget->set_state(load_state(sequence)); } } - + update_zoom_buttons(); } @@ -397,9 +397,10 @@ TimelinePanel::on_frame() shared_ptr<timeline::TimelineState> TimelinePanel::load_state(weak_ptr<Sequence> sequence) { + /* state exists */ if(contains(timelineStates, sequence)) - return timelineStates[sequence]; - + return timelineStates[sequence]; + shared_ptr<Sequence> shared_sequence = sequence.lock(); if(shared_sequence) { diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp index 8b165dd79..2215c8657 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -89,7 +89,7 @@ private: void on_arrow_tool(); void on_ibeam_tool(); - void on_zoom(int64_t zoom_size); + void on_zoom(double time_scale_ratio); void on_zoom_in(); void on_zoom_out(); diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index 038af15b5..eb4decffa 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -41,7 +41,7 @@ const int TimelineWidget::TrackPadding = 1; const int TimelineWidget::HeaderWidth = 150; const int TimelineWidget::HeaderIndentWidth = 10; const double TimelineWidget::ZoomIncrement = 1.25; -const int64_t TimelineWidget::MaxScale = 30000000; +const int64_t TimelineWidget::MaxScale = 30000000; // 30 Million TimelineWidget::TimelineWidget( boost::shared_ptr<timeline::TimelineState> source_state) : @@ -102,6 +102,7 @@ TimelineWidget::get_state() void TimelineWidget::set_state(shared_ptr<timeline::TimelineState> new_state) { + state = new_state; // Clear the track tree @@ -129,12 +130,12 @@ TimelineWidget::set_state(shared_ptr<timeline::TimelineState> new_state) } void -TimelineWidget::zoom_view(int64_t zoom_scale) +TimelineWidget::zoom_view(double timescale_ratio) { if(state) { const int view_width = body->get_allocation().get_width(); - state->get_view_window().zoom_view(view_width / 2, zoom_scale); + state->get_view_window().zoom_view(view_width / 2, timescale_ratio); } } @@ -212,10 +213,12 @@ TimelineWidget::on_view_window_changed() if(state) { timeline::TimelineViewWindow &window = state->get_view_window(); + const int view_width = body->get_allocation().get_width(); - + horizontalAdjustment.set_page_size( window.get_time_scale() * view_width); + horizontalAdjustment.set_value(_raw(window.get_time_offset())); } } diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index b3f801f9a..884598067 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(int64_t zoom_scale); + void zoom_view(double timescale_ratio); /** * Gets the type of the tool currently active. diff --git a/src/gui/widgets/timeline/timeline-state.cpp b/src/gui/widgets/timeline/timeline-state.cpp index 2a8f311e4..06e67dfa0 100644 --- a/src/gui/widgets/timeline/timeline-state.cpp +++ b/src/gui/widgets/timeline/timeline-state.cpp @@ -51,12 +51,11 @@ TimelineState::TimelineState (boost::shared_ptr<model::Sequence> source_sequence REQUIRE(sequence); ////////////////////////////////////////////////////////////TICKET #798: how to handle GUI default state - - viewWindow.set_time_scale(GAVL_TIME_SCALE / 200); - + viewWindow.set_time_scale(21000000); + setSelection (Mutation::changeTime (Time(FSecs(2)))); setSelection (Mutation::changeDuration(Duration(FSecs(2)))); - //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all + //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all } boost::shared_ptr<model::Sequence> diff --git a/src/gui/widgets/timeline/timeline-state.hpp b/src/gui/widgets/timeline/timeline-state.hpp index 47fb583fb..94c072950 100644 --- a/src/gui/widgets/timeline/timeline-state.hpp +++ b/src/gui/widgets/timeline/timeline-state.hpp @@ -103,7 +103,7 @@ public: * the GUI is really connected to the Player */ void setPlaybackPoint(Time newPos); - + /** * A signal to notify when the selected period has changed. */ @@ -143,10 +143,9 @@ private: * when actually integrated with the Player */ TimeVar playbackPoint_; - + bool isPlayback_; - // Signals /** diff --git a/src/gui/widgets/timeline/timeline-view-window.cpp b/src/gui/widgets/timeline/timeline-view-window.cpp index 84379b17c..07e017e07 100644 --- a/src/gui/widgets/timeline/timeline-view-window.cpp +++ b/src/gui/widgets/timeline/timeline-view-window.cpp @@ -35,6 +35,8 @@ TimelineViewWindow::TimelineViewWindow (Offset offset, int64_t scale) : timeOffset(offset) , timeScale(scale) { + TODO("Create a function to limit timescale between 1 and MaxScale"); + TODO("TICKET #795 Some functions need to be private"); } Offset @@ -64,10 +66,38 @@ TimelineViewWindow::set_time_scale(int64_t scale) } void -TimelineViewWindow::zoom_view(int point, int64_t new_time_scale) +TimelineViewWindow::set_time_scale(double ratio) { - // Limit zooming in too close - if(new_time_scale < 1) new_time_scale = 1; + int64_t max = TimelineWidget::MaxScale; + int64_t min = 1; + + if(ratio <= 0.0) + { + set_time_scale((int64_t)min); + return; + } + + if(ratio > 1.0) + { + ratio = 1.0; + } + + set_time_scale((int64_t)(ratio * max)); +} + +void +TimelineViewWindow::zoom_view(int point, double time_scale_ratio) +{ + TODO("Find a Central place for a Zoom Smoothing Factor Variable. Right now it is hard coded at 9.0"); + int64_t new_time_scale = + (int64_t)( pow(time_scale_ratio, 9.0) * (double)TimelineWidget::MaxScale); + + /* Prevent Zooming in To Close and Far */ + if(new_time_scale < 1) + new_time_scale = 1; + + if(new_time_scale > TimelineWidget::MaxScale) + new_time_scale = TimelineWidget::MaxScale; // The view must be shifted so that the zoom is centred on the cursor TimeVar newStartPoint = get_time_offset(); diff --git a/src/gui/widgets/timeline/timeline-view-window.hpp b/src/gui/widgets/timeline/timeline-view-window.hpp index 496085555..b9536deac 100644 --- a/src/gui/widgets/timeline/timeline-view-window.hpp +++ b/src/gui/widgets/timeline/timeline-view-window.hpp @@ -94,14 +94,14 @@ public: * zero */ void set_time_scale(int64_t time_scale); - + void set_time_scale(double ratio); /** * Zooms the view in or out as by a number of steps while keeping a * given point on the timeline still. * @param 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 new_time_scale); + void zoom_view(int point, double timescale_ratio); /** * 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 ff5374fa4..eedaf074b 100644 --- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp +++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp @@ -62,7 +62,6 @@ TimelineZoomScale::TimelineZoomScale() , slider() , zoomIn(Stock::ZOOM_IN) , zoomOut(Stock::ZOOM_OUT) - , smoothing_factor(9.0) , button_step_size(0.03) { /* Setup the Slider Control */ @@ -105,11 +104,16 @@ TimelineZoomScale::on_timeline_state_changed (boost::shared_ptr<TimelineState> n timelineState = newState; int64_t current_scale = - timelineState->get_view_window().get_time_scale(); + getViewWindow().get_time_scale(); - double new_relative_scale = + double linear_scale = (double) current_scale / (double) TimelineWidget::MaxScale; + /* We have to Revese the Smoothing */ + TODO("Find a central place for ZoomSmoothingFactor Variable. right now it is 9.0"); + double new_relative_scale = + pow(linear_scale,(1.0/9.0)); + adjustment.set_value(new_relative_scale); } @@ -130,10 +134,10 @@ TimelineZoomScale::on_zoom_out_clicked() void TimelineZoomScale::on_zoom() { - zoomSignal.emit(calculate_zoom_scale()) ; + zoomSignal.emit(adjustment.get_value()) ; } -sigc::signal<void, int64_t> +sigc::signal<void, double> TimelineZoomScale::signal_zoom() { return zoomSignal; @@ -146,25 +150,6 @@ TimelineZoomScale::getViewWindow() return timelineState->get_view_window(); } - -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 index 9306f95f2..9bfe8139a 100644 --- a/src/gui/widgets/timeline/timeline-zoom-scale.hpp +++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp @@ -53,7 +53,7 @@ public: * Accessor method to the zoomSignal * @return the zoomSignal */ - sigc::signal<void, int64_t> signal_zoom(); + sigc::signal<void, double> signal_zoom(); void wireTimelineState (boost::shared_ptr<TimelineState> currentState, TimelineWidget::TimelineStateChangeSignal); @@ -88,13 +88,6 @@ private: /** access current timeline state */ TimelineViewWindow& getViewWindow(); - /** - * 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; @@ -103,9 +96,8 @@ private: private: /* Signals */ - sigc::signal<void, int64_t> zoomSignal; + sigc::signal<void, double> zoomSignal; - const double smoothing_factor; const double button_step_size; boost::shared_ptr<TimelineState> timelineState; From 3256b7fe1100e02e3ebbc6831b9640df2b7779ab Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 12 Oct 2011 03:47:34 +0200 Subject: [PATCH 195/496] implementing the hash functions for the buffer metadata content fields --- src/proc/engine/buffer-metadata.hpp | 47 ++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index a257f6a8c..024343ea2 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 <tr1/functional> #include <boost/functional/hash.hpp> #include <boost/noncopyable.hpp> @@ -64,6 +65,7 @@ namespace engine { using lib::Literal; + using std::tr1::function; namespace error = lumiera::error; @@ -98,27 +100,57 @@ namespace engine { { } operator uint64_t() const { return privateID_; } + + friend size_t + hash_value (LocalKey const& lkey) + { + boost::hash<uint64_t> hashFunction; + return hashFunction(lkey.privateID_); + } }; + template<class X> + void + buildIntoBuffer (void* storageBuffer) + { + new(storageBuffer) X(); + } + + template<class X> + void + destroyInBuffer (void* storageBuffer) + { + X* embedded = static_cast<X*> (storageBuffer); + embedded->~X(); + } struct TypeHandler { - typedef void (*Ctor) (void*); - typedef void (*Dtor) (void*); + typedef function<void(void*)> Ctor; + typedef function<void(void*)> Dtor; Ctor createAttached; Dtor destroyAttached; TypeHandler() - : createAttached (0) - , destroyAttached (0) + : createAttached() + , destroyAttached() { } template<class X> TypeHandler() - : createAttached (0) /////////TODO: how to attach the ctor function??? Maybe better use a class with virtual functions? - , destroyAttached (0) + : createAttached (buildIntoBuffer<X>) + , destroyAttached (destroyInBuffer<X>) { } + + friend size_t + hash_value (TypeHandler handler) + { + boost::hash<Ctor> ctorHash; +// return boost::hash_combine(ctorHash(handler.createAttached), handler.destroyAttached.); + ////////////////TODO how to calculate the hash of an function object + return 0; + } }; namespace { // internal constants to mark the default case @@ -140,7 +172,8 @@ namespace engine { HashVal chainedHash(HashVal accumulatedHash, VAL changedValue) { - UNIMPLEMENTED ("calculate a new hash value, based on parent hash"); + boost::hash_combine (accumulatedHash, changedValue); + return accumulatedHash; } } From 924944f6076eacd5c363c57e6b5380210bd351d2 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 14 Oct 2011 01:10:16 +0200 Subject: [PATCH 196/496] write down some know-how regarding standard hash functions --- doc/technical/howto/HashFunctions.txt | 111 ++++++++++++++++++++++++++ doc/technical/howto/index.txt | 2 + 2 files changed, 113 insertions(+) create mode 100644 doc/technical/howto/HashFunctions.txt diff --git a/doc/technical/howto/HashFunctions.txt b/doc/technical/howto/HashFunctions.txt new file mode 100644 index 000000000..b30e23989 --- /dev/null +++ b/doc/technical/howto/HashFunctions.txt @@ -0,0 +1,111 @@ +Hash functions (C++) +==================== + +_This page is for collecting know-how related to hash functions and hash tables._ + +The original STL was lacking proper support for hashtables, hash based associative arrays +and hash calculation in general. To quite some developers, hash tables feel like some kind +of _impure_ data structure -- unfortunately the properties of modern CPUs turned the balance +significantly in favour of hash tables due to memory locality. Pointer based datastructures +can't be considered especially _performant_ as they were in the good old times. + +The tr1 extension and the new C++11 standard amended the problem by defining a framework +for hash functions and hash tables. When sticking to some rules, custom written hash functions +can be picked up automatically by the standard library and -containers. + +Standard Hash Definitions +------------------------- + +Hash values:: + hash values are unsigned integral numbers of type 'size_t' ++ +Basically this means that the range of hash values roughly matches the memory address space. +But it also means that this range is _platform dependant_ (32 or 64bit) and -- given the usual +hash calculation based on modulus (wrap around) -- that generated hash values are nonportable. + +Hash function:: + a hash function calculates a hash value for objects of its argument type. Thus, for every + supported type, there is a dedicated hash function. Quite some hash functions are generated + from function templates though. + +Hash functor:: + a function object able to calculate hash values when invoked. The standard library and the + corresponding boost libraries accept functors of type 'hash<TY>' to calculate hash values + for objects or values of type 'TY' + +Hash based containers:: + While the standard Set and Map types (including the Multiset and Multimap) are based on + balanced binary trees, the new C\+\+11 standard includes hash based variants (with name + prefix +unordered_+). These hashtable based containers require a +hash<KEY>+ functor + to be able to derive the hash value of any encountered key value. Hash functors may + be provided as additional type parameter to the container; if omitted, the compiler + tries to find a (maybe custom defined) hash functor by *ADL* (see below) + + +C++11 versus Boost +~~~~~~~~~~~~~~~~~~ +The Boost library *functional-hash* provided the foundation for the definition now accepted +into the new C++ standard. Yet the boost library provides some additional facilities not +part of the standard. Thus we're bound to choose + +* either including +<tr1/functional>+ and +using std::tr1::hash+ +* or including +<boost/functional-hash>+ and +using boost::hash+ + +The boost version additionally provides pre defined hash functors for STL containers holding +custom types -- and it provides an easy to use extension mechanism for writing hash functions +for custom types. Effectively this means that, assuming usage of the boost-include, the actual +implementation and the way it is picked up is _different but compatible_ to the standard way. + +Boost: hashing custom types +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The extension mechanism used by the boost version is best explained by looking +at the code + +.boost/functional/hash/extensions.hpp +[source,C] +---- + template <class T> struct hash + : std::unary_function<T, std::size_t> + { + std::size_t operator()(T const& val) const + { + return hash_value(val); + } + } +---- +So this templated standard implementation just _invokes an unqualified function_ +with the name +hash_value(val)+ -- when instantiating this template for your custom +class or type, the compiler will search this function not only in the current scope, +but also in the namespace defining your custom type +T+ (this mechanism is known as +``**A**rgument **D**ependant **L**ookup''). Meaning that all we'd need to do is to define a +free function or friend function named +hash_value+ alongside with our custom data types (classes). + +To further facilitate providing custom hash functions, boost defines a function ++boost::hash_combine(size_t seed, size_t hashValue)+, allowing to _chain up_ the +calculated hash values of the parts forming a composite data structure. + +- see Lumiera's link:http://git.lumiera.org/gitweb?p=LUMIERA;a=blob;f=src/proc/asset/category.hpp;h=b7c8df2f2ce69b0ccf89439954de8346fe8d9276;hb=master#l104[asset::Category] + for a simple usage example +- our link:http://git.lumiera.org/gitweb?p=LUMIERA;a=blob;f=src/lib/symbol-impl.cpp;h=9e09b4254ac57baefeb0a0c06ccd423318e923c1;hb=master#l67[lib::Symbol datatype] + uses the standard implementation of a string hash function combining the individual + character's hashes. + + + +LUID values +----------- +Lumiera's uniform identifier values shouldn't be confused with regular hash values. +The purpose of LUID values is to use just plain random numbers as ID values. But, because +of using such a incredibly large number space (128bit), we can just assume any collision +between such random LUID to be so unlikely as to reasonably ignore this possibility +altogether. Let's say, the collision of random LUID values won't ever happen, same as +the meltdown of an atomic power plant, which, as we all know, won't ever happen either. + +Relation to hash values +~~~~~~~~~~~~~~~~~~~~~~~ +When objects incorporate sich an unique LUID, this provides for a prime candidate to +derive hash values as a side-effect of that design: Since incorporating an LUID typically +means that this object has an _distinguishable identity_, all objects with the same LUID +should be considered _equivalent_ and thus hash to the same value. Consequently we can just +use a +size_t+ prefix of the LUID bitstring as hash value, without any further calculations. + diff --git a/doc/technical/howto/index.txt b/doc/technical/howto/index.txt index 20450cd49..49970fc99 100644 --- a/doc/technical/howto/index.txt +++ b/doc/technical/howto/index.txt @@ -12,3 +12,5 @@ similar usefull pieces of information targeted at Lumiera developers. See also == Notepad - link:DebugGdbPretty.html[Python pretty printers for GDB] +- link:HashFunctions.html[Notes regarding standard hash functions] + From 5269904e06115c7c694e1197e677809977404c05 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 14 Oct 2011 03:35:39 +0200 Subject: [PATCH 197/496] New documentation section for code base layout and conventions --- doc/technical/code/index.txt | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 doc/technical/code/index.txt diff --git a/doc/technical/code/index.txt b/doc/technical/code/index.txt new file mode 100644 index 000000000..f0f1553f8 --- /dev/null +++ b/doc/technical/code/index.txt @@ -0,0 +1,49 @@ +Code Base Organisation +====================== + +//Menu: label Code Base + + +This section of Lumiera's technical documentation deals with the code base as such, +not so much with the internals of the Lumiera application. It is targeted towards +people participating in Lumiera development. + +The Lumiera source base is huge and expected to grow for some time to come. Just +judging by the _lines of code_, Lumiera can be considered as a *medium sized system*, +which gives us some hints towards the adequate practices, conventions and the +degree of formalism to apply. + +Generally speaking, a *small system* doesn't exceed the mental capacity of a single +knowledgeable human. A single person can know everything and understand everything +in such a code base. Thus, any formalism and organisational overhead is detrimental; +we're best off doing away with ``Methods'' and just rely on taste and craftsmanship. + +But there is a hard limit for this approach, kind of a _sonic wall_ -- usually somewhere +around a size of 60000 LoC. At that point, the adequate value system flips around as a +whole. Since beyond that point, no single person can understand and know everything about +such a system, _communication and understanding become the primary concerns_ of the +development process. Suddenly, we _do need_ some organisation, some rituals and +a methodical approach. Yet still we don't need a high degree of formalism, since +at least we can easily _know and recall everyone involved_. + +To give a delineation to the other side, a constantly growing project might eventually +reach the point where the actual coding of new functionality makes up less than 25% +of the overall work required. More people might be involved than anyone can recall or +overlook reasonably. A code size of 1 Million LoC seems to exceed any kind of sufficiently +precise imagination. From that point on, a rigorosely formalsed organisation is vital +to keep such a *large scale project* from floundering. + +//// +Conventions +----------- +_to be written_ + +Guidelines +---------- +//// + +'''' + * also see the link:{ldoc}/devel/rfc.html[Design Process] for ongoing discussions + * see the link:/devs-vault/[Developers Vault] for frequently used developer's resources + + From e1854ec4906c1733387d63c26df77ead651b91d5 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 14 Oct 2011 03:36:36 +0200 Subject: [PATCH 198/496] Add Debian packaging informations to the documentation --- doc/technical/build/LumieraDebianPackage.txt | 89 ++++++++++++++++++++ doc/technical/build/index.txt | 6 +- doc/technical/index.txt | 3 +- doc/technical/infra/debianDepot.txt | 2 +- 4 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 doc/technical/build/LumieraDebianPackage.txt diff --git a/doc/technical/build/LumieraDebianPackage.txt b/doc/technical/build/LumieraDebianPackage.txt new file mode 100644 index 000000000..9e97f3162 --- /dev/null +++ b/doc/technical/build/LumieraDebianPackage.txt @@ -0,0 +1,89 @@ +The Lumiera Debian Package +========================== +:Author: Hermann Voßeler deb@ichthyostega.de +:Date: 11/2011 + +//Menu: label Debian Package + +Since several of the Lumiera core developers run a Debian flavour as their primary development platform, +it seems natural to care for the debian packaging of Lumiera ourselves. Moreover, we've declared +Debian/Stable to be our *reference platform* -- we'll provide any additionally required, more recent +packages through our own link:http://Lumiera.org/debian[Debian Apt-Repository] (Debian depot). + +TIP: readers new to debian packages may want to have a look into our + link:{ldoc}/user/tutorials/DebianBuilding.html[Debian build tutorial], + describing the commands for building and the structure of a debian + (source) package in general + + +Package build process +--------------------- +As our whole infrastructure relies heavily on the Git version management tool, it's only natural +also to organise the (debian) packaging with the help of Git. Fortunatlely, there is a nifty tool +called link:https://honk.sigxcpu.org/piki/projects/git-buildpackage/[git-buildpackage], written +by Guido Günther exactly for this purpuse: It treats the _debianisation_ as a branch in the +Git repository, forking off the mainline at the release point. + +For Lumiera, this _debianisation branch_ is called 'deb' and can be found in the +link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=summary[git:/git.lumiera.org/lumiera/debian] repository. + +Installing Lumiera +~~~~~~~~~~~~~~~~~~ +The link:SCons.html[SCons build] generates a relocatable distribution directory structure, +holding the main executable, additional tools and utilities, plus the core libraries and resources. +Here _relocatable_ means that this subtree can be moved and placed anywhere on the system. +As long as the relative directory layout remains intact, the executables will be able to find +and load the accompanying libraries and resources. By invoking the +scons install+ target, +this directory structure is placed into the installation target directory. + +Actually, this lookup process at application startup is performed in two phases + +- in the first phase the main application locates the directly linked shared libraries. + Especially those libraries belonging first class to the Lumiera application are built + with a relative searche path ('rpath' with $ORIGIN token). These libraries are placed + into the +modules/+ subfolder + +- after successfully launching the executable, the second phase performes a lookup programatically, + starting from the path location of the executable, trying to find a +setup.ini+ which defines + additional plug-ins and resources to load. Notably, the GUI to launch is loaded as a + plug-in through this mechanism. Moreover, this bootstrap configuration defines the + additional platform and user configuration to load for further outfitting of the system. + +LSB Installation Layout +~~~~~~~~~~~~~~~~~~~~~~~ +This organisation is used as foundation for packaging and installing. The primariy application +distribution structure will be located into a subfolder below +/usr/lib/+. Only the main application +executable will be symlinked into +/usr/bin/+. The LSB explicitly allows for such a layout, which is +typically used by large application bundles (OpenOffice, Gimp, Eclipse). Since the application +startup encompasses two phases, loading the extended configuration programmatically after +launching the application, such extended resources can easily be relocated into a separate +folder below +/usr/share/+, as required by LSB. + + +Releases, Branches and Conventions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Official releases are marked by a tag on the master branch. Usually, we'll also fork a +*release branch* at that point, featuring bugfixes only. From here we'll merge to the +*debian branch* + + +Package build commands +~~~~~~~~~~~~~~~~~~~~~~ +To (re)build the debian package + +. +git clone git:/git.lumiera.org/lumiera/debian+ +. +mkdir pack.deb+ +. +cd debian+ +. +git-buildpackage --git-upstream-branch=+ _RELEASE-TAG_ + +-- here _RELEASE-TAG_ denotes the point in the Git history, which should become +the reference source and be packaged into the *.orig.tar.gz. Usually, it's just +sufficient to use 'master' for that purpose. + + +Debian-Depot for installation via Apt +------------------------------------- +In addition to the packaging, we maintain a dedicated Apt-Repository for automated +installation and upgrades. We try to build the package for several Debian derived +distributions (like Ubuntu). -> link:../infra/debianDepot.html[more on the repository organisation] + diff --git a/doc/technical/build/index.txt b/doc/technical/build/index.txt index a9b3261da..83805ba4e 100644 --- a/doc/technical/build/index.txt +++ b/doc/technical/build/index.txt @@ -3,13 +3,15 @@ Lumiera build system As work progresses, we will add more information on the Lumiera build system. +//Menu: label Build System + build -- continuous integration -- packaging * SCons * Autotools * Dependencies * link:BuildDroneDraft.html[»Builddrone« concept from 2008] -* Packaging: Debian RPM -* Lumiera debian depot +* Packaging: link:LumieraDebianPackage.html[Debian] RPM +* Lumiera link:../infra/debianDepot.html/[debian depot] diff --git a/doc/technical/index.txt b/doc/technical/index.txt index 147751f86..b56ad484d 100644 --- a/doc/technical/index.txt +++ b/doc/technical/index.txt @@ -29,7 +29,8 @@ components. == Tools .Development -* link:http://www.lumiera.org/doxy/[*Doxygen generated documentation*] : Complete reference for the code of Lumiera. +* link:http://www.lumiera.org/doxy/[*Doxygen generated documentation*] : API documentation of the Lumiera code. +* organisation of the link:code/index.html[Code Base] in general .Building * link:build/index.html[*Buildsystem*] : Installation & compilation tools, dependencies and packaging. diff --git a/doc/technical/infra/debianDepot.txt b/doc/technical/infra/debianDepot.txt index f599950b2..6c8f95475 100644 --- a/doc/technical/infra/debianDepot.txt +++ b/doc/technical/infra/debianDepot.txt @@ -25,7 +25,7 @@ To (re)build the debian package . +git clone git:/git.lumiera.org/lumiera/debian+ . +mkdir pack.deb+ . +cd debian+ -. +git-buildpackage --git-upstream-branch=+_RELEASE-TAG_ +. +git-buildpackage --git-upstream-branch=+ _RELEASE-TAG_ -- here _RELEASE-TAG_ denotes the point in the Git history, which should become the reference source and be packaged into the *.orig.tar.gz. Usually, it's just From 6baadbaef07ea7d77243cfa98624eaa274115c18 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 14 Oct 2011 03:54:22 +0200 Subject: [PATCH 199/496] draft a workaround for hashing functor objects --- src/lib/functor-util.hpp | 21 ++++- .../proc/control/command-equality-test.cpp | 10 +- tests/lib/functor-util-test.cpp | 92 +++++++++++++++++-- 3 files changed, 108 insertions(+), 15 deletions(-) diff --git a/src/lib/functor-util.hpp b/src/lib/functor-util.hpp index d47b86a9c..b8f4780c3 100644 --- a/src/lib/functor-util.hpp +++ b/src/lib/functor-util.hpp @@ -38,11 +38,14 @@ #define FUNCTOR_UTIL_H_ #include <tr1/functional> -#include <cstring> -namespace util { ////////////TODO: refactor it. But probably not directly into namespace lib. Needs some more consideration though +namespace lib { + typedef size_t HashVal; +} + +namespace util { ////////////TODO: refactor namespace. But probably not directly into namespace lib. Needs some more consideration though using std::tr1::function; using std::tr1::bind; @@ -101,7 +104,7 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n namespace { // hiding some nasty details... /** - * This Class is used to surpass the access protection + * This Class is used to bypass the access protection * and break into the tr1::function implementation. * Thus we can implement a raw comparison function, * as a replacement for the missing functor comparison @@ -186,4 +189,16 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n } // namespace util +namespace std { +namespace tr1 { + + template<typename SIG> + inline size_t + hash_value (function<SIG> const& fun) + { + UNIMPLEMENTED ("hijack the function object and derive some hash value"); + } + +}} + #endif /*UTIL_HPP_*/ diff --git a/tests/components/proc/control/command-equality-test.cpp b/tests/components/proc/control/command-equality-test.cpp index 715a3b83f..c580f1eda 100644 --- a/tests/components/proc/control/command-equality-test.cpp +++ b/tests/components/proc/control/command-equality-test.cpp @@ -98,10 +98,11 @@ namespace test { * To conduct this test, we set up two sets of functions, and then build both complete * command objects and command implementation facilities based on them. * - * @note The hidden problem with those comparisons is the equivalence of function objects, - * which is required by TR1, but refused to implement by lib boost. We use a low level - * hack, based on internals of the boost function implementation, but this solution - * fails to detect equivalent functions under some circumstances (e.g. when there is + * @note The hidden problem with those comparisons is the equivalence of function objects. + * While required by TR1, unfortunately lib boost refuses to implement functor equality. + * Which forces us to resort to a low level hack, based on internals of the boost function + * implementation. This workaround reliably pinpoints differing functions, but sometimes + * fails to detect equivalent functions under specific circumstances (e.g. when there is * binding involved, and / or the binders have been cloned). Bottom line: \c == is * reliable, \c != might be wrong. * @@ -112,6 +113,7 @@ namespace test { * @see control::MementoTie * @see control::CommandImpl * @see command-basic-test.hpp + * @see functor-util.hpp functor equality workaround */ class CommandEquality_test : public Test { diff --git a/tests/lib/functor-util-test.cpp b/tests/lib/functor-util-test.cpp index 0692158fe..c031ddc60 100644 --- a/tests/lib/functor-util-test.cpp +++ b/tests/lib/functor-util-test.cpp @@ -27,12 +27,14 @@ #include "lib/functor-util.hpp" #include <tr1/functional> +#include <boost/functional/hash.hpp> #include <iostream> //using util::isnil; -//using std::string; +using lib::HashVal; using std::cout; using std::tr1::function; +using boost::hash; // note: only boost::hash allows for easy defining of custom hash functions namespace util { @@ -56,7 +58,7 @@ namespace test { /********************************************************************* * @test verify some aspects of the functor-util's behaviour. - * Often, this is just a scrapbook for new ideas.... + * At times, this is just a scrapbook for new ideas.... */ class FunctorUtil_test : public Test { @@ -64,18 +66,19 @@ namespace test { run (Arg) { verifyBruteForceComparison(); + verifyHashThroughBackdoor(); } + typedef function<void(int)> Fvi; + typedef function<int(void)> Fiv; + + typedef function<void(void)> Fvv; + /** @test workaround for the missing functor comparison operator */ void verifyBruteForceComparison() { - typedef function<void(int)> Fvi; - typedef function<int(void)> Fiv; - - typedef function<void(void)> Fvv; - Fvi f0; Fvi f1 (fun1); Fvi f2 (fun2); @@ -118,10 +121,83 @@ namespace test { CHECK (!rawComparison(fm3, fm4)); CHECK (!rawComparison(fm3, fm5)); CHECK (!rawComparison(fm3, fm6)); - CHECK (!rawComparison(fm4, fm5)); + CHECK (!rawComparison(fm4, fm5)); // note: same argument but different functor instance CHECK (!rawComparison(fm4, fm6)); CHECK (!rawComparison(fm5, fm6)); // again: can't detect they are equivalent } + + + /** @test workaround for missing standard hash + * calculation for functor objects. + * Workaround relying on boost + * implementation internals */ + void + verifyHashThroughBackdoor() + { + Fvi f0; + Fvi f1 (fun1); + Fvi f2 (fun2); + Fvi f22 (f2); + + hash<Fvi> calculateHash; + CHECK (calculateHash (f0)); + CHECK (calculateHash (f1)); + CHECK (calculateHash (f2)); + CHECK (calculateHash (f22)); + + HashVal h0 = calculateHash (f0); + HashVal h1 = calculateHash (f1); + HashVal h2 = calculateHash (f2); + HashVal h22 = calculateHash (f22); + + CHECK (h0 != h1); + CHECK (h0 != h2); + CHECK (h1 != h2); + + CHECK (h2 == h22); + + f1 = f2; + CHECK (h1 == h2); + CHECK (h1 != h0); + + CHECK (h0 != calculateHash (Fvi())); // note: equivalence not detected + + // checking functors based on member function(s) + Dummy dum1, dum2; + Fvi fm1 = bind (&Dummy::gummi, dum1, _1); + Fvi fm2 = bind (&Dummy::gummi, dum2, _1); + Fvv fm3 = bind (&Dummy::gummi, dum1, 23); + Fvv fm4 = bind (&Dummy::gummi, dum1, 24); + Fvv fm5 = bind (&Dummy::gummi, dum2, 24); + Fvv fm6 = bind (&Dummy::gummi, dum2, 24); + + HashVal hm1 = calculateHash (fm1); + HashVal hm2 = calculateHash (fm2); + + hash<Fvv> calculateHashVV; + HashVal hm3 = calculateHashVV (fm3); + HashVal hm4 = calculateHashVV (fm4); + HashVal hm5 = calculateHashVV (fm5); + HashVal hm6 = calculateHashVV (fm6); + + CHECK (h1 != hm1); + + CHECK (hm1 != hm2); + CHECK (hm1 != hm3); + CHECK (hm1 != hm4); + CHECK (hm1 != hm5); + CHECK (hm1 != hm6); + CHECK (hm2 != hm3); + CHECK (hm2 != hm4); + CHECK (hm2 != hm5); + CHECK (hm2 != hm6); + CHECK (hm3 != hm4); + CHECK (hm3 != hm5); + CHECK (hm3 != hm6); + CHECK (hm4 != hm5); + CHECK (hm4 != hm6); + CHECK (hm5 != hm6); // again: unable to detect the equivalence + } }; From 1627698911febe8bd25328e66a372962c20db77e Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 14 Oct 2011 14:49:55 +0200 Subject: [PATCH 200/496] implement hash function for functor obejects. Unit-test pass --- src/lib/functor-util.hpp | 45 ++++++++++++++++++++++++++++++--- tests/lib/functor-util-test.cpp | 1 + 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/lib/functor-util.hpp b/src/lib/functor-util.hpp index b8f4780c3..f10dc7b2b 100644 --- a/src/lib/functor-util.hpp +++ b/src/lib/functor-util.hpp @@ -38,6 +38,7 @@ #define FUNCTOR_UTIL_H_ #include <tr1/functional> +#include <boost/functional/hash.hpp> @@ -89,7 +90,7 @@ namespace util { ////////////TODO: refactor namespace. But probably not directly /** convenience shortcut to call two functors in sequence. * @return a Dispatch functor object which incorporates the - * functors as copy and on invocation calls the first + * functors as copy and on invocation calls the first * function and then returns the result of the second */ template<typename SIG> Dispatch<SIG> @@ -102,6 +103,9 @@ namespace util { ////////////TODO: refactor namespace. But probably not directly namespace { // hiding some nasty details... + + using lib::HashVal; + using boost::hash_combine; /** * This Class is used to bypass the access protection @@ -139,6 +143,16 @@ namespace util { ////////////TODO: refactor namespace. But probably not directly && (f1._M_functor._M_unused._M_const_object == f2._M_functor._M_unused._M_const_object ); } // note: we don't cover any member pointer offset + + friend HashVal + hash_value (HijackedFunction const& fun) + { + HashVal hash(0); + hash_combine (hash, fun.invoker_); + hash_combine (hash, fun._M_manager); + hash_combine (hash, fun._M_functor._M_unused._M_const_object); + return hash; // note: member pointer offset part uncovered + } }; } @@ -187,18 +201,41 @@ namespace util { ////////////TODO: refactor namespace. But probably not directly } + /** workaround to calculate a hash value for a given function object. + * @note use with caution. This implementation relies on internal details + * of boost/function; it can be expected to be rather conservative, + * i.e. yielding different hash values for objects, which actually + * are semantically equivalent. + * @warning especially function objects bound to member functions aren't + * fully supported. It \em may happen that we miss differences on the + * offset part and only hash the "this" pointer on some platform. + */ + template<typename SIG> + inline HashVal + rawHashValue (function<SIG> const& fun) + { + typedef HijackedFunction const& Hij; + + return hash_value (reinterpret_cast<Hij> (fun)); + } + } // namespace util namespace std { namespace tr1 { + /** inject into std::tr1 to be picked up by ADL: + * @return hash value of given functor + * @note use with caution. Hash is calculated + * relying on undocumented boost internals. + */ template<typename SIG> - inline size_t + inline lib::HashVal hash_value (function<SIG> const& fun) { - UNIMPLEMENTED ("hijack the function object and derive some hash value"); + return util::rawHashValue (fun); } - + }} #endif /*UTIL_HPP_*/ diff --git a/tests/lib/functor-util-test.cpp b/tests/lib/functor-util-test.cpp index c031ddc60..8ff8327fb 100644 --- a/tests/lib/functor-util-test.cpp +++ b/tests/lib/functor-util-test.cpp @@ -157,6 +157,7 @@ namespace test { CHECK (h2 == h22); f1 = f2; + h1 = calculateHash (f1); CHECK (h1 == h2); CHECK (h1 != h0); From f0b443abde2743c62747a437099ce45bd2f5e839 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 14 Oct 2011 22:46:04 +0200 Subject: [PATCH 201/496] add a page for documenting shady details and other smells --- doc/technical/code/darkCorners.txt | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 doc/technical/code/darkCorners.txt diff --git a/doc/technical/code/darkCorners.txt b/doc/technical/code/darkCorners.txt new file mode 100644 index 000000000..732c2c9ee --- /dev/null +++ b/doc/technical/code/darkCorners.txt @@ -0,0 +1,35 @@ +Dark Corners +============ + +_this page accounts for some problematic areas, sketchy solutions, +nonportable hacks, terrorism and other misdemeanour_ + +Library +------- + +Equality of Functors +~~~~~~~~~~~~~~~~~~~~ +One of the more important recent additions to the C++ language are function objects. +In addition to the features actually provided by the boost implementation, the tr1 report +also requires function instances to implement an equality operator. Unfortunately the +implementation approach choosen by boost makes a 100% correct implementation of +comparision very dificult, if not impossible. Thus, the boost developers refused +to implement this feature. + +The bad news is that really using the power of opaque function objects quickly drove +us (Lumiera) into a situation where such an equalty test and a hash calculation on +function objects would be necessary. The whole point of using function objects is +the ability to ``erase'' specific details, which has the downside that the resulting +generic objects are opaque and often dificult to manage, when it comes to storing +and retrieving objects building on such functors. + +Thus I built an hack, based on the implementation details of boost::function. +In +functor-util.hpp+ we define a +class HijackedFunction+, which has the same +data layout as the original boost::function. After forcibly casting such an function +(reference or pointer) into a +HijackedFunction+, we're able to inspect and evaluate +the implementation pointers for equality comparison and hash value calculation. +This approach works and actually detects copied functions to be _equal_, but is +unable to pinpoint _equivalence_, e.g. functors bound to the same function with +the same arguments through separate but otherwise identical invocations of +bind+. +Besides, should boost or the standard library implementors eventually change the +implementation, this workaround will break. From 2c380b5f99849c3601fff221a345982335ec0d63 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 15 Oct 2011 04:12:51 +0200 Subject: [PATCH 202/496] draft a dedicated test for buffer metadata keys --- src/proc/engine/buffer-metadata.hpp | 43 +++-- tests/46engine.tests | 4 + .../proc/engine/buffer-metadata-key-test.cpp | 158 ++++++++++++++++++ .../proc/engine/buffer-metadata-test.cpp | 18 +- 4 files changed, 198 insertions(+), 25 deletions(-) create mode 100644 tests/components/proc/engine/buffer-metadata-key-test.cpp diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 024343ea2..bad1b1241 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -56,6 +56,7 @@ #include "lib/error.hpp" #include "lib/symbol.hpp" +#include "lib/functor-util.hpp" #include <tr1/functional> #include <boost/functional/hash.hpp> @@ -64,13 +65,13 @@ namespace engine { + using lib::HashVal; using lib::Literal; using std::tr1::function; namespace error = lumiera::error; - typedef size_t HashVal; enum BufferState { NIL, @@ -126,11 +127,10 @@ namespace engine { struct TypeHandler { - typedef function<void(void*)> Ctor; - typedef function<void(void*)> Dtor; + typedef function<void(void*)> DoInBuffer; - Ctor createAttached; - Dtor destroyAttached; + DoInBuffer createAttached; + DoInBuffer destroyAttached; TypeHandler() : createAttached() @@ -138,18 +138,28 @@ namespace engine { { } template<class X> - TypeHandler() + TypeHandler(const X*) : createAttached (buildIntoBuffer<X>) , destroyAttached (destroyInBuffer<X>) { } - friend size_t - hash_value (TypeHandler handler) + bool + isValid() const + { + return bool(createAttached) + && bool(destroyAttached); + } + + friend HashVal + hash_value (TypeHandler const& handler) { - boost::hash<Ctor> ctorHash; -// return boost::hash_combine(ctorHash(handler.createAttached), handler.destroyAttached.); - ////////////////TODO how to calculate the hash of an function object - return 0; + HashVal hash(0); + if (handler.isValid()) + { + boost::hash_combine(hash, handler.createAttached); + boost::hash_combine(hash, handler.destroyAttached); + } + return hash; } }; @@ -182,6 +192,7 @@ namespace engine { HashVal parent_; HashVal hashID_; + protected: size_t storageSize_; TypeHandler instanceFunc_; LocalKey specifics_; @@ -195,8 +206,8 @@ namespace engine { * @param storageSize fundamental info: buffer size */ Key (HashVal familyID, size_t storageSize) - : parent_(chainedHash (familyID, storageSize)) - , hashID_(familyID) + : parent_(familyID) + , hashID_(chainedHash (familyID, storageSize)) , storageSize_(storageSize) , instanceFunc_(RAW_BUFFER) , specifics_(UNSPECIFIC) @@ -402,7 +413,7 @@ namespace engine { /** */ - Metadata::Entry& + inline Metadata::Entry& Metadata::markLocked (HashVal parentKey, const void* buffer) { UNIMPLEMENTED ("transition to locked state"); @@ -419,7 +430,7 @@ namespace engine { return this->get(newKey); } - void + inline void Metadata::release (HashVal key) { UNIMPLEMENTED ("metadata memory management"); diff --git a/tests/46engine.tests b/tests/46engine.tests index e1a3f9704..e2f704018 100644 --- a/tests/46engine.tests +++ b/tests/46engine.tests @@ -7,6 +7,10 @@ return: 0 END +PLANNED "buffer metadata type keys" BufferMetadataKey_test <<END +END + + PLANNED "buffer metadata and state transitions" BufferMetadata_test <<END END diff --git a/tests/components/proc/engine/buffer-metadata-key-test.cpp b/tests/components/proc/engine/buffer-metadata-key-test.cpp new file mode 100644 index 000000000..451296930 --- /dev/null +++ b/tests/components/proc/engine/buffer-metadata-key-test.cpp @@ -0,0 +1,158 @@ +/* + BufferMetadataKey(Test) - calculation of (internal) buffer metadata type keys + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/error.hpp" +#include "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 <boost/format.hpp> +#include <boost/scoped_ptr.hpp> +#include <cstdlib> +//#include <iostream> + +//using boost::format; +//using std::string; +//using std::cout; +//using util::for_each; +using boost::scoped_ptr; +using util::isnil; +using util::isSameObject; + + +namespace engine{ +namespace metadata{ +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 void* const SOME_POINTER = &JUST_SOMETHING; +// const uint TEST_SIZE = 1024*1024; +// const uint TEST_ELMS = 20; + + + } + + + /*********************************************************************** + * @test verify calculation and relations of Buffer metadata type keys. + * These are used internally within the standard implementation + * of BufferProvider to keep track of various kinds of buffers, + * to provide a service for attaching metadata, e.g. a state flag. + * These metadata key entries are based on chained hash values, + * thus forming sort-of a "type" hierarchy. + * - the actual BufferProvider instance-ID is the top level + * - second level is the size of the buffer required + * - optionally, custom ctor/dtor functions can be registered + * - and, also optionally, the implementation might attach an type-ID + */ + class BufferMetadataKey_test : public Test + { + + virtual void + run (Arg) + { + CHECK (ensure_proper_fixture()); + buildSimpleKeys(); + verifyChainedHashes(); + verifyTypeSpecialisation(); + } + + + bool + ensure_proper_fixture() + { + return (SIZE_A != SIZE_B); + } + + + void + buildSimpleKeys() + { + HashVal family(123); + Key k1(family, SIZE_A); + Key k12(k1, SIZE_B); + Key k123(k12, LocalKey(56)); + + CHECK (HashVal (k1)); + CHECK (HashVal (k12)); + CHECK (HashVal (k123)); + } + + + void + verifyChainedHashes() + { + HashVal family(123); + HashVal otherFamily(456); + + Key k1(family, SIZE_A); + Key k1o(otherFamily, SIZE_A); + CHECK (HashVal(k1) != HashVal(k1o)); + + // hash is reproducible + CHECK (HashVal(k1) == HashVal(Key(family, SIZE_A))); + + // differentiate on buffer size + Key k12(k1, SIZE_B); + Key k121(k12, SIZE_A); + Key k2(family, SIZE_B); + + CHECK (HashVal(k1) != HashVal(k121)); + CHECK (HashVal(k12) != HashVal(k2)); + + // so the specialisation path really matters, but this is reproducible... + CHECK (HashVal(k121) == HashVal(Key(Key(Key(family,SIZE_A),SIZE_B),SIZE_A))); + } + + + }; + + + /** Register this test class... */ + LAUNCHER (BufferMetadataKey_test, "unit player"); + + + +}}} // namespace engine::metadata::test diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp index de62e39a3..3e4659a0d 100644 --- a/tests/components/proc/engine/buffer-metadata-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -96,15 +96,15 @@ 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)) - ; - } + { + 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 From 28c555aac7c6b12136e69deff2e5269247cea33b Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 15 Oct 2011 05:05:30 +0200 Subject: [PATCH 203/496] define a type handler for attach an object to a buffer --- src/proc/engine/buffer-metadata.hpp | 88 +++++++++++---- .../proc/engine/buffer-metadata-key-test.cpp | 101 +++++++++++++++++- 2 files changed, 166 insertions(+), 23 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index bad1b1241..5312a1358 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -67,7 +67,9 @@ namespace engine { using lib::HashVal; using lib::Literal; + using std::tr1::bind; using std::tr1::function; + using std::tr1::placeholders::_1; namespace error = lumiera::error; @@ -110,21 +112,40 @@ namespace engine { } }; - template<class X> - void - buildIntoBuffer (void* storageBuffer) - { - new(storageBuffer) X(); - } - template<class X> - void - destroyInBuffer (void* storageBuffer) - { - X* embedded = static_cast<X*> (storageBuffer); - embedded->~X(); - } + namespace { // Helpers for construction within the buffer... + + template<class X> + inline void + buildIntoBuffer (void* storageBuffer) + { + new(storageBuffer) X(); + } + + template<class X, typename A1> + inline void + buildIntoBuffer_A1 (void* storageBuffer, A1 arg1) + { + new(storageBuffer) X(arg1); + } + + template<class X> + inline void + destroyInBuffer (void* storageBuffer) + { + X* embedded = static_cast<X*> (storageBuffer); + embedded->~X(); + } + }//(End)placement-new helpers + + /** + * A pair of functors to maintain a datastructure within the buffer. + * TypeHandler describes how to outfit the buffer in a specific way. + * When defined, the buffer will be prepared when locking and cleanup + * will be invoked automatically when releasing. Especially, this + * can be used to \em attach an object to the buffer (placement-new) + */ struct TypeHandler { typedef function<void(void*)> DoInBuffer; @@ -132,17 +153,43 @@ namespace engine { DoInBuffer createAttached; DoInBuffer destroyAttached; + /** build an invalid NIL TypeHandler */ TypeHandler() : createAttached() , destroyAttached() { } - template<class X> - TypeHandler(const X*) - : createAttached (buildIntoBuffer<X>) - , destroyAttached (destroyInBuffer<X>) + /** build a TypeHandler + * binding to arbitrary constructor and destructor functions. + * On invocation, these functions get a void* to the buffer. + * @note the functor objects created from these operations + * might be shared for handling multiple buffers. + * Be careful with any state or arguments. + */ + template<typename CTOR, typename DTOR> + TypeHandler(CTOR ctor, DTOR dtor) + : createAttached (ctor) + , destroyAttached (dtor) { } + /** builder function defining a TypeHandler + * to place a default-constructed object + * into the buffer. */ + template<class X> + static TypeHandler + create () + { + return TypeHandler (buildIntoBuffer<X>, destroyInBuffer<X>); + } + + template<class X, typename A1> + static TypeHandler + create (A1 a1) + { + return TypeHandler ( bind (buildIntoBuffer_A1<X,A1>, _1, a1) + , destroyInBuffer<X>); + } + bool isValid() const { @@ -168,9 +215,10 @@ namespace engine { const LocalKey UNSPECIFIC; const TypeHandler RAW_BUFFER; } - - - /* === Implementation === */ + + + + /* === Implementation === */ namespace metadata { diff --git a/tests/components/proc/engine/buffer-metadata-key-test.cpp b/tests/components/proc/engine/buffer-metadata-key-test.cpp index 451296930..b2ef96013 100644 --- a/tests/components/proc/engine/buffer-metadata-key-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-key-test.cpp @@ -36,6 +36,8 @@ //#include <boost/format.hpp> #include <boost/scoped_ptr.hpp> #include <cstdlib> +#include <cstring> +#include <limits> //#include <iostream> //using boost::format; @@ -66,11 +68,72 @@ namespace test { 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 void* const SOME_POINTER = &JUST_SOMETHING; +// const size_t CHAR_MAX = 1;//std::numeric_limits<char>::max(); +// const HashVal JUST_SOMETHING = 123; +// const void* const SOME_POINTER = &JUST_SOMETHING; // const uint TEST_SIZE = 1024*1024; // const uint TEST_ELMS = 20; - + + /** + * Test Mock to verify the attachment of objects to the buffer. + * An instance of this class overwrites the occupied storage + * with an ascending sequence of numbers on construction, + * and clears the memory area on destruction. + * + * This allows to verify that an instance of this class + * has actually been placed into the buffer, and will be + * cleaned up properly + */ + template<size_t siz> + struct PlacedNumbers + { + typedef char Pattern[siz]; + + Pattern pattern_; + + PlacedNumbers() + { + for (size_t i=0; i<siz; ++i) + pattern_[i] = i % CHAR_MAX; + } + + ~PlacedNumbers() + { + for (size_t i=0; i<siz; ++i) + pattern_[i] = 0; + } + + + /* === diagnostics === */ + + static bool + verifyFilled (const void* buff) + { + REQUIRE (buff); + const Pattern& patt = *reinterpret_cast<const Pattern*> (buff); + + for (size_t i=0; i<siz; ++i) + if (patt[i] != char(i % CHAR_MAX)) + return false; + + return true; + } + + static bool + verifyCleared (const void* buff) + { + REQUIRE (buff); + const Pattern& patt = *reinterpret_cast<const Pattern*> (buff); + + for (size_t i=0; i<siz; ++i) + if (patt[i]) + return false; + + return true; + } + + }; + } @@ -96,6 +159,7 @@ namespace test { CHECK (ensure_proper_fixture()); buildSimpleKeys(); verifyChainedHashes(); + verifyTypeHandler<500>(); verifyTypeSpecialisation(); } @@ -147,6 +211,37 @@ namespace test { } + template<size_t SIZ> + void + verifyTypeHandler() + { + char buff[SIZ]; + memset (buff, '\0', SIZ); + + typedef PlacedNumbers<SIZ> Pattern; + + TypeHandler attachPattern = TypeHandler::create<Pattern>(); + + CHECK (attachPattern.isValid()); + CHECK (0 != hash_value(attachPattern)); + + CHECK (Pattern::verifyCleared (buff)); + attachPattern.createAttached (buff); // invoke the ctor-functor to place an instance of PlacedNumbers + CHECK (Pattern::verifyFilled (buff)); + attachPattern.destroyAttached (buff); // invoke the dtor-functor to clear the attached instance + CHECK (Pattern::verifyCleared (buff)); + } + + + void + verifyTypeSpecialisation() + { + HashVal family(123); + Key kb (family, SIZE_A); + + TypeHandler noHandler; + + } }; From 3ae35b2df9d4c89b5312ee0ebbfe06474efbfa05 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 15 Oct 2011 17:47:50 +0200 Subject: [PATCH 204/496] define more extensive testing of derived metadata keys --- .../proc/engine/buffer-metadata-key-test.cpp | 139 +++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/tests/components/proc/engine/buffer-metadata-key-test.cpp b/tests/components/proc/engine/buffer-metadata-key-test.cpp index b2ef96013..68b05e0d3 100644 --- a/tests/components/proc/engine/buffer-metadata-key-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-key-test.cpp @@ -226,7 +226,7 @@ namespace test { CHECK (0 != hash_value(attachPattern)); CHECK (Pattern::verifyCleared (buff)); - attachPattern.createAttached (buff); // invoke the ctor-functor to place an instance of PlacedNumbers + attachPattern.createAttached (buff); // invoke the ctor-functor to place an instance of PlacedNumbers CHECK (Pattern::verifyFilled (buff)); attachPattern.destroyAttached (buff); // invoke the dtor-functor to clear the attached instance CHECK (Pattern::verifyCleared (buff)); @@ -239,8 +239,145 @@ namespace test { HashVal family(123); Key kb (family, SIZE_A); + typedef PlacedNumbers<45> Marker; + TypeHandler placeMarker = TypeHandler::create<Marker>(); TypeHandler noHandler; + LocalKey opaque1 (rand() % 1000); + LocalKey opaque2 (1000 + rand() % 1000); + + Key k_siz (kb, SIZE_B); + Key k_han0(kb, noHandler); + Key k_han1(kb, placeMarker); + Key k_loc1(kb, opaque1); + Key k_loc2(kb, opaque2); + + CHECK (kb != k_siz ); + CHECK (kb != k_han0); + CHECK (kb != k_han1); + CHECK (kb != k_loc1); + CHECK (kb != k_loc2); + CHECK (k_siz != k_han0); + CHECK (k_siz != k_han1); + CHECK (k_siz != k_loc1); + CHECK (k_siz != k_loc2); + CHECK (k_han0 != k_han1); + CHECK (k_han0 != k_loc1); + CHECK (k_han0 != k_loc2); + CHECK (k_han1 != k_loc1); + CHECK (k_han1 != k_loc2); + CHECK (k_loc1 != k_loc2); + + CHECK (HashVal(kb ) != HashVal(k_siz )); + CHECK (HashVal(kb ) != HashVal(k_han0)); + CHECK (HashVal(kb ) != HashVal(k_han1)); + CHECK (HashVal(kb ) != HashVal(k_loc1)); + CHECK (HashVal(kb ) != HashVal(k_loc2)); + CHECK (HashVal(k_siz ) != HashVal(k_han0)); + CHECK (HashVal(k_siz ) != HashVal(k_han1)); + CHECK (HashVal(k_siz ) != HashVal(k_loc1)); + CHECK (HashVal(k_siz ) != HashVal(k_loc2)); + CHECK (HashVal(k_han0) != HashVal(k_han1)); + CHECK (HashVal(k_han0) != HashVal(k_loc1)); + CHECK (HashVal(k_han0) != HashVal(k_loc2)); + CHECK (HashVal(k_han1) != HashVal(k_loc1)); + CHECK (HashVal(k_han1) != HashVal(k_loc2)); + CHECK (HashVal(k_loc1) != HashVal(k_loc2)); + +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834 + CHECK (SIZE_A == verifySize(kb )); + CHECK (SIZE_B == verifySize(k_siz )); + CHECK (SIZE_A == verifySize(k_han0)); + CHECK (SIZE_A == verifySize(k_han1)); + CHECK (SIZE_A == verifySize(k_loc1)); + CHECK (SIZE_A == verifySize(k_loc2)); + + CHECK (noHandler == verifyHandler(kb )); + CHECK (noHandler == verifyHandler(k_siz )); + CHECK (noHandler == verifyHandler(k_han0)); + CHECK (placeMarker == verifyHandler(k_han1)); + CHECK (noHandler == verifyHandler(k_loc1)); + CHECK (noHandler == verifyHandler(k_loc2)); + + CHECK (UNSPECIFIC == verifySpecifics(kb )); + CHECK (UNSPECIFIC == verifySpecifics(k_siz )); + CHECK (UNSPECIFIC == verifySpecifics(k_han0)); + CHECK (UNSPECIFIC == verifySpecifics(k_han1)); + CHECK (opaque1 == verifySpecifics(k_loc1)); + CHECK (opaque2 == verifySpecifics(k_loc2)); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834 + + // Verify 2nd level specialisation (some examples) + Key k_han1_siz (k_han1, SIZE_B); + Key k_siz_han1 (k_siz, placeMarker); + + // Verify some 3rd level specialisations + Key k_han1_siz_loc2 (k_han1_siz, opaque2); + Key k_loc2_han1_siz (Key(k_loc2,placeMarker), SIZE_B); + +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834 + CHECK (SIZE_B == verifySize(k_han1_siz )); + CHECK (SIZE_B == verifySize(k_siz_han1 )); + CHECK (SIZE_B == verifySize(k_han1_siz_loc2)); + CHECK (SIZE_B == verifySize(k_loc2_han1_siz)); + + CHECK (placeMarker == verifyHandler(k_han1_siz )); + CHECK (placeMarker == verifyHandler(k_siz_han1 )); + CHECK (placeMarker == verifyHandler(k_han1_siz_loc2)); + CHECK (placeMarker == verifyHandler(k_loc2_han1_siz)); + + CHECK (UNSPECIFIC == verifySpecifics(k_han1_siz )); + CHECK (UNSPECIFIC == verifySpecifics(k_siz_han1 )); + CHECK (placeMarker == verifySpecifics(k_han1_siz_loc2)); + CHECK (placeMarker == verifySpecifics(k_loc2_han1_siz)); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834 + + // for equality, also the order of specialisation matters + CHECK (k_han1_siz != k_siz_han1 ); + CHECK (k_han1_siz_loc2 != k_loc2_han1_siz); + + CHECK (HashVal(k_han1_siz ) != HashVal(k_siz_han1 )); + CHECK (HashVal(k_han1_siz_loc2) != HashVal(k_loc2_han1_siz)); + + // yet it *is* equality + Key k_again (Key(k_han1,SIZE_B), opaque2); + CHECK (k_again == k_han1_siz_loc2); + CHECK (HashVal(k_again) == HashVal(k_han1_siz_loc2)); + + // pick just some combinations for cross verification... + CHECK (kb != k_han1_siz ); + CHECK (kb != k_siz_han1 ); + CHECK (kb != k_han1_siz_loc2); + CHECK (kb != k_loc2_han1_siz); + CHECK (k_han1 != k_han1_siz ); + CHECK (k_han1 != k_siz_han1 ); + CHECK (k_han1 != k_han1_siz_loc2); + CHECK (k_han1 != k_loc2_han1_siz); + CHECK (k_siz != k_han1_siz ); + CHECK (k_siz != k_siz_han1 ); + CHECK (k_siz != k_han1_siz_loc2); + CHECK (k_siz != k_loc2_han1_siz); + CHECK (k_loc2 != k_han1_siz ); + CHECK (k_loc2 != k_siz_han1 ); + CHECK (k_loc2 != k_han1_siz_loc2); + CHECK (k_loc2 != k_loc2_han1_siz); + + CHECK (HashVal(kb ) != HashVal(k_han1_siz )); + CHECK (HashVal(kb ) != HashVal(k_siz_han1 )); + CHECK (HashVal(kb ) != HashVal(k_han1_siz_loc2)); + CHECK (HashVal(kb ) != HashVal(k_loc2_han1_siz)); + CHECK (HashVal(k_han1) != HashVal(k_han1_siz )); + CHECK (HashVal(k_han1) != HashVal(k_siz_han1 )); + CHECK (HashVal(k_han1) != HashVal(k_han1_siz_loc2)); + CHECK (HashVal(k_han1) != HashVal(k_loc2_han1_siz)); + CHECK (HashVal(k_siz ) != HashVal(k_han1_siz )); + CHECK (HashVal(k_siz ) != HashVal(k_siz_han1 )); + CHECK (HashVal(k_siz ) != HashVal(k_han1_siz_loc2)); + CHECK (HashVal(k_siz ) != HashVal(k_loc2_han1_siz)); + CHECK (HashVal(k_loc2) != HashVal(k_han1_siz )); + CHECK (HashVal(k_loc2) != HashVal(k_siz_han1 )); + CHECK (HashVal(k_loc2) != HashVal(k_han1_siz_loc2)); + CHECK (HashVal(k_loc2) != HashVal(k_loc2_han1_siz)); } }; From 3d859c8b5354fd464c674e53a7fec526bd2b657e Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 15 Oct 2011 22:46:08 +0200 Subject: [PATCH 205/496] buffer metadata hash keys implemented and tested thus far --- src/proc/engine/buffer-metadata.hpp | 15 +++ .../proc/engine/buffer-metadata-key-test.cpp | 103 ++++++++++-------- 2 files changed, 73 insertions(+), 45 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 5312a1358..eda07575e 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -208,8 +208,23 @@ namespace engine { } return hash; } + + friend bool + operator== (TypeHandler const& left, TypeHandler const& right) + { + return (!left.isValid() && !right.isValid()) + || ( util::rawComparison(left.createAttached, right.createAttached) + && util::rawComparison(left.destroyAttached, right.destroyAttached) + ); + } + friend bool + operator!= (TypeHandler const& left, TypeHandler const& right) + { + return !(left == right); + } }; + namespace { // internal constants to mark the default case const LocalKey UNSPECIFIC; diff --git a/tests/components/proc/engine/buffer-metadata-key-test.cpp b/tests/components/proc/engine/buffer-metadata-key-test.cpp index 68b05e0d3..a0e5d8f61 100644 --- a/tests/components/proc/engine/buffer-metadata-key-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-key-test.cpp @@ -24,26 +24,13 @@ #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 <boost/format.hpp> #include <boost/scoped_ptr.hpp> #include <cstdlib> #include <cstring> #include <limits> -//#include <iostream> -//using boost::format; -//using std::string; -//using std::cout; -//using util::for_each; using boost::scoped_ptr; using util::isnil; using util::isSameObject; @@ -53,10 +40,7 @@ namespace engine{ namespace metadata{ namespace test { - -// using lib::AllocationCluster; -// using mobject::session::PEffect; -// using ::engine::BuffHandle; + using lumiera::error::LUMIERA_ERROR_INVALID; using lumiera::error::LUMIERA_ERROR_LIFECYCLE; @@ -68,13 +52,8 @@ namespace test { const size_t SIZE_A = 1 + rand() % TEST_MAX_SIZE; const size_t SIZE_B = 1 + rand() % TEST_MAX_SIZE; -// const size_t CHAR_MAX = 1;//std::numeric_limits<char>::max(); -// const HashVal JUST_SOMETHING = 123; -// const void* const SOME_POINTER = &JUST_SOMETHING; -// const uint TEST_SIZE = 1024*1024; -// const uint TEST_ELMS = 20; - /** + /** * Test Mock to verify the attachment of objects to the buffer. * An instance of this class overwrites the occupied storage * with an ascending sequence of numbers on construction, @@ -131,11 +110,48 @@ namespace test { return true; } - }; - } + /** + * Helper to investigate the settings stored in Metadata Key elements. + * Since these are protected, we use an derived class as adapter + */ + struct KeyTypeSpecialisationDiagnostics + : Key + { + size_t const& investigateSize() const { return this->storageSize_; } + TypeHandler const& investigateHandler() const { return this->instanceFunc_; } + LocalKey const& investigateSpecifics() const { return this->specifics_; } + + KeyTypeSpecialisationDiagnostics (Key const& toInvestigate) + : Key(toInvestigate) + { } + }; + + + inline size_t + verifySize (Key const& subject) + { + return KeyTypeSpecialisationDiagnostics(subject).investigateSize(); + } + + inline const TypeHandler + verifyHandler (Key const& subject) + { + return KeyTypeSpecialisationDiagnostics(subject).investigateHandler(); + } + + inline const LocalKey + verifySpecifics (Key const& subject) + { + return KeyTypeSpecialisationDiagnostics(subject).investigateSpecifics(); + } + + }//(End) Test helpers + + + /*********************************************************************** @@ -148,7 +164,7 @@ namespace test { * - the actual BufferProvider instance-ID is the top level * - second level is the size of the buffer required * - optionally, custom ctor/dtor functions can be registered - * - and, also optionally, the implementation might attach an type-ID + * - also optionally, implementation might attach an private-ID */ class BufferMetadataKey_test : public Test { @@ -237,7 +253,7 @@ namespace test { verifyTypeSpecialisation() { HashVal family(123); - Key kb (family, SIZE_A); + Key kb (family, SIZE_A); // "root" key typedef PlacedNumbers<45> Marker; TypeHandler placeMarker = TypeHandler::create<Marker>(); @@ -246,11 +262,11 @@ namespace test { LocalKey opaque1 (rand() % 1000); LocalKey opaque2 (1000 + rand() % 1000); - Key k_siz (kb, SIZE_B); - Key k_han0(kb, noHandler); - Key k_han1(kb, placeMarker); - Key k_loc1(kb, opaque1); - Key k_loc2(kb, opaque2); + Key k_siz (kb, SIZE_B); // sub-key to "root": use a different buffer size + Key k_han0(kb, noHandler); // sub-key to "root": use a locally defined type functor + Key k_han1(kb, placeMarker); // sub-key to "root": use yet another type functor + Key k_loc1(kb, opaque1); // sub-key to "root": attach an private opaque ID + Key k_loc2(kb, opaque2); // sub-key to "root": attach another opaque ID CHECK (kb != k_siz ); CHECK (kb != k_han0); @@ -284,7 +300,6 @@ namespace test { CHECK (HashVal(k_han1) != HashVal(k_loc2)); CHECK (HashVal(k_loc1) != HashVal(k_loc2)); -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834 CHECK (SIZE_A == verifySize(kb )); CHECK (SIZE_B == verifySize(k_siz )); CHECK (SIZE_A == verifySize(k_han0)); @@ -292,12 +307,12 @@ namespace test { CHECK (SIZE_A == verifySize(k_loc1)); CHECK (SIZE_A == verifySize(k_loc2)); - CHECK (noHandler == verifyHandler(kb )); - CHECK (noHandler == verifyHandler(k_siz )); + CHECK (RAW_BUFFER == verifyHandler(kb )); + CHECK (RAW_BUFFER == verifyHandler(k_siz )); CHECK (noHandler == verifyHandler(k_han0)); CHECK (placeMarker == verifyHandler(k_han1)); - CHECK (noHandler == verifyHandler(k_loc1)); - CHECK (noHandler == verifyHandler(k_loc2)); + CHECK (RAW_BUFFER == verifyHandler(k_loc1)); + CHECK (RAW_BUFFER == verifyHandler(k_loc2)); CHECK (UNSPECIFIC == verifySpecifics(kb )); CHECK (UNSPECIFIC == verifySpecifics(k_siz )); @@ -305,17 +320,16 @@ namespace test { CHECK (UNSPECIFIC == verifySpecifics(k_han1)); CHECK (opaque1 == verifySpecifics(k_loc1)); CHECK (opaque2 == verifySpecifics(k_loc2)); -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834 + // Verify 2nd level specialisation (some examples) - Key k_han1_siz (k_han1, SIZE_B); - Key k_siz_han1 (k_siz, placeMarker); + Key k_han1_siz (k_han1, SIZE_B); // sub-key deriving from k_han1, but differing buffer size + Key k_siz_han1 (k_siz, placeMarker); // sub-key deriving from k_siz, but using another type functor // Verify some 3rd level specialisations Key k_han1_siz_loc2 (k_han1_siz, opaque2); Key k_loc2_han1_siz (Key(k_loc2,placeMarker), SIZE_B); -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834 CHECK (SIZE_B == verifySize(k_han1_siz )); CHECK (SIZE_B == verifySize(k_siz_han1 )); CHECK (SIZE_B == verifySize(k_han1_siz_loc2)); @@ -328,9 +342,8 @@ namespace test { CHECK (UNSPECIFIC == verifySpecifics(k_han1_siz )); CHECK (UNSPECIFIC == verifySpecifics(k_siz_han1 )); - CHECK (placeMarker == verifySpecifics(k_han1_siz_loc2)); - CHECK (placeMarker == verifySpecifics(k_loc2_han1_siz)); -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834 + CHECK (opaque2 == verifySpecifics(k_han1_siz_loc2)); + CHECK (opaque2 == verifySpecifics(k_loc2_han1_siz)); // for equality, also the order of specialisation matters CHECK (k_han1_siz != k_siz_han1 ); @@ -339,7 +352,7 @@ namespace test { CHECK (HashVal(k_han1_siz ) != HashVal(k_siz_han1 )); CHECK (HashVal(k_han1_siz_loc2) != HashVal(k_loc2_han1_siz)); - // yet it *is* equality + // yet this *is* an semantic equality test Key k_again (Key(k_han1,SIZE_B), opaque2); CHECK (k_again == k_han1_siz_loc2); CHECK (HashVal(k_again) == HashVal(k_han1_siz_loc2)); From 011afa4be8fc0fab41906d4c1f70b960f80ee264 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 15 Oct 2011 23:41:31 +0200 Subject: [PATCH 206/496] fix: ubiquitous constant defined in header ..resulting in that global constant being allocated a gazillion times --- src/lib/symbol-impl.cpp | 3 +++ src/lib/symbol.hpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/symbol-impl.cpp b/src/lib/symbol-impl.cpp index 9e09b4254..7edabf2a8 100644 --- a/src/lib/symbol-impl.cpp +++ b/src/lib/symbol-impl.cpp @@ -50,6 +50,9 @@ using boost::hash_combine; namespace lib { + const size_t STRING_MAX_RELEVANT = 1000; + + /** equality on Symbol values is defined * based on the content, not the address. */ bool diff --git a/src/lib/symbol.hpp b/src/lib/symbol.hpp index 5c1f4d298..c032a3b95 100644 --- a/src/lib/symbol.hpp +++ b/src/lib/symbol.hpp @@ -112,7 +112,7 @@ namespace lib { /** safety guard: maximum number of chars to process. * For comparisons, hash calculations etc., when dealing * with raw char ptrs (typically literal values) */ - const size_t STRING_MAX_RELEVANT = 1000; + extern const size_t STRING_MAX_RELEVANT; /* ===== to be picked up by ADL ===== */ From 1772659a00bd5d0ca2b7de5577532de2eb0f637e Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 16 Oct 2011 00:06:57 +0200 Subject: [PATCH 207/496] start implementing buffer metadata records --- src/proc/engine/buffer-metadata.hpp | 99 +++++++++++++++++-- .../proc/engine/buffer-metadata-test.cpp | 12 ++- 2 files changed, 99 insertions(+), 12 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index eda07575e..d6a4cd477 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -73,6 +73,11 @@ namespace engine { namespace error = lumiera::error; + namespace metadata { + class Key; + class Entry; + } + enum BufferState @@ -94,9 +99,6 @@ namespace engine { { uint64_t privateID_; - /** assignment prohibited */ - LocalKey& operator= (LocalKey const&); - public: LocalKey (uint64_t opaqueValue=0) : privateID_(opaqueValue) @@ -110,6 +112,17 @@ namespace engine { boost::hash<uint64_t> hashFunction; return hashFunction(lkey.privateID_); } + + private: + /** assignment usually prohibited */ + LocalKey& operator= (LocalKey const& o) + { + privateID_ = o.privateID_; + return *this; + } + + /** but Key assignments are acceptable */ + friend class metadata::Key; }; @@ -229,6 +242,18 @@ namespace engine { const LocalKey UNSPECIFIC; const TypeHandler RAW_BUFFER; + + inline bool + nontrivial (TypeHandler const& toVerify) + { + return RAW_BUFFER != toVerify; + } + + inline bool + nontrivial (LocalKey const& toVerify) + { + return UNSPECIFIC != toVerify; + } } @@ -295,7 +320,7 @@ namespace engine { * Using different ctor and dtor functions, * all else remaining the same as with parent */ - Key (Key const& parent, TypeHandler differingTypeHandlerFunctions) + Key (Key const& parent, TypeHandler const& differingTypeHandlerFunctions) : parent_(parent.hashID_) , hashID_(chainedHash (parent_, differingTypeHandlerFunctions)) , storageSize_(parent.storageSize_) @@ -401,33 +426,71 @@ namespace engine { class Metadata : boost::noncopyable { + Literal id_; + HashVal family_; + public: typedef metadata::Key Key; typedef metadata::Entry Entry; - + /** establish a metadata registry. + * Such will maintain a family of buffer type entries + * and provide a service for storing and retrieving metadata + * for concrete buffer entries associated with these types. + * @param implementationID to distinguish families + * of type keys belonging to different registries. + */ Metadata (Literal implementationID) + : id_(implementationID) + , family_(hash_value(id_)) { } + /** combine the distinguishing properties + * into a single type key, which will be known/remembered + * from that point on. Properties are combined according to + * a fixed type specialisation order, with the buffer size + * forming the base level, possible TypeHandler functors the + * second level, and implementation defined LocalKey entries + * the third level. All these levels describe abstract type + * keys, not entries for concrete buffers. The latter are + * always created as children of a known type key. + */ Key key ( size_t storageSize , TypeHandler instanceFunc =RAW_BUFFER , LocalKey specifics =UNSPECIFIC) { - UNIMPLEMENTED ("combine the distinguishing properties into a single hash"); + REQUIRE (storageSize); + Key typeKey = trackKey (family_, storageSize); + + if (nontrivial(instanceFunc)) + { + typeKey = trackKey (typeKey, instanceFunc); + } + + if (nontrivial(specifics)) + { + typeKey = trackKey (typeKey, specifics); + } + + return typeKey; } + /** create a sub-type, using a different type/handler functor */ Key - key (HashVal parentKey, TypeHandler instanceFunc) + key (HashVal parentKey, TypeHandler const& instanceFunc) { - UNIMPLEMENTED ("create sub-type key"); + Key parentEntry (get (parentKey)); + return trackKey (parentEntry, instanceFunc); } + /** create a sub-type, using a different private-ID (implementation defined) */ Key key (HashVal parentKey, LocalKey specifics) { - UNIMPLEMENTED ("create sub-type key"); + Key parentEntry (get (parentKey)); + return trackKey (parentEntry, specifics); } Key @@ -466,6 +529,24 @@ namespace engine { Entry& markLocked (HashVal parentKey, const void* buffer); void release (HashVal key); + private: + + template<typename PAR, typename DEF> + Key + trackKey (PAR parent, DEF specialisation) + { + Key newKey (parent,specialisation); + maybeStore (newKey); + return newKey; + } + + void + maybeStore (Key const& key) + { + if (isKnown (key)) return; + UNIMPLEMENTED ("registry for type keys"); + } + }; diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp index 3e4659a0d..352796413 100644 --- a/tests/components/proc/engine/buffer-metadata-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -83,7 +83,7 @@ namespace test { { /** common Metadata table to be tested */ scoped_ptr<Metadata> meta_; - + virtual void run (Arg) { @@ -110,6 +110,7 @@ namespace test { void verifyBasicProperties() { + // retrieve some type keys Metadata::Key key = meta_->key(SIZE_A); CHECK (key); @@ -120,7 +121,8 @@ namespace test { CHECK (key == key1); CHECK (key != key2); - VERIFY_ERROR (INVALID, meta_->get(0)) + // access metadata entries + VERIFY_ERROR (INVALID, meta_->get(0)); VERIFY_ERROR (INVALID, meta_->get(JUST_SOMETHING)); CHECK ( & meta_->get(key)); CHECK ( & meta_->get(key1)); @@ -130,6 +132,7 @@ namespace test { CHECK ( isSameObject (meta_->get(key), meta_->get(key1))); CHECK (!isSameObject (meta_->get(key), meta_->get(key2))); + // entries retrieved this far are inactive (type only) entries Metadata::Entry& m1 = meta_->get(key); CHECK (NIL == m1.state()); CHECK (!meta_->isLocked(key)); @@ -137,12 +140,14 @@ namespace test { VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED) ); VERIFY_ERROR (LIFECYCLE, m1.mark(LOCKED) ); + // now create an active (buffer) entry Metadata::Entry& m2 = meta_->markLocked (key, SOME_POINTER); CHECK (!isSameObject (m1,m2)); CHECK (NIL == m1.state()); CHECK (LOCKED == m2.state()); - CHECK (SOME_POINTER == m2.access()); + CHECK (SOME_POINTER == m2.access()); // buffer pointer associated + // entries are unique and identifiable HashVal keyX = meta_->key(key1, SOME_POINTER); CHECK (meta_->isLocked(keyX)); CHECK (keyX != key1); @@ -170,6 +175,7 @@ namespace test { VERIFY_ERROR (LIFECYCLE, m2.mark(LOCKED)); CHECK ( isSameObject (m2, meta_->get(keyX))); // still accessible + // release buffer... meta_->release(keyX); CHECK (!meta_->isLocked(keyX)); CHECK (!meta_->isKnown(keyX)); From 991eec81853d839d29e3ec45c5414d3e5a5547ac Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 17 Oct 2011 02:00:48 +0200 Subject: [PATCH 208/496] draft test to simulate a complete buffer metadata usage cycle --- src/proc/engine/buffer-metadata.hpp | 16 ++-- src/proc/engine/buffhandle.hpp | 3 + .../proc/engine/buff-table-test.cpp | 8 +- .../proc/engine/buffer-metadata-test.cpp | 84 +++++++++++++++++++ .../engine/buffer-provider-protocol-test.cpp | 2 +- tests/components/proc/engine/testframe.hpp | 4 +- 6 files changed, 101 insertions(+), 16 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index d6a4cd477..6addaf574 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -479,22 +479,20 @@ namespace engine { /** create a sub-type, using a different type/handler functor */ Key - key (HashVal parentKey, TypeHandler const& instanceFunc) + key (Key const& parentKey, TypeHandler const& instanceFunc) { - Key parentEntry (get (parentKey)); - return trackKey (parentEntry, instanceFunc); + return trackKey (parentKey, instanceFunc); } /** create a sub-type, using a different private-ID (implementation defined) */ Key - key (HashVal parentKey, LocalKey specifics) + key (Key const& parentKey, LocalKey specifics) { - Key parentEntry (get (parentKey)); - return trackKey (parentEntry, specifics); + return trackKey (parentKey, specifics); } Key - key (HashVal parentKey, const void* concreteBuffer) + key (Key const& parentKey, const void* concreteBuffer) { UNIMPLEMENTED ("create sub-object key for concrete buffer"); } @@ -526,7 +524,7 @@ namespace engine { /* == memory management == */ - Entry& markLocked (HashVal parentKey, const void* buffer); + Entry& markLocked (Key const& parentKey, const void* buffer); void release (HashVal key); private: @@ -558,7 +556,7 @@ namespace engine { /** */ inline Metadata::Entry& - Metadata::markLocked (HashVal parentKey, const void* buffer) + Metadata::markLocked (Key const& parentKey, const void* buffer) { UNIMPLEMENTED ("transition to locked state"); if (!buffer) diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index 8401bdb17..df7018e05 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -61,6 +61,9 @@ namespace engine { * For each kind of buffer, there is somewhere a BufferProvider responsible for the * actual storage management. This provider may "lock" a buffer for actual use, * returning a BuffHandle. + * @note this descriptor and especially the #subClassification_ is really owned + * by the BufferProvider, which may use (and even change) the opaque contents + * to organise the internal buffer management. * * @todo try to move that definition into buffer-provider.hpp ////////////////////////////////////TICKET #249 */ diff --git a/tests/components/proc/engine/buff-table-test.cpp b/tests/components/proc/engine/buff-table-test.cpp index 06ff34e54..76f9c2f2d 100644 --- a/tests/components/proc/engine/buff-table-test.cpp +++ b/tests/components/proc/engine/buff-table-test.cpp @@ -81,7 +81,7 @@ namespace test { -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833 +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #852 void* detect_start_level (BuffTableStorage& sto) { @@ -117,7 +117,7 @@ namespace test { && (not_within(b.inHandle, b.outBuff, &b.inBuff[num.nrO])) ; } -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833 +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #852 } // (End) internal defs @@ -144,9 +144,9 @@ namespace test { // allocate storage block to be used chunk wise pStorage.reset (new BuffTableStorage (TABLE_SIZ)); -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833 +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #852 invocation (0, detect_start_level(*pStorage)); -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833 +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #852 pStorage.reset(0); // dtor throws assertion error if corrupted diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp index 352796413..f76c4fb44 100644 --- a/tests/components/proc/engine/buffer-metadata-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -30,6 +30,7 @@ //#include "proc/engine/testframe.hpp" //#include "proc/engine/diagnostic-buffer-provider.hpp" #include "proc/engine/buffer-metadata.hpp" +#include "proc/engine/testframe.hpp" //#include "proc/engine/buffhandle.hpp" //#include "proc/engine/bufftable.hpp" @@ -184,9 +185,92 @@ namespace test { } + /** @test simulate a standard buffer provider usage cycle + * @note to get the big picture, please refer to + * BufferProviderProtocol_test#verifyStandardCase() + * This testcase here performs precisely the metadata related + * operations necessary to carry out the standard case outlined + * in that more high level test. + */ void verifyStandardCase() { + // to build a descriptor for a buffer holding a TestFrame + TypeHandler attachTestFrame = TypeHandler::create<TestFrame>(); + Metadata::Key bufferType1 = meta_->key(sizeof(TestFrame), attachTestFrame); + + // to build a descriptor for a raw buffer of size SIZE_B + Metadata::Key rawBuffType = meta_->key(SIZE_B); + + // to announce using a number of buffers of this type + LocalKey transaction1(1); + LocalKey transaction2(2); + bufferType1 = meta_->key(bufferType1, transaction1); + rawBuffType = meta_->key(rawBuffType, transaction2); + // these type keys are now handed over to the client, + // embedded into a BufferDescriptor... + + // later, when it comes to actually *locking* those buffers... + typedef char RawBuffer[SIZE_B]; + + // do the necessary memory allocations behind the scenes + TestFrame* frames = new TestFrame[3]; + RawBuffer* rawbuf = new RawBuffer[2]; // coding explicit allocations here for sake of clarity; + // a real-world BufferProvider would use some kind of allocator + + // track individual buffers by metadata entries + Metadata::Entry f0 = meta_->markLocked(bufferType1, &frames[0]); + Metadata::Entry f1 = meta_->markLocked(bufferType1, &frames[1]); + Metadata::Entry f2 = meta_->markLocked(bufferType1, &frames[2]); + + Metadata::Entry r0 = meta_->markLocked(bufferType1, &rawbuf[0]); + Metadata::Entry r1 = meta_->markLocked(bufferType1, &rawbuf[1]); + + CHECK (LOCKED == f0.state()); + CHECK (LOCKED == f1.state()); + CHECK (LOCKED == f2.state()); + CHECK (LOCKED == r0.state()); + CHECK (LOCKED == r1.state()); + + // for the TestFrame buffers, additionally we'd have to create/attach an object + attachTestFrame.createAttached (frames+0); ////////////////////////////////////////TODO: shouldn't this happen automatically?? + attachTestFrame.createAttached (frames+1); + attachTestFrame.createAttached (frames+2); + + CHECK (f0.access() == frames+0); + CHECK (f1.access() == frames+1); + CHECK (f2.access() == frames+2); + CHECK (r0.access() == rawbuf+0); + CHECK (r1.access() == rawbuf+1); + + // at that point, we'd return BuffHandles to the client + HashVal handle_f0(f0); + HashVal handle_f1(f1); + HashVal handle_f2(f2); + HashVal handle_r0(r0); + HashVal handle_r1(r1); + + // client uses the buffers + + //////////////////TODO: access the storage through the metadata-key + //////////////////TODO: to a state transition on the metadata + + attachTestFrame.destroyAttached (frames+0); ////////////////////////////////////////TODO: shouldn't this happen automatically?? + attachTestFrame.destroyAttached (frames+1); + attachTestFrame.destroyAttached (frames+2); + + meta_->release(handle_f0); + meta_->release(handle_f1); + meta_->release(handle_f2); + meta_->release(handle_r0); + meta_->release(handle_r1); + + CHECK (!meta_->isLocked(handle_f0)); + CHECK (!meta_->isLocked(handle_f1)); + CHECK (!meta_->isLocked(handle_f2)); + CHECK (!meta_->isLocked(handle_r0)); + CHECK (!meta_->isLocked(handle_r1)); + #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834 #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834 } diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp index 553f93190..d2e5261dd 100644 --- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp +++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp @@ -113,7 +113,7 @@ namespace test { // will be preconfigured, depending on the usage context BufferProvider& provider = DiagnosticBufferProvider::build(); - BufferDescriptor desc1 = provider.getDescriptor<TestFrame>(); + BufferDescriptor desc1 = provider.getDescriptor<TestFrame>(); // note: implies also sizeof(TestFrame) BufferDescriptor desc2 = provider.getDescriptorFor(TEST_SIZE); #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829 CHECK (desc1.verifyValidity()); diff --git a/tests/components/proc/engine/testframe.hpp b/tests/components/proc/engine/testframe.hpp index 5678bf147..ba71d6646 100644 --- a/tests/components/proc/engine/testframe.hpp +++ b/tests/components/proc/engine/testframe.hpp @@ -77,13 +77,13 @@ namespace test { - TestFrame + inline TestFrame testData (uint seqNr) { UNIMPLEMENTED ("build, memorise and expose test data frames on demand"); } - TestFrame + inline TestFrame testData (uint chanNr, uint seqNr) { UNIMPLEMENTED ("build, memorise and expose test data frames on demand (multi-channel)"); From ca0ae9c120ec8a343f6cc6e3bb4fd6cf8c86352b Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 19 Oct 2011 02:47:11 +0200 Subject: [PATCH 209/496] stubbed relevant parts of BufferProvider and OutputSlot --- src/proc/engine/buffer-provider.hpp | 2 + src/proc/engine/buffhandle.hpp | 2 +- src/proc/engine/bufftable.hpp | 186 ++++++++---------- .../engine/diagnostic-buffer-provider.cpp | 15 ++ .../engine/diagnostic-buffer-provider.hpp | 10 +- src/proc/play/output-slot.hpp | 1 + .../engine/buffer-provider-protocol-test.cpp | 19 +- .../proc/play/diagnostic-output-slot.hpp | 62 ++++++ .../proc/play/output-slot-protocol-test.cpp | 19 +- 9 files changed, 195 insertions(+), 121 deletions(-) rename {src => tests/components}/proc/play/diagnostic-output-slot.hpp (55%) diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 6f025a1c8..ca6382d66 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -80,6 +80,8 @@ namespace engine { virtual ~BufferProvider(); ///< this is an interface + virtual uint announce (uint count, BufferDescriptor const&) =0; + virtual BuffHandle lockBufferFor (BufferDescriptor const&) =0; virtual void releaseBuffer (BuffHandle const&) =0; diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index df7018e05..14845a849 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -185,7 +185,7 @@ namespace engine { } - /** convenience shortcuts: access the buffer contents in a typesafe fashion. + /** convenience shortcut: access the buffer contents in a typesafe fashion. * This is equivalent to a plain dereferentiation with additional metadata check * @throw error::Logic in case of type mismatch \c LUMIERA_ERROR_WRONG_TYPE */ diff --git a/src/proc/engine/bufftable.hpp b/src/proc/engine/bufftable.hpp index ac5773e24..00738cd64 100644 --- a/src/proc/engine/bufftable.hpp +++ b/src/proc/engine/bufftable.hpp @@ -28,6 +28,7 @@ #include "lib/error.hpp" #include "proc/engine/buffhandle.hpp" #include "proc/engine/procnode.hpp" +#include "lib/iter-adapter.hpp" #include <boost/noncopyable.hpp> #include <vector> @@ -57,122 +58,99 @@ namespace engine { { typedef BuffHandle * PHa; typedef BuffHandle::PBuff * PBu; - typedef pair<PHa const,PBu const> Chunk; - PHa outHandle; - PHa inHandle; - PBu outBuff; - PBu inBuff; - }; - - - 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 + struct StorageChunk + { }; + + template<uint count> + struct Storage { - BuffHaXXXX() : BuffHandle(just_satisfy_the_compiler()) { /* wont work ever */ } - static BufferDescriptor const& - just_satisfy_the_compiler() { } + enum{size = count * sizeof(StorageChunk)}; }; - - ////////////////////////////////////TICKET #825 should be backed by mpool and integrated with node invocation - vector<BuffHaXXXX> hTab_; - vector<BuffHandle::PBuff> pTab_; - size_t level_; - public: - BuffTableStorage (const size_t maxSiz) - : hTab_(maxSiz), - pTab_(maxSiz), - level_(0) - { } - - ~BuffTableStorage() { ASSERT (0==level_, "buffer management logic broken."); } - - protected: - - friend class BuffTableChunk; - - /** allocate the given number of slots - * starting at current level to be used - * by the newly created BuffTableChunk - */ - BuffTable::Chunk - claim (uint slots) + class Builder { - 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]); - } + public: + Builder& announce (uint count, BufferDescriptor const& type); + BuffTable& build(); + }; - void - release (uint slots) - { - ASSERT (slots <= level_); - REQUIRE (level_ <= hTab_.size()); - REQUIRE (level_ <= pTab_.size()); - - level_ -= slots; - } + static Builder& prepare (const size_t STORAGE_SIZE, void* storage); - bool - level_check (BuffTable::Chunk& prev_level) - { - return prev_level.first == &hTab_[level_] - && prev_level.second == &pTab_[level_]; - } + void lockBuffers(); + void releaseBuffers(); + + typedef vector<BuffHandle> BuffHandleTable; + typedef lib::RangeIter<BuffHandleTable::iterator> iterator; + + iterator buffers(); + iterator inBuffers(); + iterator outBuffers(); }; - /** - * 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."); - } - }; + /* === Implementation === */ + + inline BuffTable::Builder& + BuffTable::prepare (const size_t STORAGE_SIZE, void* storage) + { + UNIMPLEMENTED ("expose a builder object for outfitting a buffer pointer table"); + } + + + inline BuffTable::Builder& + BuffTable::Builder::announce (uint count, BufferDescriptor const& type) + { + UNIMPLEMENTED ("accept announcement of additional buffer table entries required"); + } + + + inline BuffTable& + BuffTable::Builder::build() + { + UNIMPLEMENTED ("finally drop off the newly configured buffer pointer table"); + } + + + inline void + BuffTable::lockBuffers() + { + UNIMPLEMENTED ("convenience shortcut: lock all preconfigured buffers within this table through the underlying buffer provider"); + } + + + inline void + BuffTable::releaseBuffers() + { + UNIMPLEMENTED ("convenience shortcut: release all the buffers managed through this buffer table, by forwarding to the underlying buffer provider"); + } + + + + inline BuffTable::iterator + BuffTable::buffers() + { + UNIMPLEMENTED ("expose an iterator to yield all prepared buffers within this buffer table"); + } + + + inline BuffTable::iterator + BuffTable::inBuffers() + { + UNIMPLEMENTED ("expose an iterator to access all the input buffer slots of this buffer table"); + } + + + inline BuffTable::iterator + BuffTable::outBuffers() + { + UNIMPLEMENTED ("expose an iterator to access all the output buffer slots of this buffer table"); + } + + + diff --git a/src/proc/engine/diagnostic-buffer-provider.cpp b/src/proc/engine/diagnostic-buffer-provider.cpp index 1711e1a6b..8a5efdfbe 100644 --- a/src/proc/engine/diagnostic-buffer-provider.cpp +++ b/src/proc/engine/diagnostic-buffer-provider.cpp @@ -97,6 +97,14 @@ namespace engine { : public BufferProvider , public ScopedPtrVect<Block> { + + virtual uint + announce (uint count, BufferDescriptor const& type) + { + UNIMPLEMENTED ("pre-register storage for buffers of a specific kind"); + } + + virtual BuffHandle lockBufferFor (BufferDescriptor const& descriptor) { @@ -141,6 +149,13 @@ namespace engine { }; + + DiagnosticBufferProvider::DiagnosticBufferProvider() + : pImpl_() //////////TODO create PImpl here + { } + + DiagnosticBufferProvider::~DiagnosticBufferProvider() { } + BufferProvider& DiagnosticBufferProvider::build() diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp index f79ca49d2..0f80fea6e 100644 --- a/src/proc/engine/diagnostic-buffer-provider.hpp +++ b/src/proc/engine/diagnostic-buffer-provider.hpp @@ -68,6 +68,11 @@ namespace engine { bool isCurrent (BufferProvider const&); + DiagnosticBufferProvider(); + ~DiagnosticBufferProvider(); + + friend class lib::singleton::StaticCreate<DiagnosticBufferProvider>; + public: /** build a new Diagnostic Buffer Provider instance, * discard the existing one. Use the static query API @@ -88,8 +93,9 @@ namespace engine { /* === diagnostic API === */ bool buffer_was_used (uint bufferID) const; - bool buffer_was_closed (uint bufferID) const; - void* accessMemory (uint bufferID) const; + bool buffer_was_closed (uint bufferID) const; + void* accessMemory (uint bufferID) const; + bool all_buffers_released() const; template<typename BU> diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index b3188724e..ece7c4627 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -75,6 +75,7 @@ namespace play { { public: + BuffHandle lockBufferFor(FrameNr); void emit(FrameNr); }; diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp index d2e5261dd..fdfba1e67 100644 --- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp +++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp @@ -54,6 +54,13 @@ namespace test { const uint TEST_SIZE = 1024*1024; const uint TEST_ELMS = 20; + + void + do_some_calculations (BuffHandle const& buffer) + { + UNIMPLEMENTED ("some verifiable test/dummy buffer accessing operations"); + } + } @@ -108,6 +115,7 @@ namespace test { void verifyStandardCase() { +#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 @@ -115,12 +123,11 @@ namespace test { BufferDescriptor desc1 = provider.getDescriptor<TestFrame>(); // note: implies also sizeof(TestFrame) BufferDescriptor desc2 = provider.getDescriptorFor(TEST_SIZE); -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829 CHECK (desc1.verifyValidity()); CHECK (desc2.verifyValidity()); - size_t num1 = provider.announce(TEST_ELMS, desc1); - size_t num2 = provider.announce(TEST_ELMS, desc2); + uint num1 = provider.announce(TEST_ELMS, desc1); + uint num2 = provider.announce(TEST_ELMS, desc2); CHECK (num1 == TEST_ELMS); CHECK (0 < num2 && num2 <=TEST_ELMS); @@ -128,15 +135,15 @@ namespace test { char storage[STORAGE_SIZE]; BuffTable& tab = BuffTable::prepare(STORAGE_SIZE, storage) - .prepare(num1, desc1) - .prepare(num2, desc2) + .announce(num1, desc1) + .announce(num2, desc2) .build(); tab.lockBuffers(); for_each (tab.buffers(), do_some_calculations); tab.releaseBuffers(); - DiagnosticBufferProvider checker = DiagnosticBufferProvider::access(provider); + DiagnosticBufferProvider& checker = DiagnosticBufferProvider::access(provider); CHECK (checker.all_buffers_released()); #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829 } diff --git a/src/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp similarity index 55% rename from src/proc/play/diagnostic-output-slot.hpp rename to tests/components/proc/play/diagnostic-output-slot.hpp index b699e0ae8..18a13c6b3 100644 --- a/src/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -33,6 +33,8 @@ #include "lib/error.hpp" #include "proc/play/output-slot.hpp" +#include "lib/iter-source.hpp" ////////////TODO really going down that path...? +#include "proc/engine/testframe.hpp" //#include "lib/sync.hpp" //#include <boost/noncopyable.hpp> @@ -46,6 +48,7 @@ namespace proc { namespace play { //using std::string; + using ::engine::test::TestFrame; //using std::vector; //using std::tr1::shared_ptr; @@ -71,6 +74,65 @@ namespace play { UNIMPLEMENTED ("Diagnostic Output Slot instance"); } + static DiagnosticOutputSlot& + access (OutputSlot& to_investigate) + { + UNIMPLEMENTED ("access the diagnostics data for the given OutputSlot instance"); + } + + + /* === diagnostics API === */ + + /** + * diagnostic facility to verify + * test data frames written to this + * Test/Dummy "output" + */ + struct OutputStreamProtocol + : lib::IterSource<TestFrame> + { + /////////////TODO: implement the extension points required to drive an IterSource + }; + + typedef OutputStreamProtocol::iterator OutFrames; + + + OutFrames + getChannel (uint channel) + { + UNIMPLEMENTED ("access output stream tracing entry"); + } + + + bool + buffer_was_used (uint channel, FrameNr frame) + { + UNIMPLEMENTED ("determine if the denoted buffer was indeed used"); + } + + + bool + buffer_unused (uint channel, FrameNr frame) + { + UNIMPLEMENTED ("determine if the specified buffer was never touched/locked for use"); + } + + + bool + buffer_was_closed (uint channel, FrameNr frame) + { + UNIMPLEMENTED ("determine if the specified buffer was indeed closed properly"); + } + + + bool + emitted (uint channel, FrameNr frame) + { + UNIMPLEMENTED ("determine if the specivied buffer was indeed handed over for emitting output"); + } + + + private: }; diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp index 124d49372..ae3ec2ae5 100644 --- a/tests/components/proc/play/output-slot-protocol-test.cpp +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -25,6 +25,7 @@ //#include "lib/util.hpp" #include "proc/play/diagnostic-output-slot.hpp" #include "proc/engine/buffhandle.hpp" +#include "proc/engine/testframe.hpp" //#include <boost/format.hpp> //#include <iostream> @@ -41,6 +42,8 @@ namespace test { // using lib::AllocationCluster; // using mobject::session::PEffect; using ::engine::BuffHandle; + using ::engine::test::testData; + using ::engine::test::TestFrame; namespace { // Test fixture @@ -77,7 +80,7 @@ namespace test { // Client claims the OutputSlot // and opens it for exclusive use. - Allocation alloc = oSlot.allocate(); + OutputSlot::Allocation alloc = oSlot.allocate(); // Now the client is able to prepare // "calculation streams" for the individual @@ -95,13 +98,13 @@ namespace test { buff10.create<TestFrame>(); // rendering process calculates content.... - buff00.access<TestFrame>() = testData[0,0]; + buff00.accessAs<TestFrame>() = testData(0,0); // while further frames might be processed in parallel BuffHandle buff11 = sink2.lockBufferFor (++frameNr); buff11.create<TestFrame>(); - buff11.access<TestFrame>() = testData[1,1]; - buff10.access<TestFrame>() = testData[1,0]; + buff11.accessAs<TestFrame>() = testData(1,1); + buff10.accessAs<TestFrame>() = testData(1,0); // Now it's time to emit the output sink2.emit (frameNr-1); @@ -110,7 +113,7 @@ namespace test { // that's all for the client // Verify sane operation.... - DiagnosticOutputSlot checker = DiagnosticOutputSlot::access(oSlot); + 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)); @@ -129,12 +132,12 @@ namespace test { DiagnosticOutputSlot::OutFrames stream1 = checker.getChannel(1); CHECK ( stream0); - CHECK (*stream0++ == testData[0,0]); + CHECK (*stream0++ == testData(0,0)); CHECK (!stream0); CHECK ( stream1); - CHECK (*stream1++ == testData[1,0]); - CHECK (*stream1++ == testData[1,1]); + CHECK (*stream1++ == testData(1,0)); + CHECK (*stream1++ == testData(1,1)); CHECK (!stream1); #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #819 } From f1a448947072f26f2df7925a73ff5322de295e81 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 21 Oct 2011 00:58:15 +0200 Subject: [PATCH 210/496] Fix minor problems after merge --- src/gui/widgets/timeline/timeline-state.cpp | 4 +++- tests/components/proc/control/command-argument-test.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gui/widgets/timeline/timeline-state.cpp b/src/gui/widgets/timeline/timeline-state.cpp index 06e67dfa0..62f67aa08 100644 --- a/src/gui/widgets/timeline/timeline-state.cpp +++ b/src/gui/widgets/timeline/timeline-state.cpp @@ -51,7 +51,9 @@ TimelineState::TimelineState (boost::shared_ptr<model::Sequence> source_sequence REQUIRE(sequence); ////////////////////////////////////////////////////////////TICKET #798: how to handle GUI default state - viewWindow.set_time_scale(21000000); + const int64_t DEFAULT_TIMELINE_SCALE =21000000; + + viewWindow.set_time_scale(DEFAULT_TIMELINE_SCALE); setSelection (Mutation::changeTime (Time(FSecs(2)))); setSelection (Mutation::changeDuration(Duration(FSecs(2)))); diff --git a/tests/components/proc/control/command-argument-test.cpp b/tests/components/proc/control/command-argument-test.cpp index 094f63d2a..edd36dc1c 100644 --- a/tests/components/proc/control/command-argument-test.cpp +++ b/tests/components/proc/control/command-argument-test.cpp @@ -244,7 +244,7 @@ namespace test { arg3->storeTuple (tuple::make (rand() % 10, TimeVar(randTime()))); arg4->storeTuple (tuple::make (rand() % 10, TimeVar(randTime()))); - arg5->storeTuple (tuple::make (TTime (randTime()), Tstr("glorious"), rand() % 25)); + arg5->storeTuple (tuple::make (TTime (randTime()), Tstr("glorious"), 10 + rand() % 90)); CHECK (!arg5->canUndo()); From 65b309e5cb8e1d643b584d04ff42af05d8a932d5 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 21 Oct 2011 01:09:12 +0200 Subject: [PATCH 211/496] Testsuite: automatically seed the random number generator --- src/lib/test/suite.cpp | 5 +++++ tests/components/proc/engine/buff-table-test.cpp | 2 -- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/test/suite.cpp b/src/lib/test/suite.cpp index 6ad3ea887..ea065655f 100644 --- a/src/lib/test/suite.cpp +++ b/src/lib/test/suite.cpp @@ -37,6 +37,8 @@ #include <memory> #include <vector> #include <map> +#include <cstdlib> +#include <ctime> namespace test { @@ -141,6 +143,9 @@ namespace test { REQUIRE( !isnil(groupID) ); TRACE(test, "Test-Suite( groupID=%s )\n", groupID.c_str () ); + // Seed random number generator + std::srand (std::time (NULL)); + if (!testcases.getGroup(groupID)) throw lumiera::error::Invalid ("empty testsuite"); } diff --git a/tests/components/proc/engine/buff-table-test.cpp b/tests/components/proc/engine/buff-table-test.cpp index 76f9c2f2d..73482d48d 100644 --- a/tests/components/proc/engine/buff-table-test.cpp +++ b/tests/components/proc/engine/buff-table-test.cpp @@ -30,7 +30,6 @@ #include <boost/scoped_ptr.hpp> #include <iostream> -#include <ctime> using test::Test; using std::cout; @@ -139,7 +138,6 @@ namespace test { virtual void run(Arg) { counter = 0; - std::srand (std::time (NULL)); // allocate storage block to be used chunk wise pStorage.reset (new BuffTableStorage (TABLE_SIZ)); From bb2ca862accb96623aaada9f5d32ecd28372a69f Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 21 Oct 2011 01:53:05 +0200 Subject: [PATCH 212/496] reference distribution for building is now Debian/Squeeze --- doc/technical/infra/debianDepot.txt | 12 ++++++------ doc/user/tutorials/DebianBuilding.txt | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/technical/infra/debianDepot.txt b/doc/technical/infra/debianDepot.txt index 6c8f95475..8573e3803 100644 --- a/doc/technical/infra/debianDepot.txt +++ b/doc/technical/infra/debianDepot.txt @@ -76,15 +76,15 @@ everyday usage ~~~~~~~~~~~~~~ import a package:: - +reprepro -V -C experimental include lenny lumiera_0.pre.01-1+maverick_i386.changes+ + +reprepro -V -C experimental include squeeze lumiera_0.pre.01-1+squeeze_i386.changes+ + this adds the given binary lumiera package, together with all sources and the original -tarball to the 'lenny' repository, into the 'experimental' section +tarball to the 'squeeze' repository, into the 'experimental' section dump out an entire repository:: - +reprepro -V export lenny+ + +reprepro -V export squeeze+ + -this will __re__generate all of the indices, signatures and metadata of the 'lenny' repository +this will __re__generate all of the indices, signatures and metadata of the 'squeeze' repository Configuration @@ -98,12 +98,12 @@ I choose this layout because of the marginal relevance of this depot management; want to create _yet another not so useful Git repository..._) * primary link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=blob;f=conf/distributions;hb=refs/heads/depot[configuration] - * Logfile of imports: link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=blob;f=log/lenny.log;hb=refs/heads/depot[for Lenny] + * Logfile of imports: link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=blob;f=log/squeeze.log;hb=refs/heads/depot[for Debian/Squeeze] [NOTE] .some special details to note in our setup ======================================================================================================================= -- each block in the 'distributions' file defines a repository for a ``distribution'' (e.g. Lenny, Lucid, Maverick). +- each block in the 'distributions' file defines a repository for a ``distribution'' (e.g. Squeeze, Lucid, Maverick). Within such a repo, there are sections named 'Components'. - The _override_ files mentioned in the configuration allow to overwrite / replace arbitrary fields in the metadata of all packages added to that distribution. diff --git a/doc/user/tutorials/DebianBuilding.txt b/doc/user/tutorials/DebianBuilding.txt index 5e33957b6..8c249b667 100644 --- a/doc/user/tutorials/DebianBuilding.txt +++ b/doc/user/tutorials/DebianBuilding.txt @@ -35,7 +35,7 @@ should _not_ be done as root. . add a suitable source line to your *Apt configuration* ('/etc/apt/sources.lst') + ---- -deb-src http://lumiera.org/debian/ lenny experimental +deb-src http://lumiera.org/debian/ squeeze experimental ---- . get all the *build dependencies* + From d48dc3b025d3c0064aa2f6650609e93a5b3bfcdf Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 21 Oct 2011 02:41:20 +0200 Subject: [PATCH 213/496] fix glitch in test definition uncovered since rand() is now properly seeded prior to each testsuite run --- src/proc/mobject/test-dummy-mobject.hpp | 2 +- tests/43session.tests | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/proc/mobject/test-dummy-mobject.hpp b/src/proc/mobject/test-dummy-mobject.hpp index 14542b629..325244691 100644 --- a/src/proc/mobject/test-dummy-mobject.hpp +++ b/src/proc/mobject/test-dummy-mobject.hpp @@ -90,7 +90,7 @@ namespace test { string display(Symbol name) const { - static format fmt("%s(ID=%4d)"); + static format fmt("%s(ID=%03d)"); return boost::str(fmt % name % this->id_); } }; diff --git a/tests/43session.tests b/tests/43session.tests index dd86bcc31..0ecfa5964 100644 --- a/tests/43session.tests +++ b/tests/43session.tests @@ -96,9 +96,9 @@ out: Placement.+mobject.+test.+TestSubMO1.+. use-cnt=1 .* out: Placement.+mobject.+test.+TestSubMO2.+. use-cnt=1 .* out: Placement.+mobject.+test.+TestSubMO21.+ use-cnt=2 .* out: Placement.+mobject.+session.+Clip.+.... use-cnt=2 .* -out: TestSubMO1\(ID= [0-9]{3}\) -out: TestSubMO2\(ID= [0-9]{3}\) -out: TestSubMO21\(ID= [0-9]{3}\) +out: TestSubMO1\(ID=[0-9]{3}\) +out: TestSubMO2\(ID=[0-9]{3}\) +out: TestSubMO21\(ID=[0-9]{3}\) out: specialAPI() out: pID\(\w{6,16}\) END From 9a1987ba241c65fb3e5e90c35aae1da5122674ad Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Fri, 21 Oct 2011 02:56:10 -0500 Subject: [PATCH 214/496] Ticket #796: Switch GUI from boost::shared_ptr to std::tr1::shared_ptr. ParentTrack and TimelineLayoutHelper need to be fixed --- src/gui/model/clip-track.cpp | 6 +-- src/gui/model/clip-track.hpp | 4 +- src/gui/model/parent-track.cpp | 27 ++++++++----- src/gui/model/parent-track.hpp | 24 +++++------ src/gui/model/project.cpp | 4 +- src/gui/model/project.hpp | 4 +- src/gui/model/sequence.cpp | 2 +- src/gui/model/track.cpp | 8 ++-- src/gui/model/track.hpp | 8 ++-- src/gui/panels/timeline-panel.cpp | 4 +- src/gui/panels/timeline-panel.hpp | 16 +++----- src/gui/widgets/timeline-widget.cpp | 19 ++++----- src/gui/widgets/timeline-widget.hpp | 40 +++++++++---------- .../widgets/timeline/timeline-arrow-tool.cpp | 14 +++---- .../widgets/timeline/timeline-arrow-tool.hpp | 2 +- src/gui/widgets/timeline/timeline-body.cpp | 6 +-- src/gui/widgets/timeline/timeline-body.hpp | 6 +-- .../widgets/timeline/timeline-clip-track.cpp | 14 +++---- .../widgets/timeline/timeline-clip-track.hpp | 10 ++--- src/gui/widgets/timeline/timeline-clip.cpp | 6 +-- src/gui/widgets/timeline/timeline-clip.hpp | 6 +-- src/gui/widgets/timeline/timeline-entity.cpp | 2 +- src/gui/widgets/timeline/timeline-entity.hpp | 7 ++-- .../widgets/timeline/timeline-group-track.cpp | 2 +- .../widgets/timeline/timeline-group-track.hpp | 2 +- .../timeline/timeline-header-container.cpp | 6 +-- .../timeline/timeline-header-container.hpp | 10 ++--- .../timeline/timeline-header-widget.cpp | 3 +- .../widgets/timeline/timeline-ibeam-tool.cpp | 2 +- .../timeline/timeline-layout-helper.cpp | 16 ++++---- .../timeline/timeline-layout-helper.hpp | 22 +++++----- src/gui/widgets/timeline/timeline-ruler.cpp | 2 +- src/gui/widgets/timeline/timeline-ruler.hpp | 4 +- src/gui/widgets/timeline/timeline-state.cpp | 6 +-- src/gui/widgets/timeline/timeline-state.hpp | 6 +-- src/gui/widgets/timeline/timeline-tool.cpp | 2 +- src/gui/widgets/timeline/timeline-tool.hpp | 2 +- src/gui/widgets/timeline/timeline-track.cpp | 6 +-- src/gui/widgets/timeline/timeline-track.hpp | 8 ++-- .../widgets/timeline/timeline-zoom-scale.cpp | 6 +-- .../widgets/timeline/timeline-zoom-scale.hpp | 10 ++--- src/gui/window-manager.cpp | 3 +- src/gui/window-manager.hpp | 5 +-- 43 files changed, 179 insertions(+), 183 deletions(-) diff --git a/src/gui/model/clip-track.cpp b/src/gui/model/clip-track.cpp index 95ddd8ac0..827aec0b9 100644 --- a/src/gui/model/clip-track.cpp +++ b/src/gui/model/clip-track.cpp @@ -24,7 +24,7 @@ #include "clip.hpp" -#include <boost/shared_ptr.hpp> +using namespace std::tr1; namespace gui { namespace model { @@ -33,7 +33,7 @@ namespace model { { // TEST CODE: add a clip to the track - boost::shared_ptr<model::Clip> modelClip(new model::Clip()); + shared_ptr<model::Clip> modelClip(new model::Clip()); modelClip->setName("Clip Name"); clips.push_back(modelClip); @@ -50,7 +50,7 @@ namespace model { return os.str(); } - lumiera::observable_list< boost::shared_ptr<Clip> >& + lumiera::observable_list< shared_ptr<Clip> >& ClipTrack::getClipList() { return clips; diff --git a/src/gui/model/clip-track.hpp b/src/gui/model/clip-track.hpp index 288b41bf4..e115ed415 100644 --- a/src/gui/model/clip-track.hpp +++ b/src/gui/model/clip-track.hpp @@ -53,12 +53,12 @@ namespace model { /** * Gets the list of clips associated with this track. */ - lumiera::observable_list< boost::shared_ptr<Clip> >& + lumiera::observable_list< std::tr1::shared_ptr<Clip> >& getClipList(void); private: - lumiera::observable_list< boost::shared_ptr<Clip> > clips; + lumiera::observable_list< std::tr1::shared_ptr<Clip> > clips; }; diff --git a/src/gui/model/parent-track.cpp b/src/gui/model/parent-track.cpp index 874c7e9d5..d20a1adc3 100644 --- a/src/gui/model/parent-track.cpp +++ b/src/gui/model/parent-track.cpp @@ -24,6 +24,7 @@ #include <boost/foreach.hpp> using namespace boost; +using std::tr1::shared_ptr; namespace gui { namespace model { @@ -32,13 +33,13 @@ ParentTrack::ParentTrack() { } -const std::list< boost::shared_ptr<Track> >& +const std::list< std::tr1::shared_ptr<Track> >& ParentTrack::get_child_tracks() const { return tracks.get_list(); } -lumiera::observable_list< boost::shared_ptr<Track> >& +lumiera::observable_list< std::tr1::shared_ptr<Track> >& ParentTrack::get_child_track_list() { return tracks; @@ -52,11 +53,11 @@ ParentTrack::can_host_children() const bool ParentTrack::remove_descendant_track( - const boost::shared_ptr<Track> track) + const std::tr1::shared_ptr<Track> track) { REQUIRE(track); - boost::shared_ptr<ParentTrack> parent = + std::tr1::shared_ptr<ParentTrack> parent = find_descendant_track_parent(track); if(parent) { @@ -67,23 +68,29 @@ ParentTrack::remove_descendant_track( return false; } -boost::shared_ptr<ParentTrack> +std::tr1::shared_ptr<ParentTrack> ParentTrack::find_descendant_track_parent( - boost::shared_ptr<Track> child) + std::tr1::shared_ptr<Track> child) { REQUIRE(child != NULL); - BOOST_FOREACH(shared_ptr<Track> track, tracks) + BOOST_FOREACH(std::tr1::shared_ptr<Track> track, tracks) { if(track == child) return shared_from_this(); - - shared_ptr<ParentTrack> result = + + std::tr1::shared_ptr<ParentTrack> result = track->find_descendant_track_parent(child); if(result) return result; } - return shared_ptr<ParentTrack>(); + return std::tr1::shared_ptr<ParentTrack>(); +} + +std::tr1::shared_ptr<ParentTrack> +ParentTrack::shared_from_this() +{ + return std::tr1::shared_ptr<ParentTrack>(this); } } // namespace model diff --git a/src/gui/model/parent-track.hpp b/src/gui/model/parent-track.hpp index 78451972e..9a087e98f 100644 --- a/src/gui/model/parent-track.hpp +++ b/src/gui/model/parent-track.hpp @@ -31,10 +31,6 @@ #include "track.hpp" #include "lib/observable-list.hpp" -#include <boost/shared_ptr.hpp> -#include <boost/enable_shared_from_this.hpp> - - namespace gui { namespace model { @@ -42,9 +38,7 @@ namespace model { * ParentTrack is the abstract base class of all tracks that can parent * children. */ -class ParentTrack : - public Track, - public boost::enable_shared_from_this<ParentTrack> +class ParentTrack : public Track { protected: /** @@ -57,13 +51,13 @@ public: /** * Gets a read-only reference to the the list of child tracks. */ - const std::list< boost::shared_ptr<Track> >& + const std::list< std::tr1::shared_ptr<Track> >& get_child_tracks() const; /** * Gets read-write access to the list of child tracks. */ - lumiera::observable_list< boost::shared_ptr<Track> >& + lumiera::observable_list< std::tr1::shared_ptr<Track> >& get_child_track_list(); /** @@ -79,7 +73,7 @@ public: * @param The model track to try and remove. * @return Returns true if the track was successfully removed. */ - bool remove_descendant_track(const boost::shared_ptr<Track> track); + bool remove_descendant_track(const std::tr1::shared_ptr<Track> track); /** * A utility function that attempts to find the parent of a track by @@ -88,14 +82,18 @@ public: * @return Returns the parent track if one was found, or an empty * shared_ptr if none was found. */ - boost::shared_ptr<ParentTrack> - find_descendant_track_parent(boost::shared_ptr<Track> child); + std::tr1::shared_ptr<ParentTrack> + find_descendant_track_parent(std::tr1::shared_ptr<Track> child); + +private: + + std::tr1::shared_ptr<ParentTrack> shared_from_this(); protected: /** * The internal list of child tracks of this parent. */ - lumiera::observable_list< boost::shared_ptr<Track> > tracks; + lumiera::observable_list< std::tr1::shared_ptr<Track> > tracks; }; } // namespace model diff --git a/src/gui/model/project.cpp b/src/gui/model/project.cpp index 3b37020cd..7f8054b53 100644 --- a/src/gui/model/project.cpp +++ b/src/gui/model/project.cpp @@ -26,7 +26,7 @@ #include "sequence.hpp" using namespace std; -using namespace boost; +using namespace std::tr1; namespace gui { namespace model { @@ -43,7 +43,7 @@ Project::~Project() } -lumiera::observable_list< boost::shared_ptr<Sequence> >& +lumiera::observable_list< shared_ptr<Sequence> >& Project::get_sequences() { return sequences; diff --git a/src/gui/model/project.hpp b/src/gui/model/project.hpp index e6e6bbc9e..29e5ca2a6 100644 --- a/src/gui/model/project.hpp +++ b/src/gui/model/project.hpp @@ -40,14 +40,14 @@ public: ~Project(); - lumiera::observable_list< boost::shared_ptr<Sequence> >& + lumiera::observable_list< std::tr1::shared_ptr<Sequence> >& get_sequences(); void add_new_sequence(uString name); private: - lumiera::observable_list< boost::shared_ptr<Sequence> > sequences; + lumiera::observable_list< std::tr1::shared_ptr<Sequence> > sequences; }; } // namespace model diff --git a/src/gui/model/sequence.cpp b/src/gui/model/sequence.cpp index 318ee4a4f..107396515 100644 --- a/src/gui/model/sequence.cpp +++ b/src/gui/model/sequence.cpp @@ -27,7 +27,7 @@ #include "group-track.hpp" #include "clip-track.hpp" -using namespace boost; +using namespace std::tr1; namespace gui { namespace model { diff --git a/src/gui/model/track.cpp b/src/gui/model/track.cpp index b8588ed49..5e7b4dad7 100644 --- a/src/gui/model/track.cpp +++ b/src/gui/model/track.cpp @@ -24,7 +24,7 @@ #include "parent-track.hpp" #include <boost/foreach.hpp> -using namespace boost; +using namespace std::tr1; using namespace std; namespace gui { @@ -39,7 +39,7 @@ Track::Track() } -const std::list< boost::shared_ptr<Track> >& +const std::list< shared_ptr<Track> >& Track::get_child_tracks() const { return Track::NoChildren; @@ -96,9 +96,9 @@ Track::print_branch() return print_branch_recursive(0); } -boost::shared_ptr<ParentTrack> +shared_ptr<ParentTrack> Track::find_descendant_track_parent( - boost::shared_ptr<Track> /*child*/) + shared_ptr<Track> /*child*/) { return shared_ptr<ParentTrack>(); } diff --git a/src/gui/model/track.hpp b/src/gui/model/track.hpp index 7e6b9085a..46f046b99 100644 --- a/src/gui/model/track.hpp +++ b/src/gui/model/track.hpp @@ -57,7 +57,7 @@ public: /** * Gets the list of child tracks. */ - virtual const std::list< boost::shared_ptr<Track> >& + virtual const std::list< std::tr1::shared_ptr<Track> >& get_child_tracks () const; /** @@ -106,8 +106,8 @@ public: * @return Returns the parent track if one was found, or an empty * shared_ptr if none was found. */ - virtual boost::shared_ptr<ParentTrack> - find_descendant_track_parent (boost::shared_ptr<Track> child); + virtual std::tr1::shared_ptr<ParentTrack> + find_descendant_track_parent (std::tr1::shared_ptr<Track> child); /** * A signal which fires when the enabled status changes. @@ -154,7 +154,7 @@ protected: * An object used internally as a return value for when there's no * children. */ - static const std::list< boost::shared_ptr<Track> > NoChildren; + static const std::list< std::tr1::shared_ptr<Track> > NoChildren; /** * The internal implementation of print_branch. diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index 6f755f25c..f2ae2da56 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -39,8 +39,8 @@ using namespace gui::widgets; using namespace gui::widgets::timeline; using namespace gui::model; -using boost::shared_ptr; ///////////////////////////////TICKET #796 -using boost::weak_ptr; ///////////////////////////////TICKET #796 +using std::tr1::shared_ptr; +using std::tr1::weak_ptr; using util::contains; namespace gui { diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp index 2215c8657..87c05425c 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -36,12 +36,8 @@ #include "lib/time/timevalue.hpp" -#include <boost/scoped_ptr.hpp> -#include <boost/weak_ptr.hpp> - using namespace gui::widgets; - namespace gui { namespace model { @@ -127,8 +123,8 @@ private: void show_time (Time); - boost::shared_ptr<widgets::timeline::TimelineState> ///////////////////////////////TICKET #796 - load_state (boost::weak_ptr<model::Sequence> sequence); + std::tr1::shared_ptr<widgets::timeline::TimelineState> + load_state (std::tr1::weak_ptr<model::Sequence> sequence); private: @@ -148,7 +144,7 @@ private: * An invisible column which will be used to identify the sequence * of a row. */ - Gtk::TreeModelColumn< boost::weak_ptr<model::Sequence> > + Gtk::TreeModelColumn< std::tr1::weak_ptr<model::Sequence> > sequenceColumn; /** @@ -172,9 +168,9 @@ private: // Body Widgets boost::scoped_ptr<TimelineWidget> timelineWidget; - - std::map< boost::weak_ptr<model::Sequence>, - boost::shared_ptr<widgets::timeline::TimelineState> > ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr + + std::map< std::tr1::weak_ptr<model::Sequence>, + std::tr1::shared_ptr<widgets::timeline::TimelineState> > timelineStates; // Toolbar Widgets diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index eb4decffa..4af85069a 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -27,7 +27,8 @@ using namespace Gtk; using namespace std; -using namespace boost; +using namespace std::tr1; + using namespace util; using namespace gui::widgets::timeline; @@ -44,7 +45,7 @@ const double TimelineWidget::ZoomIncrement = 1.25; const int64_t TimelineWidget::MaxScale = 30000000; // 30 Million TimelineWidget::TimelineWidget( - boost::shared_ptr<timeline::TimelineState> source_state) : + std::tr1::shared_ptr<timeline::TimelineState> source_state) : Table(2, 2), layoutHelper(*this), headerContainer(NULL), @@ -93,7 +94,7 @@ TimelineWidget::~TimelineWidget() /* ===== Data Access ===== */ -boost::shared_ptr<timeline::TimelineState> +shared_ptr<timeline::TimelineState> TimelineWidget::get_state() { return state; @@ -334,8 +335,8 @@ TimelineWidget::create_timeline_track_from_modelTrack( void TimelineWidget::remove_orphaned_tracks() { - std::map<boost::shared_ptr<model::Track>, - boost::shared_ptr<timeline::Track> > + std::map<shared_ptr<model::Track>, + shared_ptr<timeline::Track> > orphan_track_map(trackMap); // Remove all tracks which are still present in the sequence @@ -356,9 +357,9 @@ TimelineWidget::remove_orphaned_tracks() void TimelineWidget::search_orphaned_tracks_in_branch( - boost::shared_ptr<model::Track> modelTrack, - std::map<boost::shared_ptr<model::Track>, - boost::shared_ptr<timeline::Track> > &orphan_track_map) + shared_ptr<model::Track> modelTrack, + std::map<shared_ptr<model::Track>, + shared_ptr<timeline::Track> > &orphan_track_map) { REQUIRE(modelTrack); @@ -480,7 +481,7 @@ TimelineWidget::on_motion_in_body_notify_event(GdkEventMotion *event) return true; } -boost::shared_ptr<model::Sequence> +shared_ptr<model::Sequence> TimelineWidget::sequence() const { if(!state) diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index 884598067..6bf71ef0d 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -69,7 +69,7 @@ public: * for this timeline widget. */ TimelineWidget( - boost::shared_ptr<timeline::TimelineState> source_state); + std::tr1::shared_ptr<timeline::TimelineState> source_state); /** * Destructor @@ -84,13 +84,13 @@ public: * @return The state object that the timeline widget is currently * working with. */ - boost::shared_ptr<timeline::TimelineState> get_state(); + std::tr1::shared_ptr<timeline::TimelineState> get_state(); /** * Replaces the current TimelineState object with another. * @param new_state The new state to swap in. */ - void set_state(boost::shared_ptr<timeline::TimelineState> new_state); + void set_state(std::tr1::shared_ptr<timeline::TimelineState> new_state); /** * Zooms the view in or out as by a number of steps while keeping a @@ -110,13 +110,13 @@ public: */ void set_tool(timeline::ToolType tool_type); - boost::shared_ptr<timeline::Track> + std::tr1::shared_ptr<timeline::Track> get_hovering_track() const; public: /* ===== Signals ===== */ - typedef sigc::signal<void, boost::shared_ptr<timeline::TimelineState> > TimelineStateChangeSignal; - typedef sigc::signal<void, boost::shared_ptr<timeline::Track> > HoveringTrackChangedSignal; + typedef sigc::signal<void, std::tr1::shared_ptr<timeline::TimelineState> > TimelineStateChangeSignal; + typedef sigc::signal<void, std::tr1::shared_ptr<timeline::Track> > HoveringTrackChangedSignal; sigc::signal<void, lib::time::Time> mouse_hover_signal() const; @@ -170,7 +170,7 @@ private: * @param list The parent track of the branch. */ void create_timeline_tracks_from_branch( - boost::shared_ptr<model::Track> modelTrack); + std::tr1::shared_ptr<model::Track> modelTrack); /** * Creates a timeline UI track to correspond to a model track. @@ -178,9 +178,9 @@ private: * @return The timeline track created, or an empty shared_ptr if * modelTrack has an unreckognised type (this is an error condition). */ - boost::shared_ptr<timeline::Track> + std::tr1::shared_ptr<timeline::Track> create_timeline_track_from_modelTrack( - boost::shared_ptr<model::Track> modelTrack); + std::tr1::shared_ptr<model::Track> modelTrack); /** * Removes any UI tracks which no longer have corresponding model @@ -189,9 +189,9 @@ private: void remove_orphaned_tracks(); void search_orphaned_tracks_in_branch( - boost::shared_ptr<model::Track> modelTrack, - std::map<boost::shared_ptr<model::Track>, - boost::shared_ptr<timeline::Track> > &orphan_track_map); + std::tr1::shared_ptr<model::Track> modelTrack, + std::map<std::tr1::shared_ptr<model::Track>, + std::tr1::shared_ptr<timeline::Track> > &orphan_track_map); /** * Looks up a timeline UI track in trackMap that corresponds to a @@ -201,8 +201,8 @@ private: * modelTrack has no corresponding timeline UI track (this is an * error condition). */ - boost::shared_ptr<timeline::Track> lookup_timeline_track( - boost::shared_ptr<model::Track> modelTrack) const; + std::tr1::shared_ptr<timeline::Track> lookup_timeline_track( + std::tr1::shared_ptr<model::Track> modelTrack) const; // ----- Layout Functions ----- // @@ -232,12 +232,12 @@ private: * Helper to get the sequence object from the state. * @return Returns a shared pointer to the sequence. */ - boost::shared_ptr<model::Sequence> sequence() const; + std::tr1::shared_ptr<model::Sequence> sequence() const; // ----- Other Functions ----- // void set_hovering_track( - boost::shared_ptr<timeline::Track> hovering_track); + std::tr1::shared_ptr<timeline::Track> hovering_track); protected: @@ -245,7 +245,7 @@ protected: * The state that will be used as the data source for this timeline * widget. */ - boost::shared_ptr<timeline::TimelineState> state; + std::tr1::shared_ptr<timeline::TimelineState> state; // Model Data @@ -256,11 +256,11 @@ protected: * widget is updated with update_tracks, timeline tracks are added and * removed from the map in correspondence with the tree. */ - std::map<boost::shared_ptr<model::Track>, - boost::shared_ptr<timeline::Track> > + std::map<std::tr1::shared_ptr<model::Track>, + std::tr1::shared_ptr<timeline::Track> > trackMap; - boost::shared_ptr<timeline::Track> hoveringTrack; + std::tr1::shared_ptr<timeline::Track> hoveringTrack; // Helper Classes timeline::TimelineLayoutHelper layoutHelper; diff --git a/src/gui/widgets/timeline/timeline-arrow-tool.cpp b/src/gui/widgets/timeline/timeline-arrow-tool.cpp index dab5539e4..420d3a964 100644 --- a/src/gui/widgets/timeline/timeline-arrow-tool.cpp +++ b/src/gui/widgets/timeline/timeline-arrow-tool.cpp @@ -55,17 +55,17 @@ namespace timeline { Tool::on_button_press_event(event); // Convert the mouse click position to a Time - boost::shared_ptr<TimelineState> state = timelineBody.getTimelineWidget().get_state(); + std::tr1::shared_ptr<TimelineState> state = timelineBody.getTimelineWidget().get_state(); REQUIRE(state); TimelineViewWindow const& window = state->get_view_window(); Time tpoint = window.x_to_time(mousePoint.get_x()); // Get the clip, if any - boost::shared_ptr<timeline::Track> track = getHoveringTrack(); - boost::shared_ptr<Clip> clip = track->getClipAt(tpoint); + std::tr1::shared_ptr<timeline::Track> track = getHoveringTrack(); + std::tr1::shared_ptr<Clip> clip = track->getClipAt(tpoint); // Nothing to do if there is no clip - if (clip == boost::shared_ptr<Clip>()) + if (clip == std::tr1::shared_ptr<Clip>()) return; clip->setSelected(true); @@ -77,7 +77,7 @@ namespace timeline { REQUIRE (event != NULL); Tool::on_button_release_event(event); - boost::shared_ptr<timeline::Track> track = + std::tr1::shared_ptr<timeline::Track> track = getHoveringTrack(); } @@ -92,10 +92,10 @@ namespace timeline { return; } - boost::shared_ptr<timeline::Track> + std::tr1::shared_ptr<timeline::Track> ArrowTool::getHoveringTrack () { - boost::shared_ptr<timeline::Track> track( + std::tr1::shared_ptr<timeline::Track> track( timelineBody.getTimelineWidget().get_hovering_track()); return track; } diff --git a/src/gui/widgets/timeline/timeline-arrow-tool.hpp b/src/gui/widgets/timeline/timeline-arrow-tool.hpp index 83abd77e7..c2f0df070 100644 --- a/src/gui/widgets/timeline/timeline-arrow-tool.hpp +++ b/src/gui/widgets/timeline/timeline-arrow-tool.hpp @@ -81,7 +81,7 @@ namespace timeline { private: - boost::shared_ptr<timeline::Track> + std::tr1::shared_ptr<timeline::Track> getHoveringTrack (); bool selectionRectangleActive; diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index b64757477..d546ada65 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -34,12 +34,10 @@ using namespace Gtk; using namespace std; -using namespace boost; using namespace lumiera; using gui::util::CairoUtil; - -using boost::shared_ptr; ////////////////////TICKET #796 +using std::tr1::shared_ptr; namespace gui { namespace widgets { @@ -351,7 +349,7 @@ TimelineBody::draw_tracks(Cairo::RefPtr<Cairo::Context> cr) const shared_ptr<timeline::Track> timeline_track = timelineWidget.lookup_timeline_track(*iterator); - optional<Gdk::Rectangle> rect = + boost::optional<Gdk::Rectangle> rect = layout_helper.get_track_header_rect(timeline_track); // Is this track visible? diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index aaf90cb2e..dbc6697a1 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -124,7 +124,7 @@ protected: /** * The event handler for when the TimelineWidget's state is switched. */ - void on_state_changed (boost::shared_ptr<TimelineState> newState); + void on_state_changed (std::tr1::shared_ptr<TimelineState> newState); /* ===== Internals ===== */ private: @@ -142,7 +142,7 @@ private: void draw_tracks(Cairo::RefPtr<Cairo::Context> cr); void draw_track(Cairo::RefPtr<Cairo::Context> cr, - boost::shared_ptr<timeline::Track> timeline_track, + std::tr1::shared_ptr<timeline::Track> timeline_track, const int view_width) const; /** @@ -200,7 +200,7 @@ private: Cairo::RefPtr<Cairo::SolidPattern> playbackPointColour; gui::widgets::TimelineWidget &timelineWidget; - boost::shared_ptr<TimelineState> timelineState; ////////////////////TICKET #796 : should use std::tr1::shared_ptr + std::tr1::shared_ptr<TimelineState> timelineState; friend class Tool; diff --git a/src/gui/widgets/timeline/timeline-clip-track.cpp b/src/gui/widgets/timeline/timeline-clip-track.cpp index 39d254a48..9f5438d0a 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.cpp +++ b/src/gui/widgets/timeline/timeline-clip-track.cpp @@ -27,8 +27,8 @@ #include "timeline-view-window.hpp" using namespace Gtk; -using boost::dynamic_pointer_cast; -using boost::shared_ptr; + +using std::tr1::shared_ptr; using util::contains; namespace gui { @@ -76,7 +76,7 @@ namespace timeline { } } - boost::shared_ptr<timeline::Clip> + std::tr1::shared_ptr<timeline::Clip> ClipTrack::getClipAt(Time position) const { std::pair<shared_ptr<model::Clip>, shared_ptr<timeline::Clip> > @@ -88,7 +88,7 @@ namespace timeline { } // Nothing found - return boost::shared_ptr<timeline::Clip>(); + return std::tr1::shared_ptr<timeline::Clip>(); } //// private methods @@ -97,8 +97,8 @@ namespace timeline { ClipTrack::createTimelineClips() { // Share the draw strategy between all objects - // TODO: use factory/builder here - static boost::shared_ptr<timeline::DrawStrategy> drawStrategy(new BasicDrawStrategy()); + TODO("Use factory/builder to create Timline Clips"); + static std::tr1::shared_ptr<timeline::DrawStrategy> drawStrategy(new BasicDrawStrategy()); BOOST_FOREACH (shared_ptr<model::Clip> modelClip, getModelTrack()->getClipList()) { // Is a timeline UI clip present in the map already? @@ -115,7 +115,7 @@ namespace timeline { shared_ptr<model::ClipTrack> ClipTrack::getModelTrack () { - return dynamic_pointer_cast<model::ClipTrack>(modelTrack); + return std::tr1::dynamic_pointer_cast<model::ClipTrack>(modelTrack); } void diff --git a/src/gui/widgets/timeline/timeline-clip-track.hpp b/src/gui/widgets/timeline/timeline-clip-track.hpp index 1265c913b..980c37480 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.hpp +++ b/src/gui/widgets/timeline/timeline-clip-track.hpp @@ -52,7 +52,7 @@ namespace timeline { * Constructor. */ ClipTrack(TimelineWidget &timelineWidget, - boost::shared_ptr<model::ClipTrack> track); + std::tr1::shared_ptr<model::ClipTrack> track); /** * Draw the track in the timeline. @@ -66,7 +66,7 @@ namespace timeline { * pointer. * @param the given time */ - boost::shared_ptr<timeline::Clip> + std::tr1::shared_ptr<timeline::Clip> getClipAt(Time position) const; private: @@ -80,7 +80,7 @@ namespace timeline { /** * Gets the modelTrack as a ClipTrack. */ - boost::shared_ptr<model::ClipTrack> + std::tr1::shared_ptr<model::ClipTrack> getModelTrack (); /** @@ -107,8 +107,8 @@ namespace timeline { * The clipMap maps model clips to timeline widget clips which are responsible for the * UI representation of a clip. */ - std::map<boost::shared_ptr<model::Clip>, - boost::shared_ptr<timeline::Clip> > + std::map<std::tr1::shared_ptr<model::Clip>, + std::tr1::shared_ptr<timeline::Clip> > clipMap; }; diff --git a/src/gui/widgets/timeline/timeline-clip.cpp b/src/gui/widgets/timeline/timeline-clip.cpp index f2328bdc5..c6d90be45 100644 --- a/src/gui/widgets/timeline/timeline-clip.cpp +++ b/src/gui/widgets/timeline/timeline-clip.cpp @@ -27,11 +27,11 @@ namespace gui { namespace widgets { namespace timeline { - using boost::shared_ptr; //////////////////////TICKET #796 + using std::tr1::shared_ptr; //////////////////////TICKET #796 - Clip::Clip (boost::shared_ptr<model::Clip> clip, - boost::shared_ptr<timeline::DrawStrategy> drawStrategy) + Clip::Clip (std::tr1::shared_ptr<model::Clip> clip, + std::tr1::shared_ptr<timeline::DrawStrategy> drawStrategy) : Entity(drawStrategy) , modelClip(clip) , selected(false) diff --git a/src/gui/widgets/timeline/timeline-clip.hpp b/src/gui/widgets/timeline/timeline-clip.hpp index 2d6d83139..001fb94a0 100644 --- a/src/gui/widgets/timeline/timeline-clip.hpp +++ b/src/gui/widgets/timeline/timeline-clip.hpp @@ -49,8 +49,8 @@ namespace timeline { class Clip : public Entity { public: - Clip (boost::shared_ptr<model::Clip> clip, - boost::shared_ptr<timeline::DrawStrategy> drawStrategy); + Clip (std::tr1::shared_ptr<model::Clip> clip, + std::tr1::shared_ptr<timeline::DrawStrategy> drawStrategy); Time getBegin() const; @@ -62,7 +62,7 @@ namespace timeline { private: - boost::shared_ptr<model::Clip> modelClip; ////////////////////////TICKET #796 : should use std::tr1 + std::tr1::shared_ptr<model::Clip> modelClip; /** * True when this clip is selected in the GUI. diff --git a/src/gui/widgets/timeline/timeline-entity.cpp b/src/gui/widgets/timeline/timeline-entity.cpp index 13dc398aa..bc499b3d4 100644 --- a/src/gui/widgets/timeline/timeline-entity.cpp +++ b/src/gui/widgets/timeline/timeline-entity.cpp @@ -29,7 +29,7 @@ namespace gui { namespace widgets { namespace timeline { - Entity::Entity(boost::shared_ptr<timeline::DrawStrategy> drawStrategy) + Entity::Entity(std::tr1::shared_ptr<timeline::DrawStrategy> drawStrategy) : enabled(true), drawStrategy(drawStrategy) { } diff --git a/src/gui/widgets/timeline/timeline-entity.hpp b/src/gui/widgets/timeline/timeline-entity.hpp index a6a03b5da..bfb170aee 100644 --- a/src/gui/widgets/timeline/timeline-entity.hpp +++ b/src/gui/widgets/timeline/timeline-entity.hpp @@ -31,8 +31,9 @@ #include "lib/time/timevalue.hpp" #include <string> +#include <tr1/memory> #include <cairomm/cairomm.h> -#include <boost/shared_ptr.hpp> //////////////////////TICKET #796 + namespace gui { namespace widgets { @@ -51,7 +52,7 @@ namespace timeline { class Entity { protected: - Entity(boost::shared_ptr<timeline::DrawStrategy> drawStrategy); + Entity(std::tr1::shared_ptr<timeline::DrawStrategy> drawStrategy); virtual ~Entity(); @@ -81,7 +82,7 @@ namespace timeline { bool enabled; - boost::shared_ptr<timeline::DrawStrategy> drawStrategy; + std::tr1::shared_ptr<timeline::DrawStrategy> drawStrategy; }; } // namespace timeline diff --git a/src/gui/widgets/timeline/timeline-group-track.cpp b/src/gui/widgets/timeline/timeline-group-track.cpp index aa43e1970..4ed072453 100644 --- a/src/gui/widgets/timeline/timeline-group-track.cpp +++ b/src/gui/widgets/timeline/timeline-group-track.cpp @@ -24,7 +24,7 @@ #include "gui/widgets/timeline-widget.hpp" using namespace Gtk; -using namespace boost; +using namespace std::tr1; using namespace sigc; namespace gui { diff --git a/src/gui/widgets/timeline/timeline-group-track.hpp b/src/gui/widgets/timeline/timeline-group-track.hpp index 3cf079b47..822142b0d 100644 --- a/src/gui/widgets/timeline/timeline-group-track.hpp +++ b/src/gui/widgets/timeline/timeline-group-track.hpp @@ -37,7 +37,7 @@ class GroupTrack : public timeline::Track { public: GroupTrack(TimelineWidget &timeline_widget, - boost::shared_ptr<model::GroupTrack> track); + std::tr1::shared_ptr<model::GroupTrack> track); void draw_track(Cairo::RefPtr<Cairo::Context> cairo, TimelineViewWindow* constwindow) diff --git a/src/gui/widgets/timeline/timeline-header-container.cpp b/src/gui/widgets/timeline/timeline-header-container.cpp index 3ecfba2d0..486859845 100644 --- a/src/gui/widgets/timeline/timeline-header-container.cpp +++ b/src/gui/widgets/timeline/timeline-header-container.cpp @@ -30,7 +30,7 @@ using namespace Gtk; using namespace std; -using namespace boost; +using namespace std::tr1; using namespace util; namespace gui { @@ -343,7 +343,7 @@ TimelineHeaderContainer::on_scroll() void TimelineHeaderContainer::on_hovering_track_changed( - boost::shared_ptr<timeline::Track> hovering_track) + std::tr1::shared_ptr<timeline::Track> hovering_track) { (void)hovering_track; @@ -392,7 +392,7 @@ TimelineHeaderContainer::layout_headers() Widget &widget = timeline_track->get_header_widget(); - optional<Gdk::Rectangle> header_rect = + boost::optional<Gdk::Rectangle> header_rect = layout_helper.get_track_header_rect(timeline_track); if(header_rect) diff --git a/src/gui/widgets/timeline/timeline-header-container.hpp b/src/gui/widgets/timeline/timeline-header-container.hpp index 094619ecc..6d40d621e 100644 --- a/src/gui/widgets/timeline/timeline-header-container.hpp +++ b/src/gui/widgets/timeline/timeline-header-container.hpp @@ -140,7 +140,7 @@ private: void on_scroll(); void on_hovering_track_changed( - boost::shared_ptr<timeline::Track> hovering_track); + std::tr1::shared_ptr<timeline::Track> hovering_track); private: /* ===== Internal Event Handlers ===== */ @@ -169,7 +169,7 @@ private: * @param offset The vertical offset of the headers in pixels. */ void draw_header_decoration( - boost::shared_ptr<model::Track> modelTrack, + std::tr1::shared_ptr<model::Track> modelTrack, const Gdk::Rectangle &clip_rect); /** @@ -182,8 +182,8 @@ private: * @remarks If the return value is going to be NULL, an ENSURE will * fail. */ - boost::shared_ptr<timeline::Track> lookup_timeline_track( - boost::shared_ptr<model::Track> modelTrack); + std::tr1::shared_ptr<timeline::Track> lookup_timeline_track( + std::tr1::shared_ptr<model::Track> modelTrack); void begin_drag(); @@ -248,7 +248,7 @@ private: int scrollSlideRate; //----- User Interaction State -----// - boost::shared_ptr<timeline::Track> hoveringTrack; + std::tr1::shared_ptr<timeline::Track> hoveringTrack; Gdk::Point mousePoint; diff --git a/src/gui/widgets/timeline/timeline-header-widget.cpp b/src/gui/widgets/timeline/timeline-header-widget.cpp index 734e42dfd..e9588fa82 100644 --- a/src/gui/widgets/timeline/timeline-header-widget.cpp +++ b/src/gui/widgets/timeline/timeline-header-widget.cpp @@ -28,9 +28,10 @@ using namespace Gtk; using namespace std; -using namespace boost; using namespace util; +using std::tr1::shared_ptr; + namespace gui { namespace widgets { namespace timeline { diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp index 6a858aa18..71aeef656 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp @@ -29,7 +29,7 @@ using namespace gui::widgets; using lib::time::Mutation; -using boost::shared_ptr; /////////////////////////TICKET #796 +using std::tr1::shared_ptr; namespace gui { namespace widgets { diff --git a/src/gui/widgets/timeline/timeline-layout-helper.cpp b/src/gui/widgets/timeline/timeline-layout-helper.cpp index 0e311cd7d..71a55d44a 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.cpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.cpp @@ -30,7 +30,7 @@ using namespace Gtk; using namespace std; -using namespace boost; +using namespace std::tr1; using namespace lumiera; using namespace util; using namespace gui::util; @@ -80,17 +80,17 @@ TimelineLayoutHelper::add_branch( } } -optional<Gdk::Rectangle> +boost::optional<Gdk::Rectangle> TimelineLayoutHelper::get_track_header_rect( - boost::weak_ptr<timeline::Track> track) + std::tr1::weak_ptr<timeline::Track> track) { if(contains(headerBoxes, track)) { Gdk::Rectangle rect(headerBoxes[track]); rect.set_y(rect.get_y() - timelineWidget.get_y_scroll_offset()); - return optional<Gdk::Rectangle>(rect); + return boost::optional<Gdk::Rectangle>(rect); } - return optional<Gdk::Rectangle>(); + return boost::optional<Gdk::Rectangle>(); } shared_ptr<timeline::Track> @@ -112,7 +112,7 @@ TimelineLayoutHelper::header_from_point(Gdk::Point point) return shared_ptr<timeline::Track>(); } -boost::shared_ptr<timeline::Track> +std::tr1::shared_ptr<timeline::Track> TimelineLayoutHelper::track_from_y(int y) { // Apply the scroll offset @@ -300,7 +300,7 @@ TimelineLayoutHelper::is_animating() const TimelineLayoutHelper::TrackTree::pre_order_iterator TimelineLayoutHelper::iterator_from_track( - boost::shared_ptr<model::Track> modelTrack) + std::tr1::shared_ptr<model::Track> modelTrack) { REQUIRE(modelTrack); @@ -666,7 +666,7 @@ TimelineLayoutHelper::apply_drop_to_modelTree( timelineWidget.thaw_update_tracks(); } -boost::shared_ptr<model::Sequence> +std::tr1::shared_ptr<model::Sequence> TimelineLayoutHelper::get_sequence() const { REQUIRE(timelineWidget.state); diff --git a/src/gui/widgets/timeline/timeline-layout-helper.hpp b/src/gui/widgets/timeline/timeline-layout-helper.hpp index bc0f1c39d..07e792e9a 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.hpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.hpp @@ -59,7 +59,7 @@ public: /** * Definition of the layout track tree type. */ - typedef lumiera::tree< boost::shared_ptr<model::Track> > TrackTree; + typedef lumiera::tree< std::tr1::shared_ptr<model::Track> > TrackTree; public: /** @@ -101,7 +101,7 @@ public: * @see update_layout() */ boost::optional<Gdk::Rectangle> get_track_header_rect( - boost::weak_ptr<timeline::Track> track); + std::tr1::weak_ptr<timeline::Track> track); /** * Searches for a header which has the specified point inside of it. @@ -114,7 +114,7 @@ public: * tracks. * @see update_layout() */ - boost::shared_ptr<timeline::Track> header_from_point( + std::tr1::shared_ptr<timeline::Track> header_from_point( Gdk::Point point); /** @@ -128,14 +128,14 @@ public: * tracks. * @see update_layout() */ - boost::shared_ptr<timeline::Track> track_from_y(int y); + std::tr1::shared_ptr<timeline::Track> track_from_y(int y); /** * Begins to drag the track under mouse_point, if there is one. * @param mouse_point The mouse point to begin dragging from, measured * in pixels from the top left of the header container widget. */ - boost::shared_ptr<timeline::Track> + std::tr1::shared_ptr<timeline::Track> begin_dragging_track(const Gdk::Point &mouse_point); /** @@ -186,7 +186,7 @@ public: * iterator was found. */ TrackTree::pre_order_iterator iterator_from_track( - boost::shared_ptr<model::Track> modelTrack); + std::tr1::shared_ptr<model::Track> modelTrack); /** * A function that recursively calculates the visible height of a @@ -262,7 +262,7 @@ protected: * @see clone_tree_from_sequence() */ void add_branch(TrackTree::iterator_base parent_iterator, - boost::shared_ptr<model::Track> parent); + std::tr1::shared_ptr<model::Track> parent); /** * Recursively calculates the boxes for a given branch in the timeline @@ -298,8 +298,8 @@ protected: * @remarks If the return value is going to be NULL, an ENSURE will * fail. */ - boost::shared_ptr<timeline::Track> lookup_timeline_track( - boost::shared_ptr<model::Track> modelTrack); + std::tr1::shared_ptr<timeline::Track> lookup_timeline_track( + std::tr1::shared_ptr<model::Track> modelTrack); /** * A helper function which kicks off the animation timer. @@ -343,7 +343,7 @@ protected: * Helper to get the sequence object from the state. * @return Returns a shared pointer to the sequence. */ - boost::shared_ptr<model::Sequence> get_sequence() const; + std::tr1::shared_ptr<model::Sequence> get_sequence() const; protected: /** @@ -363,7 +363,7 @@ protected: * the update_layout method. * @see update_layout() */ - std::map<boost::weak_ptr<timeline::Track>, Gdk::Rectangle> + std::map<std::tr1::weak_ptr<timeline::Track>, Gdk::Rectangle> headerBoxes; /** diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp index 03a866482..0b8d68f22 100644 --- a/src/gui/widgets/timeline/timeline-ruler.cpp +++ b/src/gui/widgets/timeline/timeline-ruler.cpp @@ -36,7 +36,7 @@ using namespace gui; using namespace gui::widgets; using namespace gui::widgets::timeline; -using boost::shared_ptr; ////////////////////TICKET #796 +using std::tr1::shared_ptr; ////////////////////TICKET #796 using gui::util::CairoUtil; using lib::time::Time; using lib::time::TimeVar; diff --git a/src/gui/widgets/timeline/timeline-ruler.hpp b/src/gui/widgets/timeline/timeline-ruler.hpp index a7266793f..842ce6bff 100644 --- a/src/gui/widgets/timeline/timeline-ruler.hpp +++ b/src/gui/widgets/timeline/timeline-ruler.hpp @@ -111,7 +111,7 @@ private: /** * The event handler for when the TimelineWidget's state is switched. */ - void on_state_changed (boost::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr + void on_state_changed (std::tr1::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr private: /* ===== Internal Methods ===== */ @@ -242,7 +242,7 @@ private: /** * the currently active timeline state object */ - boost::shared_ptr<TimelineState> timelineState; ////////////////////TICKET #796 : should use std::tr1::shared_ptr + std::tr1::shared_ptr<TimelineState> timelineState; /** * The caches image of the ruler, over which the chevrons etc. will diff --git a/src/gui/widgets/timeline/timeline-state.cpp b/src/gui/widgets/timeline/timeline-state.cpp index 62f67aa08..b55589956 100644 --- a/src/gui/widgets/timeline/timeline-state.cpp +++ b/src/gui/widgets/timeline/timeline-state.cpp @@ -36,11 +36,11 @@ using lib::time::FSecs; using lib::time::Offset; using lib::time::Duration; using lib::time::Mutation; -using boost::shared_ptr; /////////////////////////TICKET #796 +using std::tr1::shared_ptr; -TimelineState::TimelineState (boost::shared_ptr<model::Sequence> source_sequence) +TimelineState::TimelineState (std::tr1::shared_ptr<model::Sequence> source_sequence) : sequence(source_sequence) , viewWindow(Offset(Time::ZERO), 1) , selection_(Time::ZERO, Duration::NIL) @@ -60,7 +60,7 @@ TimelineState::TimelineState (boost::shared_ptr<model::Sequence> source_sequence //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all } -boost::shared_ptr<model::Sequence> +std::tr1::shared_ptr<model::Sequence> TimelineState::get_sequence() const { return sequence; diff --git a/src/gui/widgets/timeline/timeline-state.hpp b/src/gui/widgets/timeline/timeline-state.hpp index 94c072950..23e2015e7 100644 --- a/src/gui/widgets/timeline/timeline-state.hpp +++ b/src/gui/widgets/timeline/timeline-state.hpp @@ -56,7 +56,7 @@ public: * @param source_sequence The sequence on which the TimelineWidget * will operate when this TimelineState is attached. */ - TimelineState(boost::shared_ptr<model::Sequence> source_sequence); + TimelineState(std::tr1::shared_ptr<model::Sequence> source_sequence); public: @@ -64,7 +64,7 @@ public: * Gets the sequence that is attached to this timeline state object. * @return Returns a shared_ptr to the sequence object. */ - boost::shared_ptr<model::Sequence> get_sequence() const; + std::tr1::shared_ptr<model::Sequence> get_sequence() const; /** * Gets a reference to the timeline view window object. @@ -122,7 +122,7 @@ private: * @remarks This pointer is set by the constructor and is constant, so * will not change in value during the lifetime of the class. */ - boost::shared_ptr<model::Sequence> sequence; + std::tr1::shared_ptr<model::Sequence> sequence; // View State /** diff --git a/src/gui/widgets/timeline/timeline-tool.cpp b/src/gui/widgets/timeline/timeline-tool.cpp index 9e261441d..2402f22ea 100644 --- a/src/gui/widgets/timeline/timeline-tool.cpp +++ b/src/gui/widgets/timeline/timeline-tool.cpp @@ -24,7 +24,7 @@ #include "gui/widgets/timeline-widget.hpp" using namespace Gdk; -using namespace boost; +using std::tr1::shared_ptr; namespace gui { namespace widgets { diff --git a/src/gui/widgets/timeline/timeline-tool.hpp b/src/gui/widgets/timeline/timeline-tool.hpp index e63796d56..f34cb5d32 100644 --- a/src/gui/widgets/timeline/timeline-tool.hpp +++ b/src/gui/widgets/timeline/timeline-tool.hpp @@ -133,7 +133,7 @@ protected: /** * A helper function to get the state */ - boost::shared_ptr<TimelineState> get_state() const; + std::tr1::shared_ptr<TimelineState> get_state() const; /** * A helper function to get the view window diff --git a/src/gui/widgets/timeline/timeline-track.cpp b/src/gui/widgets/timeline/timeline-track.cpp index 0a86daf2d..b0267dea3 100644 --- a/src/gui/widgets/timeline/timeline-track.cpp +++ b/src/gui/widgets/timeline/timeline-track.cpp @@ -29,7 +29,7 @@ using namespace Gtk; using namespace sigc; -using boost::shared_ptr; //////////////////////////////TICKET #796 : should use std::tr1 +using std::tr1::shared_ptr; namespace gui { namespace widgets { @@ -125,7 +125,7 @@ shared_ptr<timeline::Clip> Track::getClipAt(Time) const { // Default implementation returns empty pointer - return boost::shared_ptr<timeline::Clip>(); + return std::tr1::shared_ptr<timeline::Clip>(); } void @@ -272,7 +272,7 @@ void Track::on_remove_track() { REQUIRE(modelTrack); - boost::shared_ptr<TimelineState> state = timelineWidget.get_state(); + std::tr1::shared_ptr<TimelineState> state = timelineWidget.get_state(); REQUIRE(state); state->get_sequence()->remove_descendant_track(modelTrack); diff --git a/src/gui/widgets/timeline/timeline-track.hpp b/src/gui/widgets/timeline/timeline-track.hpp index 79b7dec98..796b1f130 100644 --- a/src/gui/widgets/timeline/timeline-track.hpp +++ b/src/gui/widgets/timeline/timeline-track.hpp @@ -76,7 +76,7 @@ public: * Constructor */ Track(TimelineWidget &timeline_widget, - boost::shared_ptr<model::Track> track); + std::tr1::shared_ptr<model::Track> track); /** * Destructor @@ -85,7 +85,7 @@ public: Gtk::Widget& get_header_widget(); - boost::shared_ptr<model::Track> //////////////////////////////TICKET #796 : should use std::tr1 + std::tr1::shared_ptr<model::Track> //////////////////////////////TICKET #796 : should use std::tr1 getModelTrack() const; /** @@ -155,7 +155,7 @@ public: * The default implementation simply returns an empty pointer. * @param the given time */ - virtual boost::shared_ptr<timeline::Clip> //////////////////////////////TICKET #796 : should use std::tr1 + virtual std::tr1::shared_ptr<timeline::Clip> //////////////////////////////TICKET #796 : should use std::tr1 getClipAt (Time position) const; private: @@ -209,7 +209,7 @@ private: protected: TimelineWidget &timelineWidget; - boost::shared_ptr<model::Track> modelTrack; + std::tr1::shared_ptr<model::Track> modelTrack; private: /** diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp index eedaf074b..083513647 100644 --- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp +++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp @@ -20,8 +20,8 @@ * *****************************************************/ -#include "gui/widgets/timeline/timeline-zoom-scale.hpp" #include "gui/widgets/timeline-widget.hpp" +#include "gui/widgets/timeline/timeline-zoom-scale.hpp" using namespace Gtk; @@ -90,7 +90,7 @@ TimelineZoomScale::TimelineZoomScale() } void -TimelineZoomScale::wireTimelineState (boost::shared_ptr<TimelineState> currentState, +TimelineZoomScale::wireTimelineState (std::tr1::shared_ptr<TimelineState> currentState, TimelineWidget::TimelineStateChangeSignal stateChangeSignal) { on_timeline_state_changed (currentState); @@ -98,7 +98,7 @@ TimelineZoomScale::wireTimelineState (boost::shared_ptr<TimelineState> currentSt } void -TimelineZoomScale::on_timeline_state_changed (boost::shared_ptr<TimelineState> newState) +TimelineZoomScale::on_timeline_state_changed (std::tr1::shared_ptr<TimelineState> newState) { REQUIRE (newState); timelineState = newState; diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.hpp b/src/gui/widgets/timeline/timeline-zoom-scale.hpp index 9bfe8139a..ec106ad94 100644 --- a/src/gui/widgets/timeline/timeline-zoom-scale.hpp +++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp @@ -29,10 +29,6 @@ #include "gui/gtk-lumiera.hpp" #include "gui/widgets/mini-button.hpp" #include "gui/widgets/timeline-widget.hpp" -#include "gui/widgets/timeline/timeline-state.hpp" -#include "gui/widgets/timeline/timeline-view-window.hpp" - -#include <boost/shared_ptr.hpp> using namespace Gtk; using namespace gui::widgets; @@ -55,7 +51,7 @@ public: */ sigc::signal<void, double> signal_zoom(); - void wireTimelineState (boost::shared_ptr<TimelineState> currentState, + void wireTimelineState (std::tr1::shared_ptr<TimelineState> currentState, TimelineWidget::TimelineStateChangeSignal); private: @@ -65,7 +61,7 @@ private: * Update the slider position when the timeline state * is changed. */ - void on_timeline_state_changed (boost::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr + void on_timeline_state_changed (std::tr1::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr /** * Event handler for when the zoomIn Button @@ -100,7 +96,7 @@ private: const double button_step_size; - boost::shared_ptr<TimelineState> timelineState; + std::tr1::shared_ptr<TimelineState> timelineState; }; } // namespace gui diff --git a/src/gui/window-manager.cpp b/src/gui/window-manager.cpp index ea8322deb..821cfde60 100644 --- a/src/gui/window-manager.cpp +++ b/src/gui/window-manager.cpp @@ -33,13 +33,12 @@ using util::cStr; using namespace Gtk; using namespace Glib; -using namespace boost; using namespace std; +using namespace std::tr1; using namespace gui::workspace; namespace fsys = boost::filesystem; - namespace gui { IconSize WindowManager::GiantIconSize = ICON_SIZE_INVALID; diff --git a/src/gui/window-manager.hpp b/src/gui/window-manager.hpp index 2dbd91e93..1f0eb3a46 100644 --- a/src/gui/window-manager.hpp +++ b/src/gui/window-manager.hpp @@ -39,10 +39,9 @@ #include "gui/gtk-base.hpp" #include <boost/noncopyable.hpp> -#include <boost/shared_ptr.hpp> #include <cairomm/cairomm.h> #include <string> - +#include <tr1/memory> namespace gui { @@ -195,7 +194,7 @@ private: private: - std::list< boost::shared_ptr<workspace::WorkspaceWindow> > windowList; + std::list< std::tr1::shared_ptr<workspace::WorkspaceWindow> > windowList; public: From c9a0209a0b9b58e29fc7dec9092451f90659dbb3 Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Fri, 21 Oct 2011 09:14:32 -0500 Subject: [PATCH 215/496] Fix ParentTrack: using std::tr1::enable_shared_from_this --- src/gui/model/parent-track.cpp | 7 +------ src/gui/model/parent-track.hpp | 8 +++----- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/gui/model/parent-track.cpp b/src/gui/model/parent-track.cpp index d20a1adc3..3384227a1 100644 --- a/src/gui/model/parent-track.cpp +++ b/src/gui/model/parent-track.cpp @@ -72,6 +72,7 @@ std::tr1::shared_ptr<ParentTrack> ParentTrack::find_descendant_track_parent( std::tr1::shared_ptr<Track> child) { + REQUIRE(child != NULL); BOOST_FOREACH(std::tr1::shared_ptr<Track> track, tracks) { @@ -87,11 +88,5 @@ ParentTrack::find_descendant_track_parent( return std::tr1::shared_ptr<ParentTrack>(); } -std::tr1::shared_ptr<ParentTrack> -ParentTrack::shared_from_this() -{ - return std::tr1::shared_ptr<ParentTrack>(this); -} - } // namespace model } // namespace gui diff --git a/src/gui/model/parent-track.hpp b/src/gui/model/parent-track.hpp index 9a087e98f..b23d2f07c 100644 --- a/src/gui/model/parent-track.hpp +++ b/src/gui/model/parent-track.hpp @@ -38,7 +38,9 @@ namespace model { * ParentTrack is the abstract base class of all tracks that can parent * children. */ -class ParentTrack : public Track +class ParentTrack : + public Track, + public std::tr1::enable_shared_from_this<ParentTrack> { protected: /** @@ -85,10 +87,6 @@ public: std::tr1::shared_ptr<ParentTrack> find_descendant_track_parent(std::tr1::shared_ptr<Track> child); -private: - - std::tr1::shared_ptr<ParentTrack> shared_from_this(); - protected: /** * The internal list of child tracks of this parent. From 2730fa8d7a24b04a829940bed29800ab0ffce99f Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 22 Oct 2011 02:49:30 +0200 Subject: [PATCH 216/496] a bit of stylistic cleanup - using std::tr1::shared_ptr in namespace gui - thus removing a lot of std::tr1 qualifications - fix some includes. Should be relative to 'src' - interface classes should declare a virtual dtor! --- src/gui/gtk-lumiera.hpp | 4 ++ src/gui/model/clip-track.hpp | 6 +- src/gui/model/parent-track.cpp | 15 +++-- src/gui/model/parent-track.hpp | 14 ++--- src/gui/model/project.hpp | 6 +- src/gui/model/sequence.hpp | 2 +- src/gui/model/track.cpp | 5 +- src/gui/model/track.hpp | 9 +-- src/gui/panels/timeline-panel.cpp | 3 +- src/gui/panels/timeline-panel.hpp | 18 ++++-- src/gui/widgets/timeline-widget.cpp | 23 ++++---- src/gui/widgets/timeline-widget.hpp | 55 +++++++++---------- .../widgets/timeline/timeline-arrow-tool.cpp | 15 +++-- .../widgets/timeline/timeline-arrow-tool.hpp | 2 +- src/gui/widgets/timeline/timeline-body.hpp | 8 +-- .../widgets/timeline/timeline-clip-track.cpp | 12 ++-- .../widgets/timeline/timeline-clip-track.hpp | 10 ++-- src/gui/widgets/timeline/timeline-clip.cpp | 6 +- src/gui/widgets/timeline/timeline-clip.hpp | 6 +- src/gui/widgets/timeline/timeline-entity.cpp | 2 +- src/gui/widgets/timeline/timeline-entity.hpp | 5 +- .../widgets/timeline/timeline-group-track.hpp | 15 ++--- .../timeline/timeline-header-container.cpp | 7 +-- .../timeline/timeline-header-container.hpp | 14 ++--- .../timeline/timeline-layout-helper.cpp | 9 ++- .../timeline/timeline-layout-helper.hpp | 22 ++++---- src/gui/widgets/timeline/timeline-ruler.cpp | 2 +- src/gui/widgets/timeline/timeline-ruler.hpp | 4 +- src/gui/widgets/timeline/timeline-state.cpp | 4 +- src/gui/widgets/timeline/timeline-state.hpp | 8 +-- src/gui/widgets/timeline/timeline-tool.hpp | 2 +- src/gui/widgets/timeline/timeline-track.cpp | 4 +- src/gui/widgets/timeline/timeline-track.hpp | 18 ++---- .../widgets/timeline/timeline-zoom-scale.cpp | 4 +- .../widgets/timeline/timeline-zoom-scale.hpp | 6 +- src/gui/window-manager.hpp | 3 +- .../proc/asset/orderingofassetstest.cpp | 2 +- tests/lib/factorytest.cpp | 6 +- 38 files changed, 173 insertions(+), 183 deletions(-) diff --git a/src/gui/gtk-lumiera.hpp b/src/gui/gtk-lumiera.hpp index 65ee00e9c..315ee4f2c 100644 --- a/src/gui/gtk-lumiera.hpp +++ b/src/gui/gtk-lumiera.hpp @@ -55,11 +55,15 @@ #include "gui/window-manager.hpp" #include <boost/noncopyable.hpp> +#include <tr1/memory> #include <vector> namespace gui { +using std::tr1::shared_ptr; + + /* ====== The Application Class ====== */ diff --git a/src/gui/model/clip-track.hpp b/src/gui/model/clip-track.hpp index e115ed415..45a067f60 100644 --- a/src/gui/model/clip-track.hpp +++ b/src/gui/model/clip-track.hpp @@ -28,7 +28,7 @@ #ifndef CLIP_TRACK_HPP #define CLIP_TRACK_HPP -#include "track.hpp" +#include "gui/model/track.hpp" #include "lib/observable-list.hpp" namespace gui { @@ -53,12 +53,12 @@ namespace model { /** * Gets the list of clips associated with this track. */ - lumiera::observable_list< std::tr1::shared_ptr<Clip> >& + lumiera::observable_list<shared_ptr<Clip> >& getClipList(void); private: - lumiera::observable_list< std::tr1::shared_ptr<Clip> > clips; + lumiera::observable_list<shared_ptr<Clip> > clips; }; diff --git a/src/gui/model/parent-track.cpp b/src/gui/model/parent-track.cpp index 3384227a1..45c8d13ae 100644 --- a/src/gui/model/parent-track.cpp +++ b/src/gui/model/parent-track.cpp @@ -23,7 +23,6 @@ #include "parent-track.hpp" #include <boost/foreach.hpp> -using namespace boost; using std::tr1::shared_ptr; namespace gui { @@ -33,13 +32,13 @@ ParentTrack::ParentTrack() { } -const std::list< std::tr1::shared_ptr<Track> >& +const std::list<shared_ptr<Track> >& ParentTrack::get_child_tracks() const { return tracks.get_list(); } -lumiera::observable_list< std::tr1::shared_ptr<Track> >& +lumiera::observable_list<shared_ptr<Track> >& ParentTrack::get_child_track_list() { return tracks; @@ -53,11 +52,11 @@ ParentTrack::can_host_children() const bool ParentTrack::remove_descendant_track( - const std::tr1::shared_ptr<Track> track) + const shared_ptr<Track> track) { REQUIRE(track); - std::tr1::shared_ptr<ParentTrack> parent = + shared_ptr<ParentTrack> parent = find_descendant_track_parent(track); if(parent) { @@ -68,10 +67,10 @@ ParentTrack::remove_descendant_track( return false; } -std::tr1::shared_ptr<ParentTrack> -ParentTrack::find_descendant_track_parent( - std::tr1::shared_ptr<Track> child) +shared_ptr<ParentTrack> +ParentTrack::find_descendant_track_parent(shared_ptr<Track> child) { + using namespace boost; REQUIRE(child != NULL); BOOST_FOREACH(std::tr1::shared_ptr<Track> track, tracks) diff --git a/src/gui/model/parent-track.hpp b/src/gui/model/parent-track.hpp index b23d2f07c..a1c709713 100644 --- a/src/gui/model/parent-track.hpp +++ b/src/gui/model/parent-track.hpp @@ -28,7 +28,7 @@ #ifndef PARENT_TRACK_HPP #define PARENT_TRACK_HPP -#include "track.hpp" +#include "gui/model/track.hpp" #include "lib/observable-list.hpp" namespace gui { @@ -53,13 +53,13 @@ public: /** * Gets a read-only reference to the the list of child tracks. */ - const std::list< std::tr1::shared_ptr<Track> >& + const std::list<shared_ptr<Track> >& get_child_tracks() const; /** * Gets read-write access to the list of child tracks. */ - lumiera::observable_list< std::tr1::shared_ptr<Track> >& + lumiera::observable_list<shared_ptr<Track> >& get_child_track_list(); /** @@ -75,7 +75,7 @@ public: * @param The model track to try and remove. * @return Returns true if the track was successfully removed. */ - bool remove_descendant_track(const std::tr1::shared_ptr<Track> track); + bool remove_descendant_track (const shared_ptr<Track> track); /** * A utility function that attempts to find the parent of a track by @@ -84,14 +84,14 @@ public: * @return Returns the parent track if one was found, or an empty * shared_ptr if none was found. */ - std::tr1::shared_ptr<ParentTrack> - find_descendant_track_parent(std::tr1::shared_ptr<Track> child); + shared_ptr<ParentTrack> + find_descendant_track_parent (shared_ptr<Track> child); protected: /** * The internal list of child tracks of this parent. */ - lumiera::observable_list< std::tr1::shared_ptr<Track> > tracks; + lumiera::observable_list<shared_ptr<Track> > tracks; }; } // namespace model diff --git a/src/gui/model/project.hpp b/src/gui/model/project.hpp index 29e5ca2a6..f2806abca 100644 --- a/src/gui/model/project.hpp +++ b/src/gui/model/project.hpp @@ -27,7 +27,7 @@ #ifndef PROJECT_HPP #define PROJECT_HPP -#include "sequence.hpp" +#include "gui/model/sequence.hpp" #include "lib/observable-list.hpp" namespace gui { @@ -40,14 +40,14 @@ public: ~Project(); - lumiera::observable_list< std::tr1::shared_ptr<Sequence> >& + lumiera::observable_list<shared_ptr<Sequence> >& get_sequences(); void add_new_sequence(uString name); private: - lumiera::observable_list< std::tr1::shared_ptr<Sequence> > sequences; + lumiera::observable_list<shared_ptr<Sequence> > sequences; }; } // namespace model diff --git a/src/gui/model/sequence.hpp b/src/gui/model/sequence.hpp index 45b9a43bf..db856d29e 100644 --- a/src/gui/model/sequence.hpp +++ b/src/gui/model/sequence.hpp @@ -27,7 +27,7 @@ #ifndef SEQUENCE_HPP #define SEQUENCE_HPP -#include "parent-track.hpp" +#include "gui/model/parent-track.hpp" namespace gui { namespace model { diff --git a/src/gui/model/track.cpp b/src/gui/model/track.cpp index 5e7b4dad7..c81cc9739 100644 --- a/src/gui/model/track.cpp +++ b/src/gui/model/track.cpp @@ -35,9 +35,10 @@ const list< shared_ptr<Track> > Track::NoChildren; Track::Track() : enabled(true), locked(false) -{ +{ } + +Track::~Track() { } -} const std::list< shared_ptr<Track> >& Track::get_child_tracks() const diff --git a/src/gui/model/track.hpp b/src/gui/model/track.hpp index 46f046b99..c7472b03f 100644 --- a/src/gui/model/track.hpp +++ b/src/gui/model/track.hpp @@ -47,6 +47,7 @@ protected: Track(); public: + virtual ~Track(); /// this is an interface /** * Returns true if this track can own any child tracks. @@ -57,7 +58,7 @@ public: /** * Gets the list of child tracks. */ - virtual const std::list< std::tr1::shared_ptr<Track> >& + virtual const std::list<shared_ptr<Track> >& get_child_tracks () const; /** @@ -106,8 +107,8 @@ public: * @return Returns the parent track if one was found, or an empty * shared_ptr if none was found. */ - virtual std::tr1::shared_ptr<ParentTrack> - find_descendant_track_parent (std::tr1::shared_ptr<Track> child); + virtual shared_ptr<ParentTrack> + find_descendant_track_parent (shared_ptr<Track> child); /** * A signal which fires when the enabled status changes. @@ -154,7 +155,7 @@ protected: * An object used internally as a return value for when there's no * children. */ - static const std::list< std::tr1::shared_ptr<Track> > NoChildren; + static const std::list< shared_ptr<Track> > NoChildren; /** * The internal implementation of print_branch. diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index f2ae2da56..7d5b4fa6c 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -130,8 +130,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager, zoomScale .set_tooltip_text(_("Adjust timeline zoom scale")); // Setup the timeline widget - shared_ptr<Sequence> sequence ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr instead of boost - = *get_project().get_sequences().begin(); + shared_ptr<Sequence> sequence = *(get_project().get_sequences().begin()); timelineWidget.reset(new TimelineWidget(load_state(sequence))); pack_start(*timelineWidget, PACK_EXPAND_WIDGET); diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp index 87c05425c..febcb857c 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -36,9 +36,15 @@ #include "lib/time/timevalue.hpp" +#include <tr1/memory> + using namespace gui::widgets; namespace gui { + +using std::tr1::shared_ptr; +using std::tr1::weak_ptr; + namespace model { class Sequence; @@ -49,6 +55,7 @@ namespace panels { using lib::time::Time; + /** * The definition of the timeline panel class, which holds timeline * widgets. @@ -123,8 +130,8 @@ private: void show_time (Time); - std::tr1::shared_ptr<widgets::timeline::TimelineState> - load_state (std::tr1::weak_ptr<model::Sequence> sequence); + shared_ptr<widgets::timeline::TimelineState> + load_state (weak_ptr<model::Sequence> sequence); private: @@ -144,7 +151,7 @@ private: * An invisible column which will be used to identify the sequence * of a row. */ - Gtk::TreeModelColumn< std::tr1::weak_ptr<model::Sequence> > + Gtk::TreeModelColumn< weak_ptr<model::Sequence> > sequenceColumn; /** @@ -169,8 +176,9 @@ private: // Body Widgets boost::scoped_ptr<TimelineWidget> timelineWidget; - std::map< std::tr1::weak_ptr<model::Sequence>, - std::tr1::shared_ptr<widgets::timeline::TimelineState> > + std::map< weak_ptr<model::Sequence> + , shared_ptr<widgets::timeline::TimelineState> + > timelineStates; // Toolbar Widgets diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index 4af85069a..c3442902f 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -44,18 +44,17 @@ const int TimelineWidget::HeaderIndentWidth = 10; const double TimelineWidget::ZoomIncrement = 1.25; const int64_t TimelineWidget::MaxScale = 30000000; // 30 Million -TimelineWidget::TimelineWidget( - std::tr1::shared_ptr<timeline::TimelineState> source_state) : - Table(2, 2), - layoutHelper(*this), - headerContainer(NULL), - body(NULL), - ruler(NULL), - horizontalAdjustment(0, 0, 0), - verticalAdjustment(0, 0, 0), - horizontalScroll(horizontalAdjustment), - verticalScroll(verticalAdjustment), - update_tracks_frozen(false) +TimelineWidget::TimelineWidget(shared_ptr<timeline::TimelineState> source_state) + : Table(2, 2) + , layoutHelper(*this) + , headerContainer(NULL) + , body(NULL) + , ruler(NULL) + , horizontalAdjustment(0, 0, 0) + , verticalAdjustment(0, 0, 0) + , horizontalScroll(horizontalAdjustment) + , verticalScroll(verticalAdjustment) + , update_tracks_frozen(false) { body = manage(new TimelineBody(*this)); ENSURE(body != NULL); diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index 6bf71ef0d..39901305a 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -64,17 +64,13 @@ class TimelineWidget : public Gtk::Table { public: /** - * Constructor * @param source_state The state that will be used as the data source * for this timeline widget. */ - TimelineWidget( - std::tr1::shared_ptr<timeline::TimelineState> source_state); + TimelineWidget (shared_ptr<timeline::TimelineState> source_state); - /** - * Destructor - */ - ~TimelineWidget(); + virtual ~TimelineWidget(); + /* ===== Data Access ===== */ public: @@ -84,13 +80,13 @@ public: * @return The state object that the timeline widget is currently * working with. */ - std::tr1::shared_ptr<timeline::TimelineState> get_state(); + shared_ptr<timeline::TimelineState> get_state(); /** * Replaces the current TimelineState object with another. * @param new_state The new state to swap in. */ - void set_state(std::tr1::shared_ptr<timeline::TimelineState> new_state); + void set_state(shared_ptr<timeline::TimelineState> new_state); /** * Zooms the view in or out as by a number of steps while keeping a @@ -110,13 +106,13 @@ public: */ void set_tool(timeline::ToolType tool_type); - std::tr1::shared_ptr<timeline::Track> + shared_ptr<timeline::Track> get_hovering_track() const; public: /* ===== Signals ===== */ - typedef sigc::signal<void, std::tr1::shared_ptr<timeline::TimelineState> > TimelineStateChangeSignal; - typedef sigc::signal<void, std::tr1::shared_ptr<timeline::Track> > HoveringTrackChangedSignal; + typedef sigc::signal<void, shared_ptr<timeline::TimelineState> > TimelineStateChangeSignal; + typedef sigc::signal<void, shared_ptr<timeline::Track> > HoveringTrackChangedSignal; sigc::signal<void, lib::time::Time> mouse_hover_signal() const; @@ -169,8 +165,8 @@ private: * already exist in trackMap. * @param list The parent track of the branch. */ - void create_timeline_tracks_from_branch( - std::tr1::shared_ptr<model::Track> modelTrack); + void + create_timeline_tracks_from_branch (shared_ptr<model::Track> modelTrack); /** * Creates a timeline UI track to correspond to a model track. @@ -178,9 +174,8 @@ private: * @return The timeline track created, or an empty shared_ptr if * modelTrack has an unreckognised type (this is an error condition). */ - std::tr1::shared_ptr<timeline::Track> - create_timeline_track_from_modelTrack( - std::tr1::shared_ptr<model::Track> modelTrack); + shared_ptr<timeline::Track> + create_timeline_track_from_modelTrack(shared_ptr<model::Track> modelTrack); /** * Removes any UI tracks which no longer have corresponding model @@ -188,10 +183,10 @@ private: */ void remove_orphaned_tracks(); - void search_orphaned_tracks_in_branch( - std::tr1::shared_ptr<model::Track> modelTrack, - std::map<std::tr1::shared_ptr<model::Track>, - std::tr1::shared_ptr<timeline::Track> > &orphan_track_map); + void + search_orphaned_tracks_in_branch (shared_ptr<model::Track> modelTrack, + std::map<shared_ptr<model::Track>, + shared_ptr<timeline::Track> > &orphan_track_map); /** * Looks up a timeline UI track in trackMap that corresponds to a @@ -201,8 +196,8 @@ private: * modelTrack has no corresponding timeline UI track (this is an * error condition). */ - std::tr1::shared_ptr<timeline::Track> lookup_timeline_track( - std::tr1::shared_ptr<model::Track> modelTrack) const; + shared_ptr<timeline::Track> + lookup_timeline_track (shared_ptr<model::Track> modelTrack) const; // ----- Layout Functions ----- // @@ -232,12 +227,12 @@ private: * Helper to get the sequence object from the state. * @return Returns a shared pointer to the sequence. */ - std::tr1::shared_ptr<model::Sequence> sequence() const; + shared_ptr<model::Sequence> sequence() const; // ----- Other Functions ----- // - void set_hovering_track( - std::tr1::shared_ptr<timeline::Track> hovering_track); + void + set_hovering_track (shared_ptr<timeline::Track> hovering_track); protected: @@ -245,7 +240,7 @@ protected: * The state that will be used as the data source for this timeline * widget. */ - std::tr1::shared_ptr<timeline::TimelineState> state; + shared_ptr<timeline::TimelineState> state; // Model Data @@ -256,11 +251,11 @@ protected: * widget is updated with update_tracks, timeline tracks are added and * removed from the map in correspondence with the tree. */ - std::map<std::tr1::shared_ptr<model::Track>, - std::tr1::shared_ptr<timeline::Track> > + std::map<shared_ptr<model::Track> + ,shared_ptr<timeline::Track> > trackMap; - std::tr1::shared_ptr<timeline::Track> hoveringTrack; + shared_ptr<timeline::Track> hoveringTrack; // Helper Classes timeline::TimelineLayoutHelper layoutHelper; diff --git a/src/gui/widgets/timeline/timeline-arrow-tool.cpp b/src/gui/widgets/timeline/timeline-arrow-tool.cpp index 420d3a964..8b2e0ad37 100644 --- a/src/gui/widgets/timeline/timeline-arrow-tool.cpp +++ b/src/gui/widgets/timeline/timeline-arrow-tool.cpp @@ -55,17 +55,17 @@ namespace timeline { Tool::on_button_press_event(event); // Convert the mouse click position to a Time - std::tr1::shared_ptr<TimelineState> state = timelineBody.getTimelineWidget().get_state(); + shared_ptr<TimelineState> state = timelineBody.getTimelineWidget().get_state(); REQUIRE(state); TimelineViewWindow const& window = state->get_view_window(); Time tpoint = window.x_to_time(mousePoint.get_x()); // Get the clip, if any - std::tr1::shared_ptr<timeline::Track> track = getHoveringTrack(); - std::tr1::shared_ptr<Clip> clip = track->getClipAt(tpoint); + shared_ptr<timeline::Track> track = getHoveringTrack(); + shared_ptr<Clip> clip = track->getClipAt(tpoint); // Nothing to do if there is no clip - if (clip == std::tr1::shared_ptr<Clip>()) + if (!clip) return; clip->setSelected(true); @@ -77,8 +77,7 @@ namespace timeline { REQUIRE (event != NULL); Tool::on_button_release_event(event); - std::tr1::shared_ptr<timeline::Track> track = - getHoveringTrack(); + shared_ptr<timeline::Track> track = getHoveringTrack(); } void @@ -92,10 +91,10 @@ namespace timeline { return; } - std::tr1::shared_ptr<timeline::Track> + shared_ptr<timeline::Track> ArrowTool::getHoveringTrack () { - std::tr1::shared_ptr<timeline::Track> track( + shared_ptr<timeline::Track> track( timelineBody.getTimelineWidget().get_hovering_track()); return track; } diff --git a/src/gui/widgets/timeline/timeline-arrow-tool.hpp b/src/gui/widgets/timeline/timeline-arrow-tool.hpp index c2f0df070..b390a25d5 100644 --- a/src/gui/widgets/timeline/timeline-arrow-tool.hpp +++ b/src/gui/widgets/timeline/timeline-arrow-tool.hpp @@ -81,7 +81,7 @@ namespace timeline { private: - std::tr1::shared_ptr<timeline::Track> + shared_ptr<timeline::Track> getHoveringTrack (); bool selectionRectangleActive; diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index dbc6697a1..25125f2cc 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -124,7 +124,7 @@ protected: /** * The event handler for when the TimelineWidget's state is switched. */ - void on_state_changed (std::tr1::shared_ptr<TimelineState> newState); + void on_state_changed (shared_ptr<TimelineState> newState); /* ===== Internals ===== */ private: @@ -142,8 +142,8 @@ private: void draw_tracks(Cairo::RefPtr<Cairo::Context> cr); void draw_track(Cairo::RefPtr<Cairo::Context> cr, - std::tr1::shared_ptr<timeline::Track> timeline_track, - const int view_width) const; + shared_ptr<timeline::Track> timeline_track, + const int view_width) const; /** * Draws the selected timeline period. @@ -200,7 +200,7 @@ private: Cairo::RefPtr<Cairo::SolidPattern> playbackPointColour; gui::widgets::TimelineWidget &timelineWidget; - std::tr1::shared_ptr<TimelineState> timelineState; + shared_ptr<TimelineState> timelineState; friend class Tool; diff --git a/src/gui/widgets/timeline/timeline-clip-track.cpp b/src/gui/widgets/timeline/timeline-clip-track.cpp index 9f5438d0a..f659aa1f8 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.cpp +++ b/src/gui/widgets/timeline/timeline-clip-track.cpp @@ -22,9 +22,9 @@ #include <boost/foreach.hpp> -#include "timeline-clip.hpp" -#include "timeline-clip-track.hpp" -#include "timeline-view-window.hpp" +#include "gui/widgets/timeline/timeline-clip.hpp" +#include "gui/widgets/timeline/timeline-clip-track.hpp" +#include "gui/widgets/timeline/timeline-view-window.hpp" using namespace Gtk; @@ -76,7 +76,7 @@ namespace timeline { } } - std::tr1::shared_ptr<timeline::Clip> + shared_ptr<timeline::Clip> ClipTrack::getClipAt(Time position) const { std::pair<shared_ptr<model::Clip>, shared_ptr<timeline::Clip> > @@ -88,7 +88,7 @@ namespace timeline { } // Nothing found - return std::tr1::shared_ptr<timeline::Clip>(); + return shared_ptr<timeline::Clip>(); } //// private methods @@ -98,7 +98,7 @@ namespace timeline { { // Share the draw strategy between all objects TODO("Use factory/builder to create Timline Clips"); - static std::tr1::shared_ptr<timeline::DrawStrategy> drawStrategy(new BasicDrawStrategy()); + static shared_ptr<timeline::DrawStrategy> drawStrategy(new BasicDrawStrategy()); BOOST_FOREACH (shared_ptr<model::Clip> modelClip, getModelTrack()->getClipList()) { // Is a timeline UI clip present in the map already? diff --git a/src/gui/widgets/timeline/timeline-clip-track.hpp b/src/gui/widgets/timeline/timeline-clip-track.hpp index 980c37480..32b93b321 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.hpp +++ b/src/gui/widgets/timeline/timeline-clip-track.hpp @@ -52,7 +52,7 @@ namespace timeline { * Constructor. */ ClipTrack(TimelineWidget &timelineWidget, - std::tr1::shared_ptr<model::ClipTrack> track); + shared_ptr<model::ClipTrack> track); /** * Draw the track in the timeline. @@ -66,7 +66,7 @@ namespace timeline { * pointer. * @param the given time */ - std::tr1::shared_ptr<timeline::Clip> + shared_ptr<timeline::Clip> getClipAt(Time position) const; private: @@ -80,7 +80,7 @@ namespace timeline { /** * Gets the modelTrack as a ClipTrack. */ - std::tr1::shared_ptr<model::ClipTrack> + shared_ptr<model::ClipTrack> getModelTrack (); /** @@ -107,8 +107,8 @@ namespace timeline { * The clipMap maps model clips to timeline widget clips which are responsible for the * UI representation of a clip. */ - std::map<std::tr1::shared_ptr<model::Clip>, - std::tr1::shared_ptr<timeline::Clip> > + std::map<shared_ptr<model::Clip>, + shared_ptr<timeline::Clip> > clipMap; }; diff --git a/src/gui/widgets/timeline/timeline-clip.cpp b/src/gui/widgets/timeline/timeline-clip.cpp index c6d90be45..1b4e04d72 100644 --- a/src/gui/widgets/timeline/timeline-clip.cpp +++ b/src/gui/widgets/timeline/timeline-clip.cpp @@ -27,11 +27,11 @@ namespace gui { namespace widgets { namespace timeline { - using std::tr1::shared_ptr; //////////////////////TICKET #796 + using std::tr1::shared_ptr; - Clip::Clip (std::tr1::shared_ptr<model::Clip> clip, - std::tr1::shared_ptr<timeline::DrawStrategy> drawStrategy) + Clip::Clip (shared_ptr<model::Clip> clip, + shared_ptr<timeline::DrawStrategy> drawStrategy) : Entity(drawStrategy) , modelClip(clip) , selected(false) diff --git a/src/gui/widgets/timeline/timeline-clip.hpp b/src/gui/widgets/timeline/timeline-clip.hpp index 001fb94a0..c7e6bd64b 100644 --- a/src/gui/widgets/timeline/timeline-clip.hpp +++ b/src/gui/widgets/timeline/timeline-clip.hpp @@ -49,8 +49,8 @@ namespace timeline { class Clip : public Entity { public: - Clip (std::tr1::shared_ptr<model::Clip> clip, - std::tr1::shared_ptr<timeline::DrawStrategy> drawStrategy); + Clip (shared_ptr<model::Clip> clip, + shared_ptr<timeline::DrawStrategy> drawStrategy); Time getBegin() const; @@ -62,7 +62,7 @@ namespace timeline { private: - std::tr1::shared_ptr<model::Clip> modelClip; + shared_ptr<model::Clip> modelClip; /** * True when this clip is selected in the GUI. diff --git a/src/gui/widgets/timeline/timeline-entity.cpp b/src/gui/widgets/timeline/timeline-entity.cpp index bc499b3d4..3f7582375 100644 --- a/src/gui/widgets/timeline/timeline-entity.cpp +++ b/src/gui/widgets/timeline/timeline-entity.cpp @@ -29,7 +29,7 @@ namespace gui { namespace widgets { namespace timeline { - Entity::Entity(std::tr1::shared_ptr<timeline::DrawStrategy> drawStrategy) + Entity::Entity (shared_ptr<timeline::DrawStrategy> drawStrategy) : enabled(true), drawStrategy(drawStrategy) { } diff --git a/src/gui/widgets/timeline/timeline-entity.hpp b/src/gui/widgets/timeline/timeline-entity.hpp index bfb170aee..f30a804a1 100644 --- a/src/gui/widgets/timeline/timeline-entity.hpp +++ b/src/gui/widgets/timeline/timeline-entity.hpp @@ -40,6 +40,7 @@ namespace widgets { namespace timeline { using lib::time::Time; + using std::tr1::shared_ptr; class DrawStrategy; @@ -52,7 +53,7 @@ namespace timeline { class Entity { protected: - Entity(std::tr1::shared_ptr<timeline::DrawStrategy> drawStrategy); + Entity (shared_ptr<timeline::DrawStrategy> drawStrategy); virtual ~Entity(); @@ -82,7 +83,7 @@ namespace timeline { bool enabled; - std::tr1::shared_ptr<timeline::DrawStrategy> drawStrategy; + shared_ptr<timeline::DrawStrategy> drawStrategy; }; } // namespace timeline diff --git a/src/gui/widgets/timeline/timeline-group-track.hpp b/src/gui/widgets/timeline/timeline-group-track.hpp index 822142b0d..6787c4184 100644 --- a/src/gui/widgets/timeline/timeline-group-track.hpp +++ b/src/gui/widgets/timeline/timeline-group-track.hpp @@ -26,7 +26,7 @@ #ifndef TIMELINE_GROUP_TRACK_HPP #define TIMELINE_GROUP_TRACK_HPP -#include "timeline-track.hpp" +#include "gui/widgets/timeline/timeline-track.hpp" #include "gui/model/group-track.hpp" namespace gui { @@ -36,19 +36,16 @@ namespace timeline { class GroupTrack : public timeline::Track { public: - GroupTrack(TimelineWidget &timeline_widget, - std::tr1::shared_ptr<model::GroupTrack> track); + GroupTrack (TimelineWidget &timeline_widget, + shared_ptr<model::GroupTrack> track); - void draw_track(Cairo::RefPtr<Cairo::Context> cairo, - TimelineViewWindow* constwindow) - const; + void draw_track (Cairo::RefPtr<Cairo::Context> cairo, + TimelineViewWindow* constwindow) const; protected: void on_child_list_changed(); }; -} // namespace timeline -} // namespace widgets -} // namespace gui +}}} // namespace gui::widgets::timeline #endif // TIMELINE_GROUP_TRACK_HPP diff --git a/src/gui/widgets/timeline/timeline-header-container.cpp b/src/gui/widgets/timeline/timeline-header-container.cpp index 486859845..1d1af9537 100644 --- a/src/gui/widgets/timeline/timeline-header-container.cpp +++ b/src/gui/widgets/timeline/timeline-header-container.cpp @@ -342,12 +342,9 @@ TimelineHeaderContainer::on_scroll() } void -TimelineHeaderContainer::on_hovering_track_changed( - std::tr1::shared_ptr<timeline::Track> hovering_track) +TimelineHeaderContainer::on_hovering_track_changed( shared_ptr<timeline::Track>) { - (void)hovering_track; - - + /* do nothing */ } bool diff --git a/src/gui/widgets/timeline/timeline-header-container.hpp b/src/gui/widgets/timeline/timeline-header-container.hpp index 6d40d621e..6c32d6ce4 100644 --- a/src/gui/widgets/timeline/timeline-header-container.hpp +++ b/src/gui/widgets/timeline/timeline-header-container.hpp @@ -139,8 +139,7 @@ private: */ void on_scroll(); - void on_hovering_track_changed( - std::tr1::shared_ptr<timeline::Track> hovering_track); + void on_hovering_track_changed(shared_ptr<timeline::Track> hovering_track); private: /* ===== Internal Event Handlers ===== */ @@ -168,9 +167,8 @@ private: * to control the amount of indention. * @param offset The vertical offset of the headers in pixels. */ - void draw_header_decoration( - std::tr1::shared_ptr<model::Track> modelTrack, - const Gdk::Rectangle &clip_rect); + void draw_header_decoration(shared_ptr<model::Track> modelTrack, + const Gdk::Rectangle &clip_rect); /** * A helper function which calls lookup_timeline_track within the @@ -182,8 +180,8 @@ private: * @remarks If the return value is going to be NULL, an ENSURE will * fail. */ - std::tr1::shared_ptr<timeline::Track> lookup_timeline_track( - std::tr1::shared_ptr<model::Track> modelTrack); + shared_ptr<timeline::Track> + lookup_timeline_track (shared_ptr<model::Track> modelTrack); void begin_drag(); @@ -248,7 +246,7 @@ private: int scrollSlideRate; //----- User Interaction State -----// - std::tr1::shared_ptr<timeline::Track> hoveringTrack; + shared_ptr<timeline::Track> hoveringTrack; Gdk::Point mousePoint; diff --git a/src/gui/widgets/timeline/timeline-layout-helper.cpp b/src/gui/widgets/timeline/timeline-layout-helper.cpp index 71a55d44a..df00dd131 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.cpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.cpp @@ -112,8 +112,8 @@ TimelineLayoutHelper::header_from_point(Gdk::Point point) return shared_ptr<timeline::Track>(); } -std::tr1::shared_ptr<timeline::Track> -TimelineLayoutHelper::track_from_y(int y) +shared_ptr<timeline::Track> +TimelineLayoutHelper::track_from_y (int y) { // Apply the scroll offset y += timelineWidget.get_y_scroll_offset(); @@ -299,8 +299,7 @@ TimelineLayoutHelper::is_animating() const } TimelineLayoutHelper::TrackTree::pre_order_iterator -TimelineLayoutHelper::iterator_from_track( - std::tr1::shared_ptr<model::Track> modelTrack) +TimelineLayoutHelper::iterator_from_track(shared_ptr<model::Track> modelTrack) { REQUIRE(modelTrack); @@ -666,7 +665,7 @@ TimelineLayoutHelper::apply_drop_to_modelTree( timelineWidget.thaw_update_tracks(); } -std::tr1::shared_ptr<model::Sequence> +shared_ptr<model::Sequence> TimelineLayoutHelper::get_sequence() const { REQUIRE(timelineWidget.state); diff --git a/src/gui/widgets/timeline/timeline-layout-helper.hpp b/src/gui/widgets/timeline/timeline-layout-helper.hpp index 07e792e9a..2c2cca44a 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.hpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.hpp @@ -114,8 +114,7 @@ public: * tracks. * @see update_layout() */ - std::tr1::shared_ptr<timeline::Track> header_from_point( - Gdk::Point point); + shared_ptr<timeline::Track> header_from_point (Gdk::Point point); /** * Searches for a tack which has the specified y-offset inside of it. @@ -128,15 +127,15 @@ public: * tracks. * @see update_layout() */ - std::tr1::shared_ptr<timeline::Track> track_from_y(int y); + shared_ptr<timeline::Track> track_from_y (int y); /** * Begins to drag the track under mouse_point, if there is one. * @param mouse_point The mouse point to begin dragging from, measured * in pixels from the top left of the header container widget. */ - std::tr1::shared_ptr<timeline::Track> - begin_dragging_track(const Gdk::Point &mouse_point); + shared_ptr<timeline::Track> + begin_dragging_track (Gdk::Point const& mouse_point); /** * Drops the dragging track. @@ -185,8 +184,8 @@ public: * @return Returns the model iterator of layoutTree.end() if no * iterator was found. */ - TrackTree::pre_order_iterator iterator_from_track( - std::tr1::shared_ptr<model::Track> modelTrack); + TrackTree::pre_order_iterator + iterator_from_track (shared_ptr<model::Track> modelTrack); /** * A function that recursively calculates the visible height of a @@ -262,7 +261,7 @@ protected: * @see clone_tree_from_sequence() */ void add_branch(TrackTree::iterator_base parent_iterator, - std::tr1::shared_ptr<model::Track> parent); + shared_ptr<model::Track> parent); /** * Recursively calculates the boxes for a given branch in the timeline @@ -298,8 +297,8 @@ protected: * @remarks If the return value is going to be NULL, an ENSURE will * fail. */ - std::tr1::shared_ptr<timeline::Track> lookup_timeline_track( - std::tr1::shared_ptr<model::Track> modelTrack); + shared_ptr<timeline::Track> + lookup_timeline_track(shared_ptr<model::Track> modelTrack); /** * A helper function which kicks off the animation timer. @@ -343,7 +342,8 @@ protected: * Helper to get the sequence object from the state. * @return Returns a shared pointer to the sequence. */ - std::tr1::shared_ptr<model::Sequence> get_sequence() const; + shared_ptr<model::Sequence> + get_sequence() const; protected: /** diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp index 0b8d68f22..b8725aedf 100644 --- a/src/gui/widgets/timeline/timeline-ruler.cpp +++ b/src/gui/widgets/timeline/timeline-ruler.cpp @@ -36,7 +36,7 @@ using namespace gui; using namespace gui::widgets; using namespace gui::widgets::timeline; -using std::tr1::shared_ptr; ////////////////////TICKET #796 +using std::tr1::shared_ptr; using gui::util::CairoUtil; using lib::time::Time; using lib::time::TimeVar; diff --git a/src/gui/widgets/timeline/timeline-ruler.hpp b/src/gui/widgets/timeline/timeline-ruler.hpp index 842ce6bff..3d903aaf5 100644 --- a/src/gui/widgets/timeline/timeline-ruler.hpp +++ b/src/gui/widgets/timeline/timeline-ruler.hpp @@ -111,7 +111,7 @@ private: /** * The event handler for when the TimelineWidget's state is switched. */ - void on_state_changed (std::tr1::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr + void on_state_changed (shared_ptr<TimelineState> newState); private: /* ===== Internal Methods ===== */ @@ -242,7 +242,7 @@ private: /** * the currently active timeline state object */ - std::tr1::shared_ptr<TimelineState> timelineState; + shared_ptr<TimelineState> timelineState; /** * The caches image of the ruler, over which the chevrons etc. will diff --git a/src/gui/widgets/timeline/timeline-state.cpp b/src/gui/widgets/timeline/timeline-state.cpp index b55589956..74042ef39 100644 --- a/src/gui/widgets/timeline/timeline-state.cpp +++ b/src/gui/widgets/timeline/timeline-state.cpp @@ -40,7 +40,7 @@ using std::tr1::shared_ptr; -TimelineState::TimelineState (std::tr1::shared_ptr<model::Sequence> source_sequence) +TimelineState::TimelineState (shared_ptr<model::Sequence> source_sequence) : sequence(source_sequence) , viewWindow(Offset(Time::ZERO), 1) , selection_(Time::ZERO, Duration::NIL) @@ -60,7 +60,7 @@ TimelineState::TimelineState (std::tr1::shared_ptr<model::Sequence> source_seque //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all } -std::tr1::shared_ptr<model::Sequence> +shared_ptr<model::Sequence> TimelineState::get_sequence() const { return sequence; diff --git a/src/gui/widgets/timeline/timeline-state.hpp b/src/gui/widgets/timeline/timeline-state.hpp index 23e2015e7..5542b1520 100644 --- a/src/gui/widgets/timeline/timeline-state.hpp +++ b/src/gui/widgets/timeline/timeline-state.hpp @@ -29,7 +29,6 @@ #include "gui/widgets/timeline/timeline-view-window.hpp" #include "lib/time/mutation.hpp" -#include <boost/shared_ptr.hpp> /////////////////////////TICKET #796 namespace gui { @@ -52,11 +51,10 @@ class TimelineState public: /** - * Constructor * @param source_sequence The sequence on which the TimelineWidget * will operate when this TimelineState is attached. */ - TimelineState(std::tr1::shared_ptr<model::Sequence> source_sequence); + TimelineState (shared_ptr<model::Sequence> source_sequence); public: @@ -64,7 +62,7 @@ public: * Gets the sequence that is attached to this timeline state object. * @return Returns a shared_ptr to the sequence object. */ - std::tr1::shared_ptr<model::Sequence> get_sequence() const; + shared_ptr<model::Sequence> get_sequence() const; /** * Gets a reference to the timeline view window object. @@ -122,7 +120,7 @@ private: * @remarks This pointer is set by the constructor and is constant, so * will not change in value during the lifetime of the class. */ - std::tr1::shared_ptr<model::Sequence> sequence; + shared_ptr<model::Sequence> sequence; // View State /** diff --git a/src/gui/widgets/timeline/timeline-tool.hpp b/src/gui/widgets/timeline/timeline-tool.hpp index f34cb5d32..7c54a36a5 100644 --- a/src/gui/widgets/timeline/timeline-tool.hpp +++ b/src/gui/widgets/timeline/timeline-tool.hpp @@ -133,7 +133,7 @@ protected: /** * A helper function to get the state */ - std::tr1::shared_ptr<TimelineState> get_state() const; + shared_ptr<TimelineState> get_state() const; /** * A helper function to get the view window diff --git a/src/gui/widgets/timeline/timeline-track.cpp b/src/gui/widgets/timeline/timeline-track.cpp index b0267dea3..ac1c6e4d1 100644 --- a/src/gui/widgets/timeline/timeline-track.cpp +++ b/src/gui/widgets/timeline/timeline-track.cpp @@ -125,7 +125,7 @@ shared_ptr<timeline::Clip> Track::getClipAt(Time) const { // Default implementation returns empty pointer - return std::tr1::shared_ptr<timeline::Clip>(); + return shared_ptr<timeline::Clip>(); } void @@ -272,7 +272,7 @@ void Track::on_remove_track() { REQUIRE(modelTrack); - std::tr1::shared_ptr<TimelineState> state = timelineWidget.get_state(); + shared_ptr<TimelineState> state = timelineWidget.get_state(); REQUIRE(state); state->get_sequence()->remove_descendant_track(modelTrack); diff --git a/src/gui/widgets/timeline/timeline-track.hpp b/src/gui/widgets/timeline/timeline-track.hpp index 796b1f130..dc7b18129 100644 --- a/src/gui/widgets/timeline/timeline-track.hpp +++ b/src/gui/widgets/timeline/timeline-track.hpp @@ -72,20 +72,14 @@ public: Collapse }; - /** - * Constructor - */ Track(TimelineWidget &timeline_widget, - std::tr1::shared_ptr<model::Track> track); - - /** - * Destructor - */ - ~Track(); + shared_ptr<model::Track> track); + + virtual ~Track(); Gtk::Widget& get_header_widget(); - std::tr1::shared_ptr<model::Track> //////////////////////////////TICKET #796 : should use std::tr1 + shared_ptr<model::Track> getModelTrack() const; /** @@ -155,7 +149,7 @@ public: * The default implementation simply returns an empty pointer. * @param the given time */ - virtual std::tr1::shared_ptr<timeline::Clip> //////////////////////////////TICKET #796 : should use std::tr1 + virtual shared_ptr<timeline::Clip> getClipAt (Time position) const; private: @@ -209,7 +203,7 @@ private: protected: TimelineWidget &timelineWidget; - std::tr1::shared_ptr<model::Track> modelTrack; + shared_ptr<model::Track> modelTrack; private: /** diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp index 083513647..7452d20cd 100644 --- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp +++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp @@ -90,7 +90,7 @@ TimelineZoomScale::TimelineZoomScale() } void -TimelineZoomScale::wireTimelineState (std::tr1::shared_ptr<TimelineState> currentState, +TimelineZoomScale::wireTimelineState (shared_ptr<TimelineState> currentState, TimelineWidget::TimelineStateChangeSignal stateChangeSignal) { on_timeline_state_changed (currentState); @@ -98,7 +98,7 @@ TimelineZoomScale::wireTimelineState (std::tr1::shared_ptr<TimelineState> curren } void -TimelineZoomScale::on_timeline_state_changed (std::tr1::shared_ptr<TimelineState> newState) +TimelineZoomScale::on_timeline_state_changed (shared_ptr<TimelineState> newState) { REQUIRE (newState); timelineState = newState; diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.hpp b/src/gui/widgets/timeline/timeline-zoom-scale.hpp index ec106ad94..a57fdd6df 100644 --- a/src/gui/widgets/timeline/timeline-zoom-scale.hpp +++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp @@ -51,7 +51,7 @@ public: */ sigc::signal<void, double> signal_zoom(); - void wireTimelineState (std::tr1::shared_ptr<TimelineState> currentState, + void wireTimelineState (shared_ptr<TimelineState> currentState, TimelineWidget::TimelineStateChangeSignal); private: @@ -61,7 +61,7 @@ private: * Update the slider position when the timeline state * is changed. */ - void on_timeline_state_changed (std::tr1::shared_ptr<TimelineState> newState); ////////////////////TICKET #796 : should use std::tr1::shared_ptr + void on_timeline_state_changed (shared_ptr<TimelineState> newState); /** * Event handler for when the zoomIn Button @@ -96,7 +96,7 @@ private: const double button_step_size; - std::tr1::shared_ptr<TimelineState> timelineState; + shared_ptr<TimelineState> timelineState; }; } // namespace gui diff --git a/src/gui/window-manager.hpp b/src/gui/window-manager.hpp index 1f0eb3a46..e06b0f4ee 100644 --- a/src/gui/window-manager.hpp +++ b/src/gui/window-manager.hpp @@ -46,6 +46,7 @@ namespace gui { using std::string; +using std::tr1::shared_ptr; namespace model { class Project; } @@ -194,7 +195,7 @@ private: private: - std::list< std::tr1::shared_ptr<workspace::WorkspaceWindow> > windowList; + std::list<shared_ptr<workspace::WorkspaceWindow> > windowList; public: diff --git a/tests/components/proc/asset/orderingofassetstest.cpp b/tests/components/proc/asset/orderingofassetstest.cpp index cff898acd..130b55bfe 100644 --- a/tests/components/proc/asset/orderingofassetstest.cpp +++ b/tests/components/proc/asset/orderingofassetstest.cpp @@ -52,7 +52,7 @@ namespace asset { * or resort to template metaprogramming tricks. * Just providing templated comparison operators * would generally override the behaviour of - * boost::shared_ptr, which is not desirable. + * std::shared_ptr, which is not desirable. * @see Asset::Ident#compare */ class OrderingOfAssets_test : public Test diff --git a/tests/lib/factorytest.cpp b/tests/lib/factorytest.cpp index 8e46771c2..dbf6dddb4 100644 --- a/tests/lib/factorytest.cpp +++ b/tests/lib/factorytest.cpp @@ -61,7 +61,7 @@ namespace test{ /** Test-Factory specialised to create TargetObj instances * using the 1-argument constructor TargetObj::TargetObj(int). - * It will create boost::shared_ptr instances, because + * It will create std::shared_ptr instances, because * factory::RefcountFac was parametrised with this smart pointer type. */ class ObjFactory : public factory::RefcountFac<TargetObj> @@ -79,7 +79,7 @@ namespace test{ /** shorthand for the created smart-pointer class, - * here it's a (refcounting) boost::shared_ptr + * here it's a (refcounting) std::shared_ptr */ typedef ObjFactory::PType pTarget; @@ -93,7 +93,7 @@ namespace test{ /******************************************************************* * @test the basic object creation Factory behaviour: We declared * a static field TargetObj::create to be a ObjFactory. So, - * by invoking this functor, we get a boost::shared_ptr + * by invoking this functor, we get a std::shared_ptr * wrapping a new TargetObj instance. From this we copy * further shared-ptrs, invoke a member function and * finally, when leaving the scope, our TargetObj From 16e41852bad5d5f80cccd24d308a6b0b2c645cbc Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 22 Oct 2011 05:14:33 +0200 Subject: [PATCH 217/496] mention the TiddlyWiki on the documentation root page --- doc/index.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/index.txt b/doc/index.txt index 60dbc239d..5037b84fa 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -17,6 +17,15 @@ Programming-oriented details about the functional layers of Lumiera as well as a * link:technical/index.html[Technical Documents] +=== the TiddlyWiki === +Currently, Lumiera is still in the design- and evolution phase. +There is an embedded JavaScript wiki (TiddlyWiki) within the source tree, mostly +used as design notebook, featuring day-to-day design sketches, notes but also +quite some more persistent planning. Finished documentation text is constantly +moved over to the documentation section(s) of the Lumiera website. + +-> access the Proy-Layer link:http://lumiera.org/wiki/renderengine.html[TiddlyWiki online here] + == Media and Presentations == From 606251749e943d49e96df39124a0e0054be8e643 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 22 Oct 2011 21:20:44 +0200 Subject: [PATCH 218/496] fix time::Control should properly include <tr1/functional> --- src/lib/time/control-impl.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/time/control-impl.hpp b/src/lib/time/control-impl.hpp index 1d2ab6d94..c950724d7 100644 --- a/src/lib/time/control-impl.hpp +++ b/src/lib/time/control-impl.hpp @@ -57,6 +57,7 @@ #include "lib/time/mutation.hpp" #include "lib/time/timevalue.hpp" +#include <tr1/functional> #include <vector> @@ -64,6 +65,8 @@ namespace lib { namespace time { namespace mutation { + using std::tr1::function; + From 4f1d7e1d3fb84e43bed9de4b298c95fbb3390bb9 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 22 Oct 2011 21:21:40 +0200 Subject: [PATCH 219/496] clean-up GUI includes. Solving problems with tr1::ref vs. boost::ref --- src/gui/panels/panel.hpp | 3 ++- src/gui/panels/viewer-panel.cpp | 4 ++-- src/gui/panels/viewer-panel.hpp | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/gui/panels/panel.hpp b/src/gui/panels/panel.hpp index 1a9801250..a6f508403 100644 --- a/src/gui/panels/panel.hpp +++ b/src/gui/panels/panel.hpp @@ -27,11 +27,12 @@ #ifndef PANEL_HPP #define PANEL_HPP -#include <gdl/gdl-dock-item.h> #include "gui/gtk-lumiera.hpp" #include "gui/widgets/panel-bar.hpp" +#include <gdl/gdl-dock-item.h> + namespace gui { namespace workspace { diff --git a/src/gui/panels/viewer-panel.cpp b/src/gui/panels/viewer-panel.cpp index 692f82308..955b92e3e 100644 --- a/src/gui/panels/viewer-panel.cpp +++ b/src/gui/panels/viewer-panel.cpp @@ -21,13 +21,13 @@ * *****************************************************/ #include "gui/gtk-lumiera.hpp" +#include "gui/panels/viewer-panel.hpp" +#include "gui/workspace/workspace-window.hpp" #include "gui/controller/controller.hpp" #include "gui/controller/playback-controller.hpp" #include "gui/display-service.hpp" -#include "gui/workspace/workspace-window.hpp" -#include "viewer-panel.hpp" using namespace Gtk; using namespace gui::widgets; diff --git a/src/gui/panels/viewer-panel.hpp b/src/gui/panels/viewer-panel.hpp index ae41516c1..7fe5cace3 100644 --- a/src/gui/panels/viewer-panel.hpp +++ b/src/gui/panels/viewer-panel.hpp @@ -26,11 +26,12 @@ #ifndef VIEWER_PANEL_HPP #define VIEWER_PANEL_HPP -#include <gtkmm.h> -#include "panel.hpp" +#include "gui/panels/panel.hpp" #include "gui/widgets/video-display-widget.hpp" +#include <gtkmm.h> + namespace gui { namespace panels { From b39edad306c3f5fe35d712ed5595200813c89a66 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 22 Oct 2011 21:19:57 +0200 Subject: [PATCH 220/496] clean-up some additional mentions of boost::ref --- src/lib/wrapper.hpp | 4 ++-- tests/components/proc/control/command-use2-test.cpp | 2 +- .../proc/mobject/session/session-modify-parts-test.cpp | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib/wrapper.hpp b/src/lib/wrapper.hpp index 8a7761263..d1065612f 100644 --- a/src/lib/wrapper.hpp +++ b/src/lib/wrapper.hpp @@ -64,7 +64,7 @@ namespace wrapper { /** - * Extension to boost::reference_wrapper: + * Extension to std::reference_wrapper: * Allows additionally to re-bind to another reference, * almost like a pointer. Helpful for building templates. * @warning potentially dangerous @@ -128,7 +128,7 @@ namespace wrapper { * A copyable, assignable value object, not managing ownership. * It can be default constructed and \c bool evaluated to detect * an empty holder. The value is retrieved pointer-like, by - * explicit dereferentiation. (Contrast this to boost::ref, + * explicit dereferentiation. (Contrast this to std::ref, * where the original reference is retrieved by conversion) * * The purpose of this template is to be able to remember diff --git a/tests/components/proc/control/command-use2-test.cpp b/tests/components/proc/control/command-use2-test.cpp index 29d5c6c90..8fec714fa 100644 --- a/tests/components/proc/control/command-use2-test.cpp +++ b/tests/components/proc/control/command-use2-test.cpp @@ -46,7 +46,7 @@ namespace test { using boost::str; using std::tr1::function; using std::tr1::bind; - using boost::ref; + using std::tr1::ref; using boost::lexical_cast; using util::contains; diff --git a/tests/components/proc/mobject/session/session-modify-parts-test.cpp b/tests/components/proc/mobject/session/session-modify-parts-test.cpp index 481e6f136..6d04accdd 100644 --- a/tests/components/proc/mobject/session/session-modify-parts-test.cpp +++ b/tests/components/proc/mobject/session/session-modify-parts-test.cpp @@ -34,11 +34,10 @@ #include "lib/query.hpp" #include <tr1/functional> -#include <boost/ref.hpp> #include <iostream> #include <set> -using boost::ref; +using std::tr1::ref; using std::tr1::placeholders::_1; using util::isSameObject; using util::and_all; From 6046749f190ef07e9bab77e3b4349a04c6a1c38a Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Sat, 22 Oct 2011 04:29:55 -0500 Subject: [PATCH 221/496] Integrating time::Control into ibeam tool. --- src/gui/widgets/timeline-widget.cpp | 4 ++-- src/gui/widgets/timeline/timeline-body.cpp | 6 ++++++ src/gui/widgets/timeline/timeline-body.hpp | 4 ++++ src/gui/widgets/timeline/timeline-ibeam-tool.cpp | 7 ++----- src/gui/widgets/timeline/timeline-ibeam-tool.hpp | 7 ++++++- src/gui/widgets/timeline/timeline-state.hpp | 2 +- 6 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index c3442902f..52008f0cc 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -62,7 +62,7 @@ TimelineWidget::TimelineWidget(shared_ptr<timeline::TimelineState> source_state) ENSURE(headerContainer != NULL); ruler = manage(new TimelineRuler(*this)); ENSURE(ruler != NULL); - + horizontalAdjustment.signal_value_changed().connect( sigc::mem_fun( this, &TimelineWidget::on_scroll) ); verticalAdjustment.signal_value_changed().connect( sigc::mem_fun( @@ -71,7 +71,7 @@ TimelineWidget::TimelineWidget(shared_ptr<timeline::TimelineState> source_state) this, &TimelineWidget::on_motion_in_body_notify_event) ); update_tracks(); - + attach(*body, 1, 2, 1, 2, FILL|EXPAND, FILL|EXPAND); attach(*ruler, 1, 2, 0, 1, FILL|EXPAND, SHRINK); attach(*headerContainer, 0, 1, 1, 2, SHRINK, FILL|EXPAND); diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index d546ada65..369b894ed 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -82,6 +82,12 @@ TimelineBody::getTimelineWidget () const return timelineWidget; } +TimeSpan +TimelineBody::get_selection() +{ + return timelineWidget.get_state()->get_selection(); +} + ToolType TimelineBody::get_tool() const { diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index 25125f2cc..16b3b13e7 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -46,6 +46,7 @@ class TimelineWidget; namespace timeline { using lib::time::TimeVar; +using lib::time::TimeSpan; class Track; @@ -73,6 +74,9 @@ public: TimelineWidget& getTimelineWidget () const; + TimeSpan + get_selection(); + /** * Returns the type of the currently selected timeline tool. */ diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp index 71aeef656..c8becb870 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp @@ -24,8 +24,6 @@ #include "gui/widgets/timeline-widget.hpp" #include "lib/time/mutation.hpp" -#include <boost/shared_ptr.hpp> - using namespace gui::widgets; using lib::time::Mutation; @@ -45,12 +43,11 @@ const int IBeamTool::ScrollSlideEventInterval = 40; IBeamTool::IBeamTool(TimelineBody &timeline_body) : Tool(timeline_body), + selectionControl(), dragType(None), pinnedDragTime(), scrollSlideRate(0) -{ - -} +{ } IBeamTool::~IBeamTool() { diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.hpp b/src/gui/widgets/timeline/timeline-ibeam-tool.hpp index 291305cde..22cf6862f 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.hpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.hpp @@ -30,14 +30,17 @@ #include "gui/widgets/timeline/timeline-tool.hpp" #include "lib/time/timevalue.hpp" +#include "lib/time/timequant.hpp" +#include "lib/time/control.hpp" -#include <gtkmm.h> namespace gui { namespace widgets { namespace timeline { using lib::time::TimeVar; +using lib::time::TimeSpan; +using lib::time::Control; /** * A helper class to implement the timeline i-beam tool @@ -159,6 +162,8 @@ private: }; /* ==== Internals ===== */ + Control<TimeSpan> selectionControl; + /** * Specifies the type of drag currently taking place. */ diff --git a/src/gui/widgets/timeline/timeline-state.hpp b/src/gui/widgets/timeline/timeline-state.hpp index 5542b1520..6446e7790 100644 --- a/src/gui/widgets/timeline/timeline-state.hpp +++ b/src/gui/widgets/timeline/timeline-state.hpp @@ -70,7 +70,7 @@ public: */ timeline::TimelineViewWindow& get_view_window(); - + TimeSpan get_selection() const { return selection_; } Time getSelectionStart() const { return selection_.start();} Time getSelectionEnd() const { return selection_.end(); } Time getPlaybackPeriodStart() const { return selection_.start();} From ca634bdd9ca34b2db54e3789432d7019f63f5af6 Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Thu, 13 Oct 2011 14:55:43 -0500 Subject: [PATCH 222/496] House Cleaning --- src/gui/model/clip.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/model/clip.cpp b/src/gui/model/clip.cpp index 2b7dc6cd6..f78e2c2af 100644 --- a/src/gui/model/clip.cpp +++ b/src/gui/model/clip.cpp @@ -32,7 +32,7 @@ namespace gui { namespace model { Clip::Clip() - : timeCoord_(Time(FSecs(1)), FSecs(2)) + : timeCoord_(Time(FSecs(1)), FSecs(3)) { } @@ -40,14 +40,14 @@ namespace model { Clip::setBegin (Time newStartTime) { timeCoord_.accept (Mutation::changeTime (newStartTime)); - // TODO: emit signal + TODO("Emit A Signal"); } void Clip::setDuration (Duration newLength) { timeCoord_.accept (Mutation::changeDuration(newLength)); - // TODO: emit signal + TODO("Emit A Signal"); } void From c9671b654e8b19ac9fc2d49afb17836eff05d50e Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Sat, 22 Oct 2011 19:03:17 -0500 Subject: [PATCH 223/496] time::Control Integration First Commit --- .../widgets/timeline/timeline-ibeam-tool.cpp | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp index c8becb870..7672bcf01 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp @@ -47,7 +47,10 @@ IBeamTool::IBeamTool(TimelineBody &timeline_body) : dragType(None), pinnedDragTime(), scrollSlideRate(0) -{ } +{ + // Connect the timlinebody selection to the selectionControl + this->get_state()->get_selection().accept(selectionControl); +} IBeamTool::~IBeamTool() { @@ -117,9 +120,14 @@ IBeamTool::on_button_press_event(GdkEventButton* event) // User began the drag in clear space, begin a Select drag dragType = Selection; pinnedDragTime = time; - state->setSelection (Mutation::changeTime(time)); - state->setSelection (Mutation::changeDuration(Duration::NIL)); - //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all + selectionControl (TimeSpan(time, Duration::NIL)); + state->selection_changed_signal().emit(); + std::cout << "\n" << std::string(time) << "\n"; + std::cout << std::string(state->get_selection().start()) << "\n\n"; + + //state->setSelection (Mutation::changeTime (time)); + //state->setSelection (Mutation::changeDuration (Duration::NIL)); + ////////////"//////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all } } } @@ -193,12 +201,14 @@ IBeamTool::set_leading_x(const int x) const bool set_playback_period = dragType == Selection; TimeVar newStartPoint (state->get_view_window().x_to_time(x)); Offset selectionLength (pinnedDragTime, newStartPoint); - + if (newStartPoint > pinnedDragTime) newStartPoint=pinnedDragTime; // use the smaller one as selection start - - state->setSelection (Mutation::changeTime(newStartPoint) , set_playback_period); - state->setSelection (Mutation::changeDuration(selectionLength), set_playback_period); + + // selectionControl (TimeSpan(newStartPoint, Duration(selectionLength))); + + //state->setSelection (Mutation::changeTime(newStartPoint) , set_playback_period); + // state->setSelection (Mutation::changeDuration(selectionLength), set_playback_period); //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once } From 4acc9d247a6fec161fdcd6c8e4d6db9550c43d20 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 23 Oct 2011 02:41:57 +0200 Subject: [PATCH 224/496] maybe fix: try to connect the selection control the other way round --- src/gui/widgets/timeline/timeline-ibeam-tool.cpp | 3 ++- src/gui/widgets/timeline/timeline-state.hpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp index 7672bcf01..59d5345be 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp @@ -49,11 +49,12 @@ IBeamTool::IBeamTool(TimelineBody &timeline_body) : scrollSlideRate(0) { // Connect the timlinebody selection to the selectionControl - this->get_state()->get_selection().accept(selectionControl); + this->get_state()->setSelection (selectionControl, false); } IBeamTool::~IBeamTool() { + selectionControl.disconnect(); end_scroll_slide(); } diff --git a/src/gui/widgets/timeline/timeline-state.hpp b/src/gui/widgets/timeline/timeline-state.hpp index 6446e7790..0d0b02a32 100644 --- a/src/gui/widgets/timeline/timeline-state.hpp +++ b/src/gui/widgets/timeline/timeline-state.hpp @@ -78,6 +78,7 @@ public: Time getPlaybackPoint() const { return playbackPoint_; } + /** is there currently any ongoing playback process? * Otherwise the #getPlaybackPoint is meaningless */ bool isPlaying() const { return isPlayback_; } From 0378f9266e5e910aefdd81d5330973222d6d7734 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 23 Oct 2011 02:42:25 +0200 Subject: [PATCH 225/496] fix misspelling in library --- src/lib/time/control-impl.hpp | 2 +- src/lib/time/control.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/time/control-impl.hpp b/src/lib/time/control-impl.hpp index c950724d7..25e96dc07 100644 --- a/src/lib/time/control-impl.hpp +++ b/src/lib/time/control-impl.hpp @@ -151,7 +151,7 @@ namespace mutation { /** disconnect any observers */ void - disconnnect() + disconnect() { listeners_.clear(); } diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp index 486097934..f7f78e4d1 100644 --- a/src/lib/time/control.hpp +++ b/src/lib/time/control.hpp @@ -151,7 +151,7 @@ namespace time { /** disconnect from observed entity and * cease any change notification */ - void disconnnect(); + void disconnect(); }; @@ -209,7 +209,7 @@ namespace time { template<class TI> void - Control<TI>::disconnnect() + Control<TI>::disconnect() { notifyListeners_.disconnect(); this->unbind(); From 9c04637aea97eea6cf3cee6ed117d9ba8c24d5ef Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 23 Oct 2011 02:42:25 +0200 Subject: [PATCH 226/496] fix misspelling in library --- src/lib/time/control-impl.hpp | 2 +- src/lib/time/control.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/time/control-impl.hpp b/src/lib/time/control-impl.hpp index c950724d7..25e96dc07 100644 --- a/src/lib/time/control-impl.hpp +++ b/src/lib/time/control-impl.hpp @@ -151,7 +151,7 @@ namespace mutation { /** disconnect any observers */ void - disconnnect() + disconnect() { listeners_.clear(); } diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp index 486097934..f7f78e4d1 100644 --- a/src/lib/time/control.hpp +++ b/src/lib/time/control.hpp @@ -151,7 +151,7 @@ namespace time { /** disconnect from observed entity and * cease any change notification */ - void disconnnect(); + void disconnect(); }; @@ -209,7 +209,7 @@ namespace time { template<class TI> void - Control<TI>::disconnnect() + Control<TI>::disconnect() { notifyListeners_.disconnect(); this->unbind(); From 8ff36fc209b3aa6db539c22494ea2c5d64751278 Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Sat, 22 Oct 2011 20:27:29 -0500 Subject: [PATCH 227/496] Timeline selection is now controlled by lib::time::Control --- src/gui/widgets/timeline/timeline-ibeam-tool.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp index 59d5345be..02d6cbd74 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp @@ -121,14 +121,8 @@ IBeamTool::on_button_press_event(GdkEventButton* event) // User began the drag in clear space, begin a Select drag dragType = Selection; pinnedDragTime = time; - selectionControl (TimeSpan(time, Duration::NIL)); + selectionControl (TimeSpan(time, Duration::NIL)); //TODO: TimelineState Needs a listener for selection changes state->selection_changed_signal().emit(); - std::cout << "\n" << std::string(time) << "\n"; - std::cout << std::string(state->get_selection().start()) << "\n\n"; - - //state->setSelection (Mutation::changeTime (time)); - //state->setSelection (Mutation::changeDuration (Duration::NIL)); - ////////////"//////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all } } } @@ -206,11 +200,9 @@ IBeamTool::set_leading_x(const int x) if (newStartPoint > pinnedDragTime) newStartPoint=pinnedDragTime; // use the smaller one as selection start - // selectionControl (TimeSpan(newStartPoint, Duration(selectionLength))); - - //state->setSelection (Mutation::changeTime(newStartPoint) , set_playback_period); - // state->setSelection (Mutation::changeDuration(selectionLength), set_playback_period); - //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once + //TODO: TimelineState Needs a listener for selection changes + selectionControl (TimeSpan (newStartPoint, Duration(selectionLength))); + state->selection_changed_signal().emit(); } void From 38111835466ba428a5a25a54536d1f698f6fe741 Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Sat, 22 Oct 2011 21:53:23 -0500 Subject: [PATCH 228/496] Added SelectionListener handling selection changing events --- .../widgets/timeline/timeline-ibeam-tool.cpp | 3 +- src/gui/widgets/timeline/timeline-state.cpp | 21 ++++++- src/gui/widgets/timeline/timeline-state.hpp | 55 ++++++++++++++++++- 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp index 02d6cbd74..cd397bf3f 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp @@ -49,7 +49,8 @@ IBeamTool::IBeamTool(TimelineBody &timeline_body) : scrollSlideRate(0) { // Connect the timlinebody selection to the selectionControl - this->get_state()->setSelection (selectionControl, false); + // TODO: Create a virtual initialize function in the base class + get_state()->set_selection_control(selectionControl); } IBeamTool::~IBeamTool() diff --git a/src/gui/widgets/timeline/timeline-state.cpp b/src/gui/widgets/timeline/timeline-state.cpp index 74042ef39..70a8521cc 100644 --- a/src/gui/widgets/timeline/timeline-state.cpp +++ b/src/gui/widgets/timeline/timeline-state.cpp @@ -24,6 +24,7 @@ #include "gui/widgets/timeline/timeline-state.hpp" #include "lib/time/timevalue.hpp" #include "lib/time/mutation.hpp" +#include "lib/time/control.hpp" using namespace Gtk; using namespace sigc; @@ -36,14 +37,15 @@ using lib::time::FSecs; using lib::time::Offset; using lib::time::Duration; using lib::time::Mutation; +using lib::time::Control; using std::tr1::shared_ptr; - TimelineState::TimelineState (shared_ptr<model::Sequence> source_sequence) : sequence(source_sequence) , viewWindow(Offset(Time::ZERO), 1) , selection_(Time::ZERO, Duration::NIL) + , selectionListener() , playbackPeriod_(Time::ZERO, Duration::NIL) , playbackPoint_(Time::ZERO) , isPlayback_(false) @@ -55,6 +57,9 @@ TimelineState::TimelineState (shared_ptr<model::Sequence> source_sequence) viewWindow.set_time_scale(DEFAULT_TIMELINE_SCALE); + selectionListener.connect( + mem_fun(*this, &TimelineState::on_selection_changed)); + setSelection (Mutation::changeTime (Time(FSecs(2)))); setSelection (Mutation::changeDuration(Duration(FSecs(2)))); //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all @@ -97,6 +102,14 @@ TimelineState::setPlaybackPoint (Time newPosition) playbackChangedSignal.emit(); } +void +TimelineState::set_selection_control (SelectionControl &control) +{ + control.disconnect(); + selection_.accept (control); + control.connectChangeNotification (selectionListener); +} + sigc::signal<void> TimelineState::selection_changed_signal() const { @@ -109,6 +122,12 @@ TimelineState::playback_changed_signal() const return playbackChangedSignal; } +void +TimelineState::on_selection_changed() +{ + selectionChangedSignal.emit(); +} + } // namespace timeline } // namespace widgets } // namespace gui diff --git a/src/gui/widgets/timeline/timeline-state.hpp b/src/gui/widgets/timeline/timeline-state.hpp index 0d0b02a32..2033665db 100644 --- a/src/gui/widgets/timeline/timeline-state.hpp +++ b/src/gui/widgets/timeline/timeline-state.hpp @@ -28,6 +28,7 @@ #include "gui/widgets/timeline/timeline-view-window.hpp" #include "lib/time/mutation.hpp" +#include "lib/time/control.hpp" namespace gui { @@ -38,9 +39,42 @@ class Sequence; namespace widgets { namespace timeline { - + +using lib::time::Control; using lib::time::Mutation; +typedef Control<TimeSpan> SelectionControl; + +/** + * SelectionListener is a template class which emits a signal when + * the value is changed by it's associated time::Control object. + */ +template<class TI> +class SelectionListener + : boost::noncopyable + { + sigc::signal<void> valueChangedSignal; + public: + SelectionListener() + { + + } + + + void + operator() (TI const& changeValue) const + { + valueChangedSignal.emit(); + } + + + void connect (const sigc::slot<void> &connection) + { + valueChangedSignal.connect (connection); + } + + }; + /** * TimelineState is a container for the state data for TimelineWidget. * @remarks TimelineState s can be swapped out so that TimelineWidget @@ -70,7 +104,11 @@ public: */ timeline::TimelineViewWindow& get_view_window(); - TimeSpan get_selection() const { return selection_; } + TimeSpan& get_selection() { return selection_; } + + SelectionListener<TimeSpan>& + get_selection_listener() { return selectionListener; } + Time getSelectionStart() const { return selection_.start();} Time getSelectionEnd() const { return selection_.end(); } Time getPlaybackPeriodStart() const { return selection_.start();} @@ -83,6 +121,7 @@ public: * Otherwise the #getPlaybackPoint is meaningless */ bool isPlaying() const { return isPlayback_; } + void set_selection_control (SelectionControl &control); /** * Sets the period of the selection. @@ -113,6 +152,15 @@ public: * changed. */ sigc::signal<void> playback_changed_signal() const; + +private: + + /* ========= Event Handlers ========== */ + + /** + * Event handler for when the selection is changed + */ + void on_selection_changed(); private: @@ -134,6 +182,9 @@ private: /** currently selected time period. */ TimeSpan selection_; + /** listens for a selection change */ + SelectionListener<TimeSpan> selectionListener; + /** current playback period. */ TimeSpan playbackPeriod_; From b87b6078ada0773a333ff269b68902052cb64463 Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Sun, 23 Oct 2011 00:20:48 -0500 Subject: [PATCH 229/496] Adding TimeSpan param to SelectionListener's signal --- src/gui/widgets/timeline/timeline-ibeam-tool.cpp | 13 ++++++------- src/gui/widgets/timeline/timeline-state.cpp | 7 +++++-- src/gui/widgets/timeline/timeline-state.hpp | 12 +++++++----- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp index cd397bf3f..6f20997e7 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp @@ -49,8 +49,7 @@ IBeamTool::IBeamTool(TimelineBody &timeline_body) : scrollSlideRate(0) { // Connect the timlinebody selection to the selectionControl - // TODO: Create a virtual initialize function in the base class - get_state()->set_selection_control(selectionControl); + get_state()->set_selection_control (selectionControl); } IBeamTool::~IBeamTool() @@ -122,8 +121,7 @@ IBeamTool::on_button_press_event(GdkEventButton* event) // User began the drag in clear space, begin a Select drag dragType = Selection; pinnedDragTime = time; - selectionControl (TimeSpan(time, Duration::NIL)); //TODO: TimelineState Needs a listener for selection changes - state->selection_changed_signal().emit(); + selectionControl (TimeSpan(time, Duration::NIL)); } } } @@ -194,16 +192,17 @@ IBeamTool::set_leading_x(const int x) { shared_ptr<TimelineState> state = get_state(); - const bool set_playback_period = dragType == Selection; + // The line below needs handled differently now; + // + //const bool set_playback_period = dragType == Selection; + TimeVar newStartPoint (state->get_view_window().x_to_time(x)); Offset selectionLength (pinnedDragTime, newStartPoint); if (newStartPoint > pinnedDragTime) newStartPoint=pinnedDragTime; // use the smaller one as selection start - //TODO: TimelineState Needs a listener for selection changes selectionControl (TimeSpan (newStartPoint, Duration(selectionLength))); - state->selection_changed_signal().emit(); } void diff --git a/src/gui/widgets/timeline/timeline-state.cpp b/src/gui/widgets/timeline/timeline-state.cpp index 70a8521cc..3e512cc1e 100644 --- a/src/gui/widgets/timeline/timeline-state.cpp +++ b/src/gui/widgets/timeline/timeline-state.cpp @@ -51,7 +51,10 @@ TimelineState::TimelineState (shared_ptr<model::Sequence> source_sequence) , isPlayback_(false) { REQUIRE(sequence); - + + // Initialize the listener + selectionListener (TimeSpan (Time::ZERO, Duration::NIL)); + ////////////////////////////////////////////////////////////TICKET #798: how to handle GUI default state const int64_t DEFAULT_TIMELINE_SCALE =21000000; @@ -123,7 +126,7 @@ TimelineState::playback_changed_signal() const } void -TimelineState::on_selection_changed() +TimelineState::on_selection_changed (TimeSpan selection) { selectionChangedSignal.emit(); } diff --git a/src/gui/widgets/timeline/timeline-state.hpp b/src/gui/widgets/timeline/timeline-state.hpp index 2033665db..f4bc57e75 100644 --- a/src/gui/widgets/timeline/timeline-state.hpp +++ b/src/gui/widgets/timeline/timeline-state.hpp @@ -48,27 +48,29 @@ typedef Control<TimeSpan> SelectionControl; /** * SelectionListener is a template class which emits a signal when * the value is changed by it's associated time::Control object. + * SelectionListener wraps a sigc::signal that emits every time + * the selection is changed */ + template<class TI> class SelectionListener : boost::noncopyable { - sigc::signal<void> valueChangedSignal; + sigc::signal<void, TI> valueChangedSignal; public: SelectionListener() { } - void operator() (TI const& changeValue) const { - valueChangedSignal.emit(); + valueChangedSignal.emit(changeValue); } - void connect (const sigc::slot<void> &connection) + void connect (const sigc::slot<void, TI> &connection) { valueChangedSignal.connect (connection); } @@ -160,7 +162,7 @@ private: /** * Event handler for when the selection is changed */ - void on_selection_changed(); + void on_selection_changed (TimeSpan selection); private: From 6c17d06e66fef79796c2d99dd98d0c680dc3186f Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Sun, 23 Oct 2011 04:20:39 -0500 Subject: [PATCH 230/496] Timeline is no longer zoomed in to far on startup. See timline-view-window.hpp int64_t timeScale for explanation --- src/gui/panels/timeline-panel.cpp | 25 +++++++------- .../widgets/timeline/timeline-ibeam-tool.cpp | 4 +-- src/gui/widgets/timeline/timeline-state.cpp | 15 ++++----- src/gui/widgets/timeline/timeline-state.hpp | 6 ++-- .../widgets/timeline/timeline-view-window.hpp | 7 ++++ .../widgets/timeline/timeline-zoom-scale.cpp | 33 ++++++++++--------- .../widgets/timeline/timeline-zoom-scale.hpp | 2 ++ 7 files changed, 52 insertions(+), 40 deletions(-) diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index 7d5b4fa6c..0036be478 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -138,12 +138,13 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager, // wire the zoom slider to react on timeline state changes zoomScale.wireTimelineState (timelineWidget->get_state(), timelineWidget->state_changed_signal()); - + // Set the initial UI state update_sequence_chooser(); update_tool_buttons(); update_zoom_buttons(); show_time (Time::ZERO); + std::cout << timelineWidget->get_state()->get_view_window().get_time_scale() << "\n"; } const char* @@ -335,17 +336,17 @@ TimelinePanel::update_zoom_buttons() { REQUIRE(timelineWidget); - const shared_ptr<timeline::TimelineState> state = - timelineWidget->get_state(); - if(state) - { - timeline::TimelineViewWindow &viewWindow = - state->get_view_window(); - - zoomIn.set_sensitive(viewWindow.get_time_scale() != 1); - zoomOut.set_sensitive(viewWindow.get_time_scale() - != TimelineWidget::MaxScale); - } + int64_t current_scale = + timelineWidget->get_state()->get_view_window().get_time_scale(); + + double linear_scale = + (double) current_scale / (double) TimelineWidget::MaxScale; + + /* We have to Revese the Smoothing */ + double new_relative_scale = + pow(linear_scale,(1.0/9.0)); + + timelineWidget->zoom_view (new_relative_scale); } void diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp index 6f20997e7..67da78ecd 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp @@ -180,8 +180,8 @@ IBeamTool::on_motion_notify_event(GdkEventMotion *event) bool IBeamTool::on_scroll_slide_timer() { - const Gdk::Rectangle body_rect(get_body_rectangle()); - view_window().shift_view(body_rect.get_width(), scrollSlideRate); + const Gdk::Rectangle body_rect (get_body_rectangle()); + view_window().shift_view (body_rect.get_width(), scrollSlideRate); // Return true to keep the timer going return true; diff --git a/src/gui/widgets/timeline/timeline-state.cpp b/src/gui/widgets/timeline/timeline-state.cpp index 3e512cc1e..a38911ff5 100644 --- a/src/gui/widgets/timeline/timeline-state.cpp +++ b/src/gui/widgets/timeline/timeline-state.cpp @@ -52,19 +52,18 @@ TimelineState::TimelineState (shared_ptr<model::Sequence> source_sequence) { REQUIRE(sequence); - // Initialize the listener + // Initialize the selection listener selectionListener (TimeSpan (Time::ZERO, Duration::NIL)); - - ////////////////////////////////////////////////////////////TICKET #798: how to handle GUI default state - const int64_t DEFAULT_TIMELINE_SCALE =21000000; - - viewWindow.set_time_scale(DEFAULT_TIMELINE_SCALE); - selectionListener.connect( mem_fun(*this, &TimelineState::on_selection_changed)); + ////////////////////////////////////////////////////////////TICKET #798: how to handle GUI default state + const int64_t DEFAULT_TIMELINE_SCALE =6400; + + viewWindow.set_time_scale(DEFAULT_TIMELINE_SCALE); + setSelection (Mutation::changeTime (Time(FSecs(2)))); - setSelection (Mutation::changeDuration(Duration(FSecs(2)))); + setSelection (Mutation::changeDuration (Duration(FSecs(2)))); //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all } diff --git a/src/gui/widgets/timeline/timeline-state.hpp b/src/gui/widgets/timeline/timeline-state.hpp index f4bc57e75..45ef48d13 100644 --- a/src/gui/widgets/timeline/timeline-state.hpp +++ b/src/gui/widgets/timeline/timeline-state.hpp @@ -49,7 +49,9 @@ typedef Control<TimeSpan> SelectionControl; * SelectionListener is a template class which emits a signal when * the value is changed by it's associated time::Control object. * SelectionListener wraps a sigc::signal that emits every time - * the selection is changed + * the selection is changed by the time::Control object. + * SelectionListener does NOT emit the signal if a change to the + * selection is made outside of the Control/Listener partnership. */ template<class TI> @@ -66,7 +68,7 @@ class SelectionListener void operator() (TI const& changeValue) const { - valueChangedSignal.emit(changeValue); + valueChangedSignal.emit (changeValue); } diff --git a/src/gui/widgets/timeline/timeline-view-window.hpp b/src/gui/widgets/timeline/timeline-view-window.hpp index b9536deac..d52f84406 100644 --- a/src/gui/widgets/timeline/timeline-view-window.hpp +++ b/src/gui/widgets/timeline/timeline-view-window.hpp @@ -138,6 +138,13 @@ public: private: TimeVar timeOffset; + + /** + * The scale of the Timline Body. + * @remarks This value represents the time span that is visible in + * the TimelineBodyWidget. Smaller numbers here will "zoom in" + * while larger numbers will "zoom out" + */ int64_t timeScale; sigc::signal<void> changedSignal; diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp index 7452d20cd..2ee413c71 100644 --- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp +++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp @@ -65,26 +65,27 @@ TimelineZoomScale::TimelineZoomScale() , 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); + slider.set_adjustment (adjustment); + slider.set_size_request (123,10); + slider.set_digits (6); + + /* Inverted because smaller values "zoom in" */ + 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)); - + connect (sigc::mem_fun(this, &TimelineZoomScale::on_zoom_in_clicked)); zoomOut.signal_clicked(). - connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom_out_clicked)); - + connect (sigc::mem_fun(this, &TimelineZoomScale::on_zoom_out_clicked)); adjustment.signal_value_changed(). - connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom)); + 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); + pack_start (zoomOut,PACK_SHRINK); + pack_start (slider,PACK_SHRINK); + pack_start (zoomIn,PACK_SHRINK); show_all(); } @@ -94,7 +95,8 @@ TimelineZoomScale::wireTimelineState (shared_ptr<TimelineState> currentState, TimelineWidget::TimelineStateChangeSignal stateChangeSignal) { on_timeline_state_changed (currentState); - stateChangeSignal.connect (sigc::mem_fun(this, &TimelineZoomScale::on_timeline_state_changed)); + stateChangeSignal.connect ( + sigc::mem_fun(this, &TimelineZoomScale::on_timeline_state_changed)); } void @@ -110,7 +112,6 @@ TimelineZoomScale::on_timeline_state_changed (shared_ptr<TimelineState> newState (double) current_scale / (double) TimelineWidget::MaxScale; /* We have to Revese the Smoothing */ - TODO("Find a central place for ZoomSmoothingFactor Variable. right now it is 9.0"); double new_relative_scale = pow(linear_scale,(1.0/9.0)); @@ -134,7 +135,7 @@ TimelineZoomScale::on_zoom_out_clicked() void TimelineZoomScale::on_zoom() { - zoomSignal.emit(adjustment.get_value()) ; + zoomSignal.emit (adjustment.get_value()) ; } sigc::signal<void, double> diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.hpp b/src/gui/widgets/timeline/timeline-zoom-scale.hpp index a57fdd6df..694252319 100644 --- a/src/gui/widgets/timeline/timeline-zoom-scale.hpp +++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp @@ -51,6 +51,8 @@ public: */ sigc::signal<void, double> signal_zoom(); + void set_value(double val) { adjustment.set_value(val); } + void wireTimelineState (shared_ptr<TimelineState> currentState, TimelineWidget::TimelineStateChangeSignal); From 0aa3ca76d1d8a2af6e06616869e02143d8e66eb8 Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Sun, 23 Oct 2011 04:45:10 -0500 Subject: [PATCH 231/496] Created and Applied TimelineWidget::ZoomSmoothing --- src/gui/panels/timeline-panel.cpp | 16 +++------------- src/gui/widgets/timeline-widget.cpp | 3 ++- src/gui/widgets/timeline-widget.hpp | 1 + .../widgets/timeline/timeline-view-window.cpp | 5 +++-- src/gui/widgets/timeline/timeline-zoom-scale.cpp | 2 +- 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index 0036be478..0366504c6 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -334,19 +334,9 @@ TimelinePanel::update_tool_buttons() void TimelinePanel::update_zoom_buttons() { - REQUIRE(timelineWidget); - - int64_t current_scale = - timelineWidget->get_state()->get_view_window().get_time_scale(); - - double linear_scale = - (double) current_scale / (double) TimelineWidget::MaxScale; - - /* We have to Revese the Smoothing */ - double new_relative_scale = - pow(linear_scale,(1.0/9.0)); - - timelineWidget->zoom_view (new_relative_scale); +/* This function is no longer needed + * TODO: Let the ZoomScaleWidget perform + * the update on its own */ } void diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index 52008f0cc..b971477c6 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -41,7 +41,8 @@ namespace widgets { const int TimelineWidget::TrackPadding = 1; const int TimelineWidget::HeaderWidth = 150; const int TimelineWidget::HeaderIndentWidth = 10; -const double TimelineWidget::ZoomIncrement = 1.25; +const double TimelineWidget::ZoomIncrement = 1.25; // Not currently used +const double TimelineWidget::ZoomSmoothing = 9.0; const int64_t TimelineWidget::MaxScale = 30000000; // 30 Million TimelineWidget::TimelineWidget(shared_ptr<timeline::TimelineState> source_state) diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index 39901305a..b5d57c667 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -285,6 +285,7 @@ public: * to 30000000 lumiera::Time increments. */ static const int64_t MaxScale; + static const double ZoomSmoothing; protected: static const int TrackPadding; diff --git a/src/gui/widgets/timeline/timeline-view-window.cpp b/src/gui/widgets/timeline/timeline-view-window.cpp index 07e017e07..9e80d2f9c 100644 --- a/src/gui/widgets/timeline/timeline-view-window.cpp +++ b/src/gui/widgets/timeline/timeline-view-window.cpp @@ -88,9 +88,10 @@ TimelineViewWindow::set_time_scale(double ratio) void TimelineViewWindow::zoom_view(int point, double time_scale_ratio) { - TODO("Find a Central place for a Zoom Smoothing Factor Variable. Right now it is hard coded at 9.0"); + // Apply the smoothing factor int64_t new_time_scale = - (int64_t)( pow(time_scale_ratio, 9.0) * (double)TimelineWidget::MaxScale); + (int64_t)( pow(time_scale_ratio, TimelineWidget::ZoomSmoothing) * + (double)TimelineWidget::MaxScale); /* Prevent Zooming in To Close and Far */ if(new_time_scale < 1) diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp index 2ee413c71..dfa71f858 100644 --- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp +++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp @@ -113,7 +113,7 @@ TimelineZoomScale::on_timeline_state_changed (shared_ptr<TimelineState> newState /* We have to Revese the Smoothing */ double new_relative_scale = - pow(linear_scale,(1.0/9.0)); + pow(linear_scale,(1.0 / TimelineWidget::ZoomSmoothing)); adjustment.set_value(new_relative_scale); } From 1bb584f895a544fb5075ecd0de344f771e84a331 Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Sun, 23 Oct 2011 04:52:30 -0500 Subject: [PATCH 232/496] Removed some TODO's --- src/gui/widgets/timeline/timeline-view-window.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/gui/widgets/timeline/timeline-view-window.cpp b/src/gui/widgets/timeline/timeline-view-window.cpp index 9e80d2f9c..8b6528f11 100644 --- a/src/gui/widgets/timeline/timeline-view-window.cpp +++ b/src/gui/widgets/timeline/timeline-view-window.cpp @@ -35,8 +35,7 @@ TimelineViewWindow::TimelineViewWindow (Offset offset, int64_t scale) : timeOffset(offset) , timeScale(scale) { - TODO("Create a function to limit timescale between 1 and MaxScale"); - TODO("TICKET #795 Some functions need to be private"); + } Offset @@ -90,7 +89,7 @@ TimelineViewWindow::zoom_view(int point, double time_scale_ratio) { // Apply the smoothing factor int64_t new_time_scale = - (int64_t)( pow(time_scale_ratio, TimelineWidget::ZoomSmoothing) * + (int64_t)(pow(time_scale_ratio, TimelineWidget::ZoomSmoothing) * (double)TimelineWidget::MaxScale); /* Prevent Zooming in To Close and Far */ @@ -102,8 +101,8 @@ TimelineViewWindow::zoom_view(int point, double time_scale_ratio) // 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)); - set_time_offset(newStartPoint); + newStartPoint += TimeValue (point * (timeScale - new_time_scale)); + set_time_offset (newStartPoint); // Apply the new scale set_time_scale(new_time_scale); @@ -112,13 +111,13 @@ TimelineViewWindow::zoom_view(int point, double time_scale_ratio) void TimelineViewWindow::shift_view(int view_width, int shift_size) { - set_time_offset(timeOffset + TimeValue(timeScale * shift_size * view_width / 256)); + set_time_offset (timeOffset + TimeValue(timeScale * shift_size * view_width / 256)); } int TimelineViewWindow::time_to_x(TimeValue const& time) const { - return int(_raw(time - timeOffset) / timeScale); //////TODO protect against values out-of range + return int (_raw(time - timeOffset) / timeScale); //////TODO protect against values out-of range } Time From 65a29244af010519668eee5940182d136dac4f8d Mon Sep 17 00:00:00 2001 From: "Michael R. Fisher" <mfisher31@gmail.com> Date: Sun, 23 Oct 2011 09:52:12 -0500 Subject: [PATCH 233/496] The tool also needs updated on TimelineState Change --- src/gui/widgets/timeline/timeline-body.cpp | 14 ++++++++++---- src/gui/widgets/timeline/timeline-body.hpp | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index 369b894ed..e64110997 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -57,9 +57,12 @@ TimelineBody::TimelineBody (TimelineWidget &timelineWidget) timelineWidget.state_changed_signal().connect( sigc::mem_fun(this, &TimelineBody::on_state_changed) ); + // Set a default Tool + this->set_tool(Arrow); + // Install style properties register_styles(); - + // Reset the state propagateStateChange(); } @@ -96,10 +99,10 @@ TimelineBody::get_tool() const } void -TimelineBody::set_tool(timeline::ToolType tool_type) +TimelineBody::set_tool(timeline::ToolType tool_type, bool force) { // Tidy up old tool - if(tool) + if(tool && !force) { // Do we need to change tools? if(tool->get_type() == tool_type) @@ -322,7 +325,10 @@ TimelineBody::propagateStateChange() viewWindow().changed_signal().connect( sigc::mem_fun(this, &TimelineBody::on_update_view) ); } - + + // Need to reload the current tool... + set_tool (get_tool(), true); + // Redraw queue_draw(); } diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index 16b3b13e7..8abf01a3f 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -88,7 +88,7 @@ public: * @param tool_type The type of tool to set. */ void - set_tool(ToolType tool_type); + set_tool(ToolType tool_type, bool force=false); /* ===== Events ===== */ protected: From f7a3fdb7a5abf3829320c8edca863c9d7eb4f5a7 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 23 Oct 2011 22:06:32 +0200 Subject: [PATCH 234/496] fix further test glitches (due to using real random numbers now) --- tests/lib/polymorphic-value-test.cpp | 2 +- tests/lib/time/digxel-test.cpp | 13 +++++++++++-- tests/lib/util-floordiv-test.cpp | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/lib/polymorphic-value-test.cpp b/tests/lib/polymorphic-value-test.cpp index 8ee1ef356..eacf30933 100644 --- a/tests/lib/polymorphic-value-test.cpp +++ b/tests/lib/polymorphic-value-test.cpp @@ -117,7 +117,7 @@ namespace test{ virtual long apiFunc() { - long rr = ii * (rand() % MAX_RAND); + long rr = ii * (1 + rand() % MAX_RAND); mark (rr); _callSum += rr; return rr; diff --git a/tests/lib/time/digxel-test.cpp b/tests/lib/time/digxel-test.cpp index b99656233..809558352 100644 --- a/tests/lib/time/digxel-test.cpp +++ b/tests/lib/time/digxel-test.cpp @@ -27,6 +27,7 @@ #include "lib/time/digxel.hpp" #include "lib/util.hpp" +#include <time.h> #include <cstdlib> #include <iostream> #include <boost/format.hpp> @@ -55,7 +56,15 @@ namespace test{ { double arbitrary = (rand() % RAND_RANGE); arbitrary /= (1 + rand() % RAND_DENOM); - return arbitrary; + + static double prevVal; + if (arbitrary != prevVal) + { + prevVal = arbitrary; + return arbitrary; + } + else + return randomFrac(); } inline uint @@ -267,7 +276,7 @@ namespace test{ CHECK (d1 == d2); - double someValue = randomFrac(); + double someValue = d1 + randomFrac(); d1 = someValue; CHECK (d1 == someValue); diff --git a/tests/lib/util-floordiv-test.cpp b/tests/lib/util-floordiv-test.cpp index dad2a40b7..63c14a1a6 100644 --- a/tests/lib/util-floordiv-test.cpp +++ b/tests/lib/util-floordiv-test.cpp @@ -25,6 +25,7 @@ #include "lib/util.hpp" #include <cmath> +#include <time.h> #include <vector> #include <iostream> #include <boost/format.hpp> From 76a6407a5ef32945e5f7d561a9b2966b120d6f54 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 23 Oct 2011 04:09:51 +0200 Subject: [PATCH 235/496] better formulation for the buffer metadata state machine --- src/proc/engine/buffer-metadata.hpp | 32 +++++++++++------------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 6addaf574..03b04c4d9 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -374,28 +374,20 @@ namespace engine { virtual Entry& mark (BufferState newState) { - switch (this->state_) + __must_not_be_NIL(); + __must_not_be_FREE(); + + if ( (state_ == LOCKED && newState == EMITTED) + ||(state_ == EMITTED && newState == BLOCKED) + ||(state_ == BLOCKED && newState == FREE)) { - case NIL: __must_not_be_NIL(); - case FREE: __must_not_be_FREE(); - - case LOCKED: - if (newState == EMITTED) break; // allow transition - - case EMITTED: - if (newState == BLOCKED) break; // allow transition - - case BLOCKED: - if (newState == FREE) // note fall through for LOCKED and EMITTED too - { - buffer_ = 0; - break; // allow transition - } - default: - throw error::Fatal ("Invalid buffer state encountered."); + // allowed transition + if (newState == FREE) buffer_ = 0; + state_ = newState; + return *this; } - state_ = newState; - return *this; + + throw error::Fatal ("Invalid buffer state encountered."); } From c4ff87c3a8ed8e6d0626c677def6f2d28d5183af Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 24 Oct 2011 01:03:56 +0200 Subject: [PATCH 236/496] decide about the basic way to implement concrete buffer metadata --- src/proc/engine/buffer-metadata.hpp | 57 ++++++++++++++----- .../proc/engine/buffer-metadata-test.cpp | 4 +- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 03b04c4d9..eb97a8833 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -77,6 +77,7 @@ namespace engine { class Key; class Entry; } + class Metadata; @@ -275,6 +276,12 @@ namespace engine { } } + /** + * Description of a Buffer-"type". + * Key elements will be used to generate hash IDs, + * to be embedded into a BufferDescriptor. + * Keys are chained hierarchically. + */ class Key { HashVal parent_; @@ -347,21 +354,46 @@ namespace engine { }; + /** + * A complete metadata Entry, based on a Key. + * This special Key element usually describes an actual Buffer. + * Entries are to be managed in a hashtable, which is "the metadata table". + * As a special case, an entry without a concrete buffer storage pointer + * can be created. This corresponds to a (plain) key and describes just + * a buffer type. Such type-only entries are fixed to the NIL state. + * All other entries allow for state transitions. + * + * The "metadata table" with its entries is maintained by an engine::Metadata + * instance. For the latter, Entry serves as representation and access point + * to the individual metadata; this includes using the TypeHandler for + * building and destroying buffer structures. + */ class Entry : public Key { BufferState state_; const void* buffer_; + protected: + Entry (Key const& parent, const void* bufferPtr =0) + : Key(parent) + , state_(bufferPtr? LOCKED:NIL) + , buffer_(bufferPtr) + { } + + /// Metadata is allowed to create + friend class engine::Metadata; + + // standard copy operations permitted + public: - virtual BufferState + BufferState state() const { - __must_not_be_NIL(); return state_; } - virtual const void* + const void* access() const { __must_not_be_NIL(); @@ -371,7 +403,7 @@ namespace engine { return buffer_; } - virtual Entry& + Entry& mark (BufferState newState) { __must_not_be_NIL(); @@ -391,6 +423,7 @@ namespace engine { } + private: void __must_not_be_NIL() const { @@ -409,7 +442,7 @@ namespace engine { , LUMIERA_ERROR_LIFECYCLE ); } }; - } + }//namespace metadata @@ -451,7 +484,7 @@ namespace engine { Key key ( size_t storageSize , TypeHandler instanceFunc =RAW_BUFFER - , LocalKey specifics =UNSPECIFIC) + , LocalKey specifics =UNSPECIFIC) { REQUIRE (storageSize); Key typeKey = trackKey (family_, storageSize); @@ -489,16 +522,14 @@ namespace engine { UNIMPLEMENTED ("create sub-object key for concrete buffer"); } - Key const& + Entry& get (HashVal hashID) - { - UNIMPLEMENTED ("access the plain key entry"); - } - - Entry& - get (Key key) { UNIMPLEMENTED ("access, possibly create metadata records"); + ////////////////////////////////////////////////////////////////TODO: how can we 'possibly create' without knowing the buffer pointer?? + + ////////////////////////////////////////////////////////////////TODO: thus, this should only access existing buffer (not type) entries + ////////////////////////////////////////////////////////////////TODO: ==> contradiction: Entry is subclass of Key. What's the point of this lookup? } bool diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp index f76c4fb44..b109b9fa0 100644 --- a/tests/components/proc/engine/buffer-metadata-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -133,7 +133,7 @@ namespace test { CHECK ( isSameObject (meta_->get(key), meta_->get(key1))); CHECK (!isSameObject (meta_->get(key), meta_->get(key2))); - // entries retrieved this far are inactive (type only) entries + // entries retrieved thus far were inactive (type only) entries Metadata::Entry& m1 = meta_->get(key); CHECK (NIL == m1.state()); CHECK (!meta_->isLocked(key)); @@ -234,7 +234,7 @@ namespace test { // for the TestFrame buffers, additionally we'd have to create/attach an object attachTestFrame.createAttached (frames+0); ////////////////////////////////////////TODO: shouldn't this happen automatically?? - attachTestFrame.createAttached (frames+1); + attachTestFrame.createAttached (frames+1); //////////////////////////TODO: answer: yes. Metadata is exactly the entity which has all necessary information attachTestFrame.createAttached (frames+2); CHECK (f0.access() == frames+0); From 9af0fb096f357bd367059f8de3e4c05be8ea9eea Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 24 Oct 2011 02:14:08 +0200 Subject: [PATCH 237/496] WIP draft implementation of buffer metadata creation/storage --- src/proc/engine/buffer-metadata.hpp | 80 ++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 18 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index eb97a8833..23a4ed125 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -516,32 +516,77 @@ namespace engine { return trackKey (parentKey, specifics); } - Key + /** shortcut to access the Key part of a (probably new) Entry + * describing a concrete buffer at the given address + * @note might create/register a new Entry as a side-effect + */ + Key const& key (Key const& parentKey, const void* concreteBuffer) { - UNIMPLEMENTED ("create sub-object key for concrete buffer"); + return get (parentKey,concreteBuffer); } + /** core operation to access or create a concrete buffer metadata entry. + * The hashID of the entry in question is built, based on the parentKey, + * which denotes a buffer type, and the concrete buffer address. If yet + * unknown, a new concrete buffer metadata Entry is created and initialised + * to LOCKED state. Otherwise just the existing Entry is fetched. + * @param parentKey a key describing the \em type of the buffer + * @param concreteBuffer storage pointer, must not be NULL + * @param onlyNew disallows fetching an existing entry + * @throw error::Logic when #onlyNew is set, but an equivalent entry + * was registered previously. This indicates a serious error + * in buffer lifecycle management. + * @return reference to the entry stored in the metadata table. + * @warning the exposed reference might become invalid when the + * buffer is released or re-used later. + */ + Entry& + get (Key const& parentKey, const void* concreteBuffer, bool onlyNew =false) + { + Entry newEntry(parentKey, concreteBuffer); + Entry* existing = table_.fetch (newEntry); + if (existing && onlyNew) + throw error::Logic ("Attempt to lock a slot for a new buffer, " + "while actually the old buffer is still locked." + , error::LUMIERA_ERROR_LIFECYCLE ); + + if (!existing) + return table_.store (newEntry); + else + return *existing; + } + + /** access the metadata record registered with the given hash key. + * This might be a pseudo entry in case of a Key describing a buffer type. + * Otherwise, the entry associated with a concrete buffer pointer is returned + * by reference, an can be modified (e.g. state change) + * @param hashID which can be calculated from the Key + * @throw error::Invalid when there is no such entry + * @note use #isKnown to check existence + */ Entry& get (HashVal hashID) { - UNIMPLEMENTED ("access, possibly create metadata records"); - ////////////////////////////////////////////////////////////////TODO: how can we 'possibly create' without knowing the buffer pointer?? + Entry* entry = table_.fetch (hashID); + if (!entry) + throw error::Invalid ("Attempt to access an unknown buffer metadata entry"); - ////////////////////////////////////////////////////////////////TODO: thus, this should only access existing buffer (not type) entries - ////////////////////////////////////////////////////////////////TODO: ==> contradiction: Entry is subclass of Key. What's the point of this lookup? + return *entry; } bool isKnown (HashVal key) const { - UNIMPLEMENTED ("diagnostics: known record?"); + return bool(table_.fetch (key)); } bool isLocked (HashVal key) const { - UNIMPLEMENTED ("diagnostics: actually locked buffer instance record?"); + Entry* entry = table_.fetch (key); + return entry + && entry->isLocked(); } @@ -565,7 +610,7 @@ namespace engine { maybeStore (Key const& key) { if (isKnown (key)) return; - UNIMPLEMENTED ("registry for type keys"); + table_.store (Entry (key, NULL)); } }; @@ -581,24 +626,23 @@ namespace engine { inline Metadata::Entry& Metadata::markLocked (Key const& parentKey, const void* buffer) { - UNIMPLEMENTED ("transition to locked state"); if (!buffer) throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?" , error::LUMIERA_ERROR_BOTTOM_VALUE); - Key newKey = this->key (parentKey, buffer); - if (isLocked(newKey)) - throw error::Logic ("Attempt to lock a slot for a new buffer, " - "while actually the old buffer is still locked." - , error::LUMIERA_ERROR_LIFECYCLE ); - - return this->get(newKey); + return this->get (parentKey, buffer, true); // force creation of a new entry } inline void Metadata::release (HashVal key) { - UNIMPLEMENTED ("metadata memory management"); + Entry* entry = table_.fetch (key); + if (!entry) return; + if (entry && (FREE != entry->state())) + throw error::Logic ("Attempt to release a buffer still in use" + , error::LUMIERA_ERROR_LIFECYCLE); + + table_.remove (key); } From c8cdf914eb6ac347eb30055b8506008d0ac19cad Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 28 Oct 2011 01:11:39 +0200 Subject: [PATCH 238/496] review metadata storage, stub the actual access operations --- src/proc/engine/buffer-metadata.hpp | 69 ++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 23a4ed125..333256759 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -387,6 +387,39 @@ namespace engine { // standard copy operations permitted public: + /** is this Entry currently associated to a + * concrete buffer? Is this buffer in use? */ + bool + isLocked() const + { + ASSERT (NIL != state_ && FREE != state_); + return bool(buffer_); + } + + /** is this Entry just an (abstract) placeholder for a type? + * @return false if it's a real entry corresponding to a concrete buffer + */ + bool + isTypeKey() const + { + return !bool(buffer_); + } + + /** @note hiding the base implementation to support storing + * and lookup of individual entries in a hashtable */ + operator HashVal() const + { + return isTypeKey()? Key::operator HashVal() + : chainedHash (parentKey(), buffer_); + } + + HashVal + parentKey() const + { + return isTypeKey()? Key::parentKey() + : Key::operator HashVal(); + } + BufferState state() const { @@ -442,6 +475,32 @@ namespace engine { , LUMIERA_ERROR_LIFECYCLE ); } }; + + + /** + * (Hash)Table to store and manage buffer metadata + */ + class Table + { + public: + Entry* + fetch (HashVal hashID) const + { + UNIMPLEMENTED ("fetch metadata record by ID"); + } + + Entry& + store (Entry const& newEntry) + { + UNIMPLEMENTED ("store new metadata record"); + } + + void + remove (HashVal hashID) + { + UNIMPLEMENTED ("delete metadata record"); + } + }; }//namespace metadata @@ -454,6 +513,8 @@ namespace engine { Literal id_; HashVal family_; + metadata::Table table_; + public: typedef metadata::Key Key; @@ -533,10 +594,12 @@ namespace engine { * to LOCKED state. Otherwise just the existing Entry is fetched. * @param parentKey a key describing the \em type of the buffer * @param concreteBuffer storage pointer, must not be NULL - * @param onlyNew disallows fetching an existing entry + * @param onlyNew disallow fetching an existing entry * @throw error::Logic when #onlyNew is set, but an equivalent entry * was registered previously. This indicates a serious error * in buffer lifecycle management. + * @throw error::Invalid when invoked with NULL buffer. Use the #key + * functions instead to register and track type keys. * @return reference to the entry stored in the metadata table. * @warning the exposed reference might become invalid when the * buffer is released or re-used later. @@ -544,6 +607,10 @@ namespace engine { Entry& get (Key const& parentKey, const void* concreteBuffer, bool onlyNew =false) { + if (!concreteBuffer) + throw error::Invalid ("Attempt to lock a slot for a NULL buffer" + , error::LUMIERA_ERROR_BOTTOM_VALUE); + Entry newEntry(parentKey, concreteBuffer); Entry* existing = table_.fetch (newEntry); if (existing && onlyNew) From c91e703682a2058867dd0ec8686fd3120b395471 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 28 Oct 2011 01:18:22 +0200 Subject: [PATCH 239/496] rename the core type BufferMetadata makes the purpose more clear, and moreover it matches the header filename --- src/proc/engine/buffer-metadata.hpp | 18 ++++++------ src/proc/engine/buffer-provider.cpp | 2 +- src/proc/engine/buffer-provider.hpp | 4 +-- .../proc/engine/buffer-metadata-test.cpp | 28 +++++++++---------- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 333256759..9fa6d2e71 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -77,7 +77,7 @@ namespace engine { class Key; class Entry; } - class Metadata; + class BufferMetadata; @@ -363,7 +363,7 @@ namespace engine { * a buffer type. Such type-only entries are fixed to the NIL state. * All other entries allow for state transitions. * - * The "metadata table" with its entries is maintained by an engine::Metadata + * The "metadata table" with its entries is maintained by an engine::BufferMetadata * instance. For the latter, Entry serves as representation and access point * to the individual metadata; this includes using the TypeHandler for * building and destroying buffer structures. @@ -381,8 +381,8 @@ namespace engine { , buffer_(bufferPtr) { } - /// Metadata is allowed to create - friend class engine::Metadata; + /// BufferMetadata is allowed to create + friend class engine::BufferMetadata; // standard copy operations permitted @@ -507,7 +507,7 @@ namespace engine { - class Metadata + class BufferMetadata : boost::noncopyable { Literal id_; @@ -527,7 +527,7 @@ namespace engine { * @param implementationID to distinguish families * of type keys belonging to different registries. */ - Metadata (Literal implementationID) + BufferMetadata (Literal implementationID) : id_(implementationID) , family_(hash_value(id_)) { } @@ -690,8 +690,8 @@ namespace engine { /** */ - inline Metadata::Entry& - Metadata::markLocked (Key const& parentKey, const void* buffer) + inline BufferMetadata::Entry& + BufferMetadata::markLocked (Key const& parentKey, const void* buffer) { if (!buffer) throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?" @@ -701,7 +701,7 @@ namespace engine { } inline void - Metadata::release (HashVal key) + BufferMetadata::release (HashVal key) { Entry* entry = table_.fetch (key); if (!entry) return; diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp index e0670cd73..f0d1b337f 100644 --- a/src/proc/engine/buffer-provider.cpp +++ b/src/proc/engine/buffer-provider.cpp @@ -36,7 +36,7 @@ namespace engine { BufferProvider::BufferProvider (Literal implementationID) - : meta_(new Metadata (implementationID)) + : meta_(new BufferMetadata (implementationID)) { } BufferProvider::~BufferProvider() { } diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index ca6382d66..97712c712 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -56,7 +56,7 @@ namespace engine { using lib::Literal; - class Metadata; + class BufferMetadata; /** @@ -71,7 +71,7 @@ namespace engine { class BufferProvider : boost::noncopyable { - scoped_ptr<Metadata> meta_; + scoped_ptr<BufferMetadata> meta_; protected: BufferProvider (Literal implementationID); diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp index b109b9fa0..dd55542e2 100644 --- a/tests/components/proc/engine/buffer-metadata-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -83,7 +83,7 @@ namespace test { class BufferMetadata_test : public Test { /** common Metadata table to be tested */ - scoped_ptr<Metadata> meta_; + scoped_ptr<BufferMetadata> meta_; virtual void run (Arg) @@ -99,7 +99,7 @@ namespace test { ensure_proper_fixture() { if (!meta_) - meta_.reset(new Metadata("BufferMetadata_test")); + meta_.reset(new BufferMetadata("BufferMetadata_test")); return (SIZE_A != SIZE_B) && (JUST_SOMETHING != meta_->key(SIZE_A)) @@ -112,11 +112,11 @@ namespace test { verifyBasicProperties() { // retrieve some type keys - Metadata::Key key = meta_->key(SIZE_A); + metadata::Key key = meta_->key(SIZE_A); CHECK (key); - Metadata::Key key1 = meta_->key(SIZE_A); - Metadata::Key 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); @@ -134,7 +134,7 @@ namespace test { CHECK (!isSameObject (meta_->get(key), meta_->get(key2))); // entries retrieved thus far were inactive (type only) entries - Metadata::Entry& m1 = meta_->get(key); + metadata::Entry& m1 = meta_->get(key); CHECK (NIL == m1.state()); CHECK (!meta_->isLocked(key)); @@ -142,7 +142,7 @@ namespace test { VERIFY_ERROR (LIFECYCLE, m1.mark(LOCKED) ); // now create an active (buffer) entry - Metadata::Entry& m2 = meta_->markLocked (key, SOME_POINTER); + metadata::Entry& m2 = meta_->markLocked (key, SOME_POINTER); CHECK (!isSameObject (m1,m2)); CHECK (NIL == m1.state()); CHECK (LOCKED == m2.state()); @@ -197,10 +197,10 @@ namespace test { { // to build a descriptor for a buffer holding a TestFrame TypeHandler attachTestFrame = TypeHandler::create<TestFrame>(); - Metadata::Key bufferType1 = meta_->key(sizeof(TestFrame), attachTestFrame); + metadata::Key bufferType1 = meta_->key(sizeof(TestFrame), attachTestFrame); // to build a descriptor for a raw buffer of size SIZE_B - Metadata::Key rawBuffType = meta_->key(SIZE_B); + metadata::Key rawBuffType = meta_->key(SIZE_B); // to announce using a number of buffers of this type LocalKey transaction1(1); @@ -219,12 +219,12 @@ namespace test { // a real-world BufferProvider would use some kind of allocator // track individual buffers by metadata entries - Metadata::Entry f0 = meta_->markLocked(bufferType1, &frames[0]); - Metadata::Entry f1 = meta_->markLocked(bufferType1, &frames[1]); - Metadata::Entry f2 = meta_->markLocked(bufferType1, &frames[2]); + metadata::Entry f0 = meta_->markLocked(bufferType1, &frames[0]); + metadata::Entry f1 = meta_->markLocked(bufferType1, &frames[1]); + metadata::Entry f2 = meta_->markLocked(bufferType1, &frames[2]); - Metadata::Entry r0 = meta_->markLocked(bufferType1, &rawbuf[0]); - Metadata::Entry r1 = meta_->markLocked(bufferType1, &rawbuf[1]); + metadata::Entry r0 = meta_->markLocked(bufferType1, &rawbuf[0]); + metadata::Entry r1 = meta_->markLocked(bufferType1, &rawbuf[1]); CHECK (LOCKED == f0.state()); CHECK (LOCKED == f1.state()); From f849ca62d99f07fbd968c1ad5d4980434dd1f9cf Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 29 Oct 2011 02:41:56 +0200 Subject: [PATCH 240/496] implement metadata storage (hash)table not passing all tests yet --- src/proc/engine/buffer-metadata.hpp | 97 ++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 9 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 9fa6d2e71..19c18eecb 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -57,8 +57,11 @@ #include "lib/error.hpp" #include "lib/symbol.hpp" #include "lib/functor-util.hpp" +#include "lib/util-foreach.hpp" +#include "include/logging.h" #include <tr1/functional> +#include <tr1/unordered_map> #include <boost/functional/hash.hpp> #include <boost/noncopyable.hpp> @@ -67,6 +70,7 @@ namespace engine { using lib::HashVal; using lib::Literal; + using util::for_each; using std::tr1::bind; using std::tr1::function; using std::tr1::placeholders::_1; @@ -392,7 +396,7 @@ namespace engine { bool isLocked() const { - ASSERT (NIL != state_ && FREE != state_); + ASSERT (!buffer_ || (NIL != state_ && FREE != state_)); return bool(buffer_); } @@ -461,8 +465,9 @@ namespace engine { __must_not_be_NIL() const { if (NIL == state_) - throw error::Fatal ("Concrete buffer entry with state==NIL encountered." - "State transition logic broken (programming error)"); + throw error::Fatal ("Buffer metadata entry with state==NIL encountered." + "State transition logic broken (programming error)" + , LUMIERA_ERROR_LIFECYCLE); } void @@ -478,29 +483,103 @@ namespace engine { /** - * (Hash)Table to store and manage buffer metadata + * (Hash)Table to store and manage buffer metadata. + * Buffer metadata entries are comprised of a Key part and an extended + * Entry, holding the actual management and housekeeping metadata. The + * Keys are organised hierarchically and denote the "kind" of buffer. + * The hash values for lookup are based on the key part, chained with + * the actual memory location of the concrete buffer corresponding + * to the metadata entry to be retrieved. */ class Table { + typedef std::tr1::unordered_map<HashVal,Entry> MetadataStore; + + MetadataStore entries_; + public: + ~Table() { verify_all_buffers_freed(); } + + /** fetch metadata record, if any + * @param hashID for the Key part of the metadata entry + * @return pointer to the entry in the table or NULL + */ Entry* + fetch (HashVal hashID) + { + MetadataStore::iterator pos = entries_.find (hashID); + if (pos != entries_.end()) + return &(pos->second); + else + return NULL; + } + + const Entry* fetch (HashVal hashID) const { - UNIMPLEMENTED ("fetch metadata record by ID"); + MetadataStore::const_iterator pos = entries_.find (hashID); + if (pos != entries_.end()) + return &(pos->second); + else + return NULL; } - + + /** store a copy of the given new metadata entry. + * The hash key for lookup is retrieved from the given Entry, by conversion to HashVal. + * Consequently, this will be the hashID of the parent Key (type), when the entry holds + * a NULL buffer (i.e a "pseudo entry"). Otherwise, it will be this parent Key hash, + * extended by hashing the actual buffer address. + * @return reference to relevant entry for this Key. This might be a copy + * of the new entry, or an already existing entry with the same Key + */ Entry& store (Entry const& newEntry) { - UNIMPLEMENTED ("store new metadata record"); + using std::make_pair; + REQUIRE (!fetch (newEntry), "duplicate buffer metadata entry"); + MetadataStore::iterator pos = entries_.insert (make_pair (HashVal(newEntry), newEntry)) + .first; + + ENSURE (pos != entries_.end()); + return pos->second; } void remove (HashVal hashID) { - UNIMPLEMENTED ("delete metadata record"); + uint cnt = entries_.erase (hashID); + ENSURE (cnt, "entry to remove didn't exist"); + } + + private: + void + verify_all_buffers_freed() + try + { + for_each (entries_, verify_is_free); + } + catch (std::exception& problem) + { + const char* errID = lumiera_error(); + const char* operation = "Shutdown of BufferProvider metadata store"; + WARN (engine, "%s failed: %s", operation, problem.what()); + TRACE (debugging, "Error flag was: %s", errID); + } + catch (...) + { + const char* errID = lumiera_error(); + const char* operation = "Shutdown of BufferProvider metadata store"; + ERROR (engine, "%s failed with unknown exception; error flag is: %s", operation, errID); + } + + static void + verify_is_free (std::pair<HashVal, Entry> const& e) + { + WARN_IF (e.second.isLocked(), engine, + "Buffer still in use while shutting down BufferProvider? "); } }; + }//namespace metadata @@ -651,7 +730,7 @@ namespace engine { bool isLocked (HashVal key) const { - Entry* entry = table_.fetch (key); + const Entry* entry = table_.fetch (key); return entry && entry->isLocked(); } From e7f0211711583257abdc85037939be75138a8a45 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 30 Oct 2011 01:17:31 +0200 Subject: [PATCH 241/496] better solution for calculating the Key for a concrete Entry --- src/proc/engine/buffer-metadata.hpp | 35 ++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 19c18eecb..21ed1c50a 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -353,6 +353,25 @@ namespace engine { { } + /** build derived Key for a concrete buffer Entry + * @param parent type key to subsume this buffer + * @param bufferAddr pointer to the concrete buffer + * @return Child key with hashID based on the buffer address. + * For NULL buffer a copy of the parent is returned. + */ + static Key + forEntry (Key const& parent, const void* bufferAddr) + { + Key newKey(parent); + if (bufferAddr) + { + newKey.parent_ = HashVal(parent); + newKey.hashID_ = chainedHash(parent, bufferAddr); + } + return newKey; + } + + HashVal parentKey() const { return parent_;} operator HashVal() const { return hashID_;} }; @@ -380,7 +399,7 @@ namespace engine { protected: Entry (Key const& parent, const void* bufferPtr =0) - : Key(parent) + : Key (Key::forEntry (parent, bufferPtr)) , state_(bufferPtr? LOCKED:NIL) , buffer_(bufferPtr) { } @@ -409,20 +428,6 @@ namespace engine { return !bool(buffer_); } - /** @note hiding the base implementation to support storing - * and lookup of individual entries in a hashtable */ - operator HashVal() const - { - return isTypeKey()? Key::operator HashVal() - : chainedHash (parentKey(), buffer_); - } - - HashVal - parentKey() const - { - return isTypeKey()? Key::parentKey() - : Key::operator HashVal(); - } BufferState state() const From ccd130966beb44bf4d4a840a72cf01641aed38fa Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 30 Oct 2011 02:19:10 +0200 Subject: [PATCH 242/496] finish and pass the first round of tests still missing: - implementation of a Mock frame - automatical invocation of the TypeHandler --- src/proc/engine/buffer-metadata.hpp | 2 ++ .../proc/engine/buffer-metadata-test.cpp | 20 +++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 21ed1c50a..500d1994b 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -452,7 +452,9 @@ namespace engine { __must_not_be_FREE(); if ( (state_ == LOCKED && newState == EMITTED) + ||(state_ == LOCKED && newState == FREE) ||(state_ == EMITTED && newState == BLOCKED) + ||(state_ == EMITTED && newState == FREE) ||(state_ == BLOCKED && newState == FREE)) { // allowed transition diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp index dd55542e2..b32da07ab 100644 --- a/tests/components/proc/engine/buffer-metadata-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -91,7 +91,6 @@ namespace test { CHECK (ensure_proper_fixture()); verifyBasicProperties(); verifyStandardCase(); - UNIMPLEMENTED ("cover all metadata properties"); } @@ -219,12 +218,12 @@ namespace test { // a real-world BufferProvider would use some kind of allocator // track individual buffers by metadata entries - metadata::Entry f0 = meta_->markLocked(bufferType1, &frames[0]); - metadata::Entry f1 = meta_->markLocked(bufferType1, &frames[1]); - metadata::Entry f2 = meta_->markLocked(bufferType1, &frames[2]); + metadata::Entry& f0 = meta_->markLocked(bufferType1, &frames[0]); + metadata::Entry& f1 = meta_->markLocked(bufferType1, &frames[1]); + metadata::Entry& f2 = meta_->markLocked(bufferType1, &frames[2]); - metadata::Entry r0 = meta_->markLocked(bufferType1, &rawbuf[0]); - metadata::Entry r1 = meta_->markLocked(bufferType1, &rawbuf[1]); + metadata::Entry& r0 = meta_->markLocked(rawBuffType, &rawbuf[0]); + metadata::Entry& r1 = meta_->markLocked(rawBuffType, &rawbuf[1]); CHECK (LOCKED == f0.state()); CHECK (LOCKED == f1.state()); @@ -254,6 +253,11 @@ namespace test { //////////////////TODO: access the storage through the metadata-key //////////////////TODO: to a state transition on the metadata + f0.mark(FREE); + f1.mark(FREE); + f2.mark(FREE); + r0.mark(FREE); + r1.mark(FREE); attachTestFrame.destroyAttached (frames+0); ////////////////////////////////////////TODO: shouldn't this happen automatically?? attachTestFrame.destroyAttached (frames+1); @@ -265,6 +269,10 @@ namespace test { meta_->release(handle_r0); meta_->release(handle_r1); + // manual cleanup of test allocations + delete[] frames; + delete[] rawbuf; + CHECK (!meta_->isLocked(handle_f0)); CHECK (!meta_->isLocked(handle_f1)); CHECK (!meta_->isLocked(handle_f2)); From 931dc3f8839094aceb293f90149fdc81841f6587 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 30 Oct 2011 05:35:36 +0100 Subject: [PATCH 243/496] draft: automatically invoke an attached ctor/dtor functor --- src/proc/engine/buffer-metadata.hpp | 132 ++++++++++++------ .../proc/engine/buffer-metadata-test.cpp | 61 ++++++-- tests/components/proc/engine/testframe.hpp | 19 +++ 3 files changed, 157 insertions(+), 55 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 500d1994b..f86d1cdaf 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -395,10 +395,10 @@ namespace engine { : public Key { BufferState state_; - const void* buffer_; + void* buffer_; protected: - Entry (Key const& parent, const void* bufferPtr =0) + Entry (Key const& parent, void* bufferPtr =0) : Key (Key::forEntry (parent, bufferPtr)) , state_(bufferPtr? LOCKED:NIL) , buffer_(bufferPtr) @@ -435,8 +435,8 @@ namespace engine { return state_; } - const void* - access() const + void* + access() { __must_not_be_NIL(); __must_not_be_FREE(); @@ -458,7 +458,8 @@ namespace engine { ||(state_ == BLOCKED && newState == FREE)) { // allowed transition - if (newState == FREE) buffer_ = 0; + if (newState == FREE) + invokeEmbeddedDtor_and_clear(); state_ = newState; return *this; } @@ -466,6 +467,29 @@ namespace engine { throw error::Fatal ("Invalid buffer state encountered."); } + protected: + /** @internal maybe invoke a registered TypeHandler's + * constructor function, which typically builds some + * content object into the buffer by placement new. */ + void + invokeEmbeddedCtor() + { + REQUIRE (buffer_); + if (nontrivial (instanceFunc_)) + instanceFunc_.createAttached (buffer_); + } + + /** @internal maybe invoke a registered TypeHandler's + * destructor function, which typically clears up some + * content object living within the buffer */ + void + invokeEmbeddedDtor_and_clear() + { + REQUIRE (buffer_); + if (nontrivial (instanceFunc_)) + instanceFunc_.destroyAttached (buffer_); + buffer_ = 0; + } private: void @@ -668,9 +692,9 @@ namespace engine { * @note might create/register a new Entry as a side-effect */ Key const& - key (Key const& parentKey, const void* concreteBuffer) + key (Key const& parentKey, void* concreteBuffer) { - return get (parentKey,concreteBuffer); + return lock (parentKey,concreteBuffer); } /** core operation to access or create a concrete buffer metadata entry. @@ -678,6 +702,11 @@ namespace engine { * which denotes a buffer type, and the concrete buffer address. If yet * unknown, a new concrete buffer metadata Entry is created and initialised * to LOCKED state. Otherwise just the existing Entry is fetched. + * @note this function really \em activates the buffer. + * In case the type (Key) involves a TypeHandler (functor), + * its constructor function will be invoked, if actually a new + * entry gets created. Typically this mechanism will be used + * to placement-create an object into the buffer. * @param parentKey a key describing the \em type of the buffer * @param concreteBuffer storage pointer, must not be NULL * @param onlyNew disallow fetching an existing entry @@ -691,7 +720,7 @@ namespace engine { * buffer is released or re-used later. */ Entry& - get (Key const& parentKey, const void* concreteBuffer, bool onlyNew =false) + lock (Key const& parentKey, void* concreteBuffer, bool onlyNew =false) { if (!concreteBuffer) throw error::Invalid ("Attempt to lock a slot for a NULL buffer" @@ -705,7 +734,7 @@ namespace engine { , error::LUMIERA_ERROR_LIFECYCLE ); if (!existing) - return table_.store (newEntry); + return store_and_lock (newEntry); // actual creation else return *existing; } @@ -743,10 +772,45 @@ namespace engine { } - /* == memory management == */ - Entry& markLocked (Key const& parentKey, const void* buffer); - void release (HashVal key); + /* == memory management operations == */ + + /** combine the type (Key) with a concrete buffer, + * thereby marking this buffer as locked. Store a concrete + * metadata Entry to account for this fact. This might include + * invoking a constructor function, in case the type (Key) + * defines a (nontrivial) TypeHandler. + * @throw error::Fatal when locking a NULL buffer + * @throw exceptions which might be raised by a TypeHandler's + * constructor function. In this case, the Entry remains + * created, but is marked as FREE + */ + Entry& + markLocked (Key const& parentKey, void* buffer) + { + if (!buffer) + throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?" + , error::LUMIERA_ERROR_BOTTOM_VALUE); + + return this->lock(parentKey, buffer, true); // force creation of a new entry + } + + /** purge the bare metadata Entry from the metadata tables. + * @throw error::Logic if the entry isn't marked FREE already + */ + void + release (HashVal key) + { + Entry* entry = table_.fetch (key); + if (!entry) return; + if (entry && (FREE != entry->state())) + throw error::Logic ("Attempt to release a buffer still in use" + , error::LUMIERA_ERROR_LIFECYCLE); + + table_.remove (key); + } + + private: @@ -765,40 +829,28 @@ namespace engine { if (isKnown (key)) return; table_.store (Entry (key, NULL)); } + + Entry& + store_and_lock (Entry const& metadata) + { + Entry& newEntry = table_.store (metadata); + try + { + newEntry.invokeEmbeddedCtor(); + } + catch(...) + { + newEntry.mark(FREE); + throw; + } + return newEntry; + } }; - - - - - /** */ - inline BufferMetadata::Entry& - BufferMetadata::markLocked (Key const& parentKey, const void* buffer) - { - if (!buffer) - throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?" - , error::LUMIERA_ERROR_BOTTOM_VALUE); - - return this->get (parentKey, buffer, true); // force creation of a new entry - } - - inline void - BufferMetadata::release (HashVal key) - { - Entry* entry = table_.fetch (key); - if (!entry) return; - if (entry && (FREE != entry->state())) - throw error::Logic ("Attempt to release a buffer still in use" - , error::LUMIERA_ERROR_LIFECYCLE); - - table_.remove (key); - } - - } // namespace engine #endif diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp index b32da07ab..85818ba6f 100644 --- a/tests/components/proc/engine/buffer-metadata-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -37,13 +37,16 @@ //#include <boost/format.hpp> #include <boost/scoped_ptr.hpp> #include <cstdlib> +#include <cstring> //#include <iostream> //using boost::format; //using std::string; //using std::cout; //using util::for_each; +using std::strncpy; using boost::scoped_ptr; +using lib::test::randStr; using util::isnil; using util::isSameObject; @@ -66,13 +69,24 @@ namespace test { 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 void* const SOME_POINTER = &JUST_SOMETHING; + HashVal JUST_SOMETHING = 123; + void* const SOME_POINTER = &JUST_SOMETHING; // const uint TEST_SIZE = 1024*1024; // const uint TEST_ELMS = 20; - - } + + template<typename TY> + TY& + accessAs (metadata::Entry& entry) + { + TY* ptr = reinterpret_cast<TY*> (entry.access()); + ASSERT (ptr); + return *ptr; + } + }//(End) Test fixture and helpers + + + /******************************************************************* @@ -231,10 +245,6 @@ namespace test { CHECK (LOCKED == r0.state()); CHECK (LOCKED == r1.state()); - // for the TestFrame buffers, additionally we'd have to create/attach an object - attachTestFrame.createAttached (frames+0); ////////////////////////////////////////TODO: shouldn't this happen automatically?? - attachTestFrame.createAttached (frames+1); //////////////////////////TODO: answer: yes. Metadata is exactly the entity which has all necessary information - attachTestFrame.createAttached (frames+2); CHECK (f0.access() == frames+0); CHECK (f1.access() == frames+1); @@ -242,6 +252,11 @@ namespace test { CHECK (r0.access() == rawbuf+0); CHECK (r1.access() == rawbuf+1); + TestFrame defaultFrame; + CHECK (defaultFrame == f0.access()); + CHECK (defaultFrame == f1.access()); + CHECK (defaultFrame == f2.access()); + // at that point, we'd return BuffHandles to the client HashVal handle_f0(f0); HashVal handle_f1(f1); @@ -249,19 +264,31 @@ namespace test { HashVal handle_r0(r0); HashVal handle_r1(r1); - // client uses the buffers + // client uses the buffers---------------------(Start) + accessAs<TestFrame> (f0) = testData(1); + accessAs<TestFrame> (f1) = testData(2); + accessAs<TestFrame> (f2) = testData(3); - //////////////////TODO: access the storage through the metadata-key - //////////////////TODO: to a state transition on the metadata - f0.mark(FREE); + CHECK (TestFrame::isAlive (f0.access())); + CHECK (TestFrame::isAlive (f1.access())); + CHECK (TestFrame::isAlive (f2.access())); + + strncpy (& accessAs<char> (r0), randStr(SIZE_B - 1).c_str(), SIZE_B); + strncpy (& accessAs<char> (r1), randStr(SIZE_B - 1).c_str(), SIZE_B); + + // client might trigger some state transitions + f0.mark(EMITTED); + f1.mark(EMITTED); + f1.mark(BLOCKED); + // client uses the buffers---------------------(End) + + + f0.mark(FREE); // note: this implicitly invoked the embedded dtor f1.mark(FREE); f2.mark(FREE); r0.mark(FREE); r1.mark(FREE); - attachTestFrame.destroyAttached (frames+0); ////////////////////////////////////////TODO: shouldn't this happen automatically?? - attachTestFrame.destroyAttached (frames+1); - attachTestFrame.destroyAttached (frames+2); meta_->release(handle_f0); meta_->release(handle_f1); @@ -269,6 +296,10 @@ namespace test { meta_->release(handle_r0); meta_->release(handle_r1); + CHECK (TestFrame::isDead (&frames[0])); + CHECK (TestFrame::isDead (&frames[1])); + CHECK (TestFrame::isDead (&frames[2])); + // manual cleanup of test allocations delete[] frames; delete[] rawbuf; diff --git a/tests/components/proc/engine/testframe.hpp b/tests/components/proc/engine/testframe.hpp index ba71d6646..d9e7b7b20 100644 --- a/tests/components/proc/engine/testframe.hpp +++ b/tests/components/proc/engine/testframe.hpp @@ -56,6 +56,25 @@ namespace test { public: + /** Helper to verify a given memory location holds + * an active TestFrame instance (created, not yet destroyed) + * @return true if the TestFrame datastructure is intact and + * marked as still alive. + */ + static bool + isAlive (void* memLocation) + { + UNIMPLEMENTED ("access memory as TestFrame and check internal accounting"); + } + + /** Helper to verify a given memory location holds + * an already destroyed TestFrame instance */ + static bool + isDead (void* memLocation) + { + UNIMPLEMENTED ("access memory as TestFrame and verify dtor invocation"); + } + bool operator== (void* memLocation) { From a14e4f7ccd9233d1a0148cba7e9920c3751feb8e Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 31 Oct 2011 00:13:35 +0100 Subject: [PATCH 244/496] define a facility to generate test frames --- tests/46engine.tests | 8 +- .../components/proc/engine/testframe-test.cpp | 178 ++++++++++++++++++ tests/components/proc/engine/testframe.cpp | 141 ++++++++++++++ tests/components/proc/engine/testframe.hpp | 64 +++---- 4 files changed, 355 insertions(+), 36 deletions(-) create mode 100644 tests/components/proc/engine/testframe-test.cpp create mode 100644 tests/components/proc/engine/testframe.cpp diff --git a/tests/46engine.tests b/tests/46engine.tests index e2f704018..aca5be526 100644 --- a/tests/46engine.tests +++ b/tests/46engine.tests @@ -2,12 +2,18 @@ TESTING "Component Test Suite: Render Engine parts" ./test-components --group=en +PLANNED "Test support: dummy frames" TestFrame_test <<END +return: 0 +END + + PLANNED "Buffer provider diagnostics" BufferProviderProtocol_test <<END return: 0 END -PLANNED "buffer metadata type keys" BufferMetadataKey_test <<END +TEST "buffer metadata type keys" BufferMetadataKey_test <<END +return: 0 END diff --git a/tests/components/proc/engine/testframe-test.cpp b/tests/components/proc/engine/testframe-test.cpp new file mode 100644 index 000000000..efac01b44 --- /dev/null +++ b/tests/components/proc/engine/testframe-test.cpp @@ -0,0 +1,178 @@ +/* + TestFrame(Test) - verify proper operation of dummy data frames + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "proc/engine/testframe.hpp" + +//#include <iostream> +#include <boost/scoped_ptr.hpp> +#include <cstdlib> +#include <limits.h> + +using test::Test; +//using std::cout; +using std::rand; +using boost::scoped_ptr; +//using std::memset; + + +namespace engine{ +namespace test { + + namespace { // used internally + + const uint CHAN_COUNT = 30; // independent families of test frames to generate + const uint NUM_FRAMES = 1000; // number of test frames in each of these families + + + void + corruptMemory(void* base, uint offset, uint count) + { + char* accessor = reinterpret_cast<char*> (base); + while (count--) + accessor[offset+count] = rand() % CHAR_MAX; + } + + } // (End) internal defs + + + + /******************************************************************* + * @test verify test helper for engine tests: a dummy data frame. + * TestFrame instances can be created right away, without any + * external library dependencies. A test frame is automatically + * filled with random data; multiple frames are arranged in + * sequences and channels, causing the random data to be + * reproducible yet different in each frame. + * + * To ease writing unit tests, TestFrame provides comparison + * and assignment and tracks lifecycle automatically. As tests + * regarding the engine typically have to deal with buffer + * management, an arbitrary memory location can be interpreted + * as TestFrame and checked for corruption. + */ + class TestFrame_test : public Test + { + + virtual void + run (Arg) + { + verifyBasicProperties(); + verifyFrameLifecycle(); + verifyFrameSeries(); + } + + + void + verifyBasicProperties() + { + CHECK (1024 < sizeof(TestFrame)); + + TestFrame frameA; + TestFrame frameB; + TestFrame frameC(5); + + CHECK (frameA == frameB); + CHECK (frameA != frameC); + CHECK (frameB != frameC); + + CHECK (frameA.isAlive()); + CHECK (frameB.isAlive()); + CHECK (frameC.isAlive()); + + CHECK (frameA.isSane()); + CHECK (frameB.isSane()); + CHECK (frameC.isSane()); + + void * frameMem = &frameB; + + CHECK (frameA == frameMem); + corruptMemory(frameMem,20,5); + CHECK (!frameB.isSane()); + + frameB = frameC; + + CHECK (frameB.isSane()); + CHECK (frameA != frameB); + CHECK (frameA != frameC); + CHECK (frameB == frameC); + } + + + void + verifyFrameLifecycle() + { + CHECK (!TestFrame::isDead (this)); + CHECK (!TestFrame::isAlive (this)); + + TestFrame* onHeap = new TestFrame(23); + CHECK ( TestFrame::isAlive (onHeap)); + CHECK (!onHeap->isDead()); + CHECK (onHeap->isAlive()); + CHECK (onHeap->isSane()); + + delete onHeap; + CHECK ( TestFrame::isDead (onHeap)); + CHECK (!TestFrame::isAlive (onHeap)); + } + + + /** @test build sequences of test frames, + * organised into multiple families (channels). + * Verify that adjacent frames hold differing data + */ + void + verifyFrameSeries() + { + scoped_ptr<TestFrame> thisFrames[CHAN_COUNT]; + scoped_ptr<TestFrame> prevFrames[CHAN_COUNT]; + + for (uint i=0; i<CHAN_COUNT; ++i) + thisFrames[i].reset (new TestFrame(0, i)); + + for (uint nr=1; nr<NUM_FRAMES; ++nr) + for (uint i=0; i<CHAN_COUNT; ++i) + { + prevFrames[i].swap (thisFrames[i]); + thisFrames[i].reset (new TestFrame(nr, i)); + CHECK (thisFrames[i]->isSane()); + CHECK (prevFrames[i]->isSane()); + CHECK (prevFrames[i]->isAlive()); + + CHECK (*thisFrames[i] != *prevFrames[i]); // differs from predecessor in the same channel + + for (uint j=0; j<CHAN_COUNT; ++j) + if (j!=i) + { + CHECK (*thisFrames[i] != *thisFrames[j]); // differs from frames in other channels at this point + CHECK (*thisFrames[i] != *prevFrames[j]); // differs cross wise from predecessors in other channels + } } } + }; + + + /** Register this test class... */ + LAUNCHER (TestFrame_test, "unit engine"); + + + +}} // namespace engine::test diff --git a/tests/components/proc/engine/testframe.cpp b/tests/components/proc/engine/testframe.cpp new file mode 100644 index 000000000..da6c7108d --- /dev/null +++ b/tests/components/proc/engine/testframe.cpp @@ -0,0 +1,141 @@ +/* + TestFrame - test data frame (stub) for checking Render engine functionality + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/engine/testframe.hpp" +#include "lib/error.hpp" + +#include <cstring> + +using std::memcpy; + +namespace engine { +namespace test { + + + namespace error = lumiera::error; + + namespace { // hidden local support facilities.... + + } // (End) hidden impl details + + + TestFrame::~TestFrame() { } // emit VTables here.... + + + TestFrame::TestFrame(uint seq, uint family) + : discriminator_() + , stage_(CREATED) + { } + + + TestFrame::TestFrame (TestFrame const& o) + : discriminator_(o.discriminator_) + , stage_(CREATED) + { + memcpy (data_, o.data_, BUFFSIZ); + } + + TestFrame& + TestFrame::operator= (TestFrame const& o) + { + if (DISCARDED == stage_) + throw new error::Logic ("target TestFrame is already dead"); + if (this != &o) + { + discriminator_ = o.discriminator_; + stage_ = CREATED; + memcpy (data_, o.data_, BUFFSIZ); + } + return *this; + } + + + + /** whether this output slot is occupied + * @return true if currently unconnected and + * able to connect and handle output data + */ + bool + TestFrame::isAlive (void* memLocation) + { + UNIMPLEMENTED ("access memory as TestFrame and check internal accounting"); + } + + /** Helper to verify a given memory location holds + * an already destroyed TestFrame instance */ + bool + TestFrame::isDead (void* memLocation) + { + UNIMPLEMENTED ("access memory as TestFrame and verify dtor invocation"); + } + + bool + TestFrame::operator== (void* memLocation) const + { + UNIMPLEMENTED ("verify contents of an arbitrary memory location"); + } + + bool + TestFrame::contentEquals (TestFrame const& o) const + { + UNIMPLEMENTED ("equality of test data frames"); + } + + bool + TestFrame::isAlive() const + { + UNIMPLEMENTED ("sanity & lifecycle"); + } + + bool + TestFrame::isDead() const + { + UNIMPLEMENTED ("sanity & lifecycle"); + } + + bool + TestFrame::isSane() const + { + UNIMPLEMENTED ("sanity & lifecycle"); + } + + + + + + 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)"); + } + + + + +}} // namespace engine::test diff --git a/tests/components/proc/engine/testframe.hpp b/tests/components/proc/engine/testframe.hpp index d9e7b7b20..f1b780e16 100644 --- a/tests/components/proc/engine/testframe.hpp +++ b/tests/components/proc/engine/testframe.hpp @@ -28,6 +28,8 @@ //#include "lib/time/timevalue.hpp" //#include <string> +#include <cstdlib> +#include <stdint.h> //using std::tr1::shared_ptr; @@ -53,60 +55,52 @@ namespace test { */ class TestFrame { + enum StageOfLife { + CREATED, EMITTED, DISCARDED + }; + + static const size_t BUFFSIZ = 1024; + + uint64_t discriminator_; + StageOfLife stage_; + + char data_[BUFFSIZ]; public: + ~TestFrame(); + TestFrame (uint seq=0, uint family=0); + TestFrame (TestFrame const&); + TestFrame& operator= (TestFrame const&); /** Helper to verify a given memory location holds * an active TestFrame instance (created, not yet destroyed) * @return true if the TestFrame datastructure is intact and * marked as still alive. */ - static bool - isAlive (void* memLocation) - { - UNIMPLEMENTED ("access memory as TestFrame and check internal accounting"); - } + static bool isAlive (void* memLocation); /** Helper to verify a given memory location holds * an already destroyed TestFrame instance */ - static bool - isDead (void* memLocation) - { - UNIMPLEMENTED ("access memory as TestFrame and verify dtor invocation"); - } + static bool isDead (void* memLocation); - bool - operator== (void* memLocation) - { - UNIMPLEMENTED ("verify contents of an arbitrary memory location"); - } + bool isAlive() const; + bool isDead() const; + bool isSane() const; - friend bool - operator== (TestFrame const& f1, TestFrame const& f2) - { - UNIMPLEMENTED ("equality of test data frames"); - } + bool operator== (void* memLocation) const; - friend bool - operator!= (TestFrame const& f1, TestFrame const& f2) - { - return !(f1 == f2); - } + friend bool operator== (TestFrame const& f1, TestFrame const& f2) { return f1.contentEquals(f2); } + friend bool operator!= (TestFrame const& f1, TestFrame const& f2) { return !f1.contentEquals(f2); } + + private: + bool contentEquals (TestFrame const& o) const; }; - inline TestFrame - testData (uint seqNr) - { - UNIMPLEMENTED ("build, memorise and expose test data frames on demand"); - } + TestFrame testData (uint seqNr); - inline TestFrame - testData (uint chanNr, uint seqNr) - { - UNIMPLEMENTED ("build, memorise and expose test data frames on demand (multi-channel)"); - } + TestFrame testData (uint chanNr, uint seqNr); From 20777cca8a9c0e549421dda8e669a8b4839e1991 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 31 Oct 2011 02:20:32 +0100 Subject: [PATCH 245/496] implemented testframe, not passing all tests yet --- .../components/proc/engine/testframe-test.cpp | 20 +- tests/components/proc/engine/testframe.cpp | 214 +++++++++++++++--- tests/components/proc/engine/testframe.hpp | 29 ++- 3 files changed, 216 insertions(+), 47 deletions(-) diff --git a/tests/components/proc/engine/testframe-test.cpp b/tests/components/proc/engine/testframe-test.cpp index efac01b44..29e96b9c5 100644 --- a/tests/components/proc/engine/testframe-test.cpp +++ b/tests/components/proc/engine/testframe-test.cpp @@ -24,16 +24,13 @@ #include "lib/test/run.hpp" #include "proc/engine/testframe.hpp" -//#include <iostream> -#include <boost/scoped_ptr.hpp> #include <cstdlib> #include <limits.h> +#include <boost/scoped_ptr.hpp> using test::Test; -//using std::cout; using std::rand; using boost::scoped_ptr; -//using std::memset; namespace engine{ @@ -52,7 +49,6 @@ namespace test { while (count--) accessor[offset+count] = rand() % CHAR_MAX; } - } // (End) internal defs @@ -153,7 +149,7 @@ namespace test { for (uint nr=1; nr<NUM_FRAMES; ++nr) for (uint i=0; i<CHAN_COUNT; ++i) { - prevFrames[i].swap (thisFrames[i]); + thisFrames[i].swap (prevFrames[i]); thisFrames[i].reset (new TestFrame(nr, i)); CHECK (thisFrames[i]->isSane()); CHECK (prevFrames[i]->isSane()); @@ -161,12 +157,12 @@ namespace test { CHECK (*thisFrames[i] != *prevFrames[i]); // differs from predecessor in the same channel - for (uint j=0; j<CHAN_COUNT; ++j) - if (j!=i) - { - CHECK (*thisFrames[i] != *thisFrames[j]); // differs from frames in other channels at this point - CHECK (*thisFrames[i] != *prevFrames[j]); // differs cross wise from predecessors in other channels - } } } + for (uint j=0; j<i; ++j) + { + ENSURE (j!=i); + CHECK (*thisFrames[i] != *thisFrames[j]); // differs from frames in other channels at this point + CHECK (*thisFrames[i] != *prevFrames[j], "nr=%d, i=%d, j=%d", nr, i,j); ///////FIXME getting matches here // differs cross wise from predecessors in other channels + } } } }; diff --git a/tests/components/proc/engine/testframe.cpp b/tests/components/proc/engine/testframe.cpp index da6c7108d..e2293b0de 100644 --- a/tests/components/proc/engine/testframe.cpp +++ b/tests/components/proc/engine/testframe.cpp @@ -24,9 +24,14 @@ #include "proc/engine/testframe.hpp" #include "lib/error.hpp" +#include <boost/scoped_ptr.hpp> + +#include <limits.h> #include <cstring> +#include <vector> using std::memcpy; +using std::vector; namespace engine { namespace test { @@ -36,16 +41,141 @@ namespace test { namespace { // hidden local support facilities.... + /** @internal helper for generating unique test frames. + * The "discriminator" is used as a stepping size when + * filling the test frame data buffers. It is generated + * to be different on adjacent frames of the same series, + * as well as to differ to all near by neighbouring channels. + * @param seq the sequence number of the frame within the channel + * @param family the channel this frame belongs to + */ + uint64_t + generateDiscriminator(uint seq, uint family) + { + // random offset, but fixed per executable run + static uint base(10 + rand() % 90); + + // use the family as stepping + return (seq+1) * (base+family); + } + + + TestFrame& + accessAsTestFrame (void* memoryLocation) + { + REQUIRE (memoryLocation); + return *reinterpret_cast<TestFrame*> (memoryLocation); + } + + + /** + * @internal table to hold test data frames. + * These frames are built on demand, but retained thereafter. + * Some tests might rely on the actual memory locations, using the + * test frames to simulate a real input frame data stream. + * @param CHA the maximum number of channels to expect + * @param FRA the maximum number of frames to expect per channel + * @warning choose the maximum number parameters wisely. + * We're allocating memory to hold a table of test frames + * e.g. sizeof(TestFrame) * 20channels * 100frames ≈ 2 MiB + * The table uses vectors, and thus will grow on demand, + * but this might cause existing frames to be relocated in memory; + * some tests might rely on fixed memory locations. Just be cautious! + */ + template<uint CHA, uint FRA> + struct TestFrameTable + : vector<vector<TestFrame> > + { + typedef vector<vector<TestFrame> > VECT; + + TestFrameTable() + : VECT(CHA) + { + for (uint i=0; i<CHA; ++i) + at(i).reserve(FRA); + } + + TestFrame& + getFrame (uint seqNr, uint chanNr=0) + { + if (chanNr >= this->size()) + { + WARN (test, "Growing table of test frames to %d channels, " + "which is > the default (%d)", chanNr, CHA); + resize(chanNr+1); + } + ENSURE (chanNr < this->size()); + vector<TestFrame>& channel = at(chanNr); + + if (seqNr >= channel.size()) + { + WARN_IF (seqNr >= FRA, test, + "Growing channel #%d of test frames to %d elements, " + "which is > the default (%d)", chanNr, seqNr, FRA); + for (uint i=channel.size(); i<=seqNr; ++i) + channel.push_back (TestFrame (seqNr,chanNr)); + } + ENSURE (seqNr < channel.size()); + + return channel[seqNr]; + } + }; + + const uint INITIAL_CHAN = 20; + const uint INITIAL_FRAMES = 100; + + typedef TestFrameTable<INITIAL_CHAN,INITIAL_FRAMES> TestFrames; + + boost::scoped_ptr<TestFrames> testFrames; + + TestFrame& + accessTestFrame (uint seqNr, uint chanNr) + { + if (!testFrames) testFrames.reset (new TestFrames); + + return testFrames->getFrame(seqNr,chanNr); + } + } // (End) hidden impl details - TestFrame::~TestFrame() { } // emit VTables here.... + + + TestFrame& + testData (uint seqNr) + { + return accessTestFrame (seqNr, 0); + } + + TestFrame& + testData (uint chanNr, uint seqNr) + { + return accessTestFrame (seqNr,chanNr); + } + + void + resetTestFrames() + { + testFrames.reset(0); + } + + + + + + TestFrame::~TestFrame() + { + stage_ = DISCARDED; + } TestFrame::TestFrame(uint seq, uint family) - : discriminator_() + : discriminator_(generateDiscriminator(seq,family)) , stage_(CREATED) - { } + { + ASSERT (0 < discriminator_); + buildData(); + } TestFrame::TestFrame (TestFrame const& o) @@ -71,71 +201,91 @@ namespace test { - /** whether this output slot is occupied - * @return true if currently unconnected and - * able to connect and handle output data + /** @note performing an unchecked conversion of the given + * memory location to be accessed as TestFrame. + * The sanity of the data found at that location + * is checked as well, not only the lifecycle flag. */ bool TestFrame::isAlive (void* memLocation) { - UNIMPLEMENTED ("access memory as TestFrame and check internal accounting"); + TestFrame& candidate (accessAsTestFrame (memLocation)); + return candidate.isSane() + && candidate.isAlive(); } - /** Helper to verify a given memory location holds - * an already destroyed TestFrame instance */ bool TestFrame::isDead (void* memLocation) { - UNIMPLEMENTED ("access memory as TestFrame and verify dtor invocation"); + TestFrame& candidate (accessAsTestFrame (memLocation)); + return candidate.isSane() + && candidate.isDead(); } bool TestFrame::operator== (void* memLocation) const { - UNIMPLEMENTED ("verify contents of an arbitrary memory location"); + TestFrame& candidate (accessAsTestFrame (memLocation)); + return candidate.isSane() + && candidate == *this; } bool TestFrame::contentEquals (TestFrame const& o) const { - UNIMPLEMENTED ("equality of test data frames"); + for (uint i=0; i<BUFFSIZ; ++i) + if (data_[i] != o.data_[i]) + return false; + return true; } + bool + TestFrame::verifyData() const + { + char c(0); + for (uint i=0; i<BUFFSIZ; ++i) + if (data_[i] != (c=generate(c))) + return false; + return true; + } + + void + TestFrame::buildData() + { + char c(0); + for (uint i=0; i<BUFFSIZ; ++i) + data_[i] = (c=generate(c)); + } + + char + TestFrame::generate (char prev) const + { + return (discriminator_ + prev) % CHAR_MAX; + } + + bool TestFrame::isAlive() const { - UNIMPLEMENTED ("sanity & lifecycle"); + return (CREATED == stage_) + || (EMITTED == stage_); } bool TestFrame::isDead() const { - UNIMPLEMENTED ("sanity & lifecycle"); + return (DISCARDED == stage_); } bool TestFrame::isSane() const { - UNIMPLEMENTED ("sanity & lifecycle"); + return ( (CREATED == stage_) + ||(EMITTED == stage_) + ||(DISCARDED == stage_)) + && verifyData(); } - - - - - 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)"); - } - - }} // namespace engine::test diff --git a/tests/components/proc/engine/testframe.hpp b/tests/components/proc/engine/testframe.hpp index f1b780e16..347e4b324 100644 --- a/tests/components/proc/engine/testframe.hpp +++ b/tests/components/proc/engine/testframe.hpp @@ -49,7 +49,14 @@ namespace test { * Placeholder functions are provided for assignment (simulating the actual * calculations); additional diagnostic functions allow to verify the * performed operations after-the fact - * + * + * Each TestFrame is automatically filled with pseudo random data; + * multiple frames are arranged in sequences and channels, causing the random data + * to be reproducible yet different in each frame. The lifecycle of each TestFrame + * is tracked and marked in an embedded state field. Moreover, the contents of + * the data block can be verified, because the sequence of bytes is reproducible, + * based on the channel and sequence number of the test frame. + * * @todo WIP-WIP-WIP 9/11 * */ @@ -94,13 +101,29 @@ namespace test { private: bool contentEquals (TestFrame const& o) const; + bool verifyData() const; + char generate (char) const; + + void buildData (); }; - TestFrame testData (uint seqNr); + /** Helper to access a specific frame of test data at a fixed memory location. + * The series of test frames is generated on demand, but remains in memory thereafter, + * similar to real data accessible from some kind of source stream. Each of these generated + * test frames filled with different yet reproducible pseudo random data. + * Client code is free to access and corrupt this data. + */ + TestFrame& testData (uint seqNr); - TestFrame testData (uint chanNr, uint seqNr); + TestFrame& testData (uint chanNr, uint seqNr); + + + /** discards all the TestFrame instances and + * initialises an empty table of test frames */ + void resetTestFrames(); + From db3ea6638daab220954f4f1a65c8c721374dc4fc Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 1 Nov 2011 03:11:43 +0100 Subject: [PATCH 246/496] use boost::random to ensure distinct test frame contents --- src/tool/try.cpp | 52 +++++++--------------- tests/46engine.tests | 2 +- tests/components/proc/engine/testframe.cpp | 39 ++++++++-------- tests/components/proc/engine/testframe.hpp | 31 ++++--------- 4 files changed, 44 insertions(+), 80 deletions(-) diff --git a/src/tool/try.cpp b/src/tool/try.cpp index 9e68a90a5..7b73f4c1f 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -20,53 +20,31 @@ // 1/11 - integer floor and wrap operation(s) // 1/11 - how to fetch the path of the own executable -- at least under Linux? // 10/11 - simple demo using a pointer and a struct +// 11/11 - using the boost random number generator(s) #include <iostream> +#include <boost/lexical_cast.hpp> +#include <boost/random/linear_congruential.hpp> + +using boost::lexical_cast; using std::cout; - - - -/** - * 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; i<length_; ++i) - data_[i] = *(tmp+i); - } - }; - - -// define a global variable holding a MyStruct -MyStruct theStruct; - - - -void -printMyStruct(MyStruct* myPointer) - { - for (int i=0; i < myPointer->length_; ++i) - cout << myPointer->data_[i]; - - cout << "\n"; - } +using std::endl; int -main (int, char**) //(int argc, char* argv[]) +main (int cnt, char* argv[]) { - printMyStruct (&theStruct); + + int32_t seed = (2 == cnt)? lexical_cast<int32_t> (argv[1]) : 42; + + boost::rand48 ranGen(seed); + + cout << "seed = "<< seed << endl; + for (uint i=0; i< 100; ++i) + cout << ranGen() % CHAR_MAX <<"__"; cout << "\n.gulp.\n"; diff --git a/tests/46engine.tests b/tests/46engine.tests index aca5be526..293dc00c7 100644 --- a/tests/46engine.tests +++ b/tests/46engine.tests @@ -2,7 +2,7 @@ TESTING "Component Test Suite: Render Engine parts" ./test-components --group=en -PLANNED "Test support: dummy frames" TestFrame_test <<END +TEST "Test support: dummy frames" TestFrame_test <<END return: 0 END diff --git a/tests/components/proc/engine/testframe.cpp b/tests/components/proc/engine/testframe.cpp index e2293b0de..3822f4b30 100644 --- a/tests/components/proc/engine/testframe.cpp +++ b/tests/components/proc/engine/testframe.cpp @@ -24,25 +24,30 @@ #include "proc/engine/testframe.hpp" #include "lib/error.hpp" +#include <boost/random/linear_congruential.hpp> #include <boost/scoped_ptr.hpp> #include <limits.h> #include <cstring> #include <vector> -using std::memcpy; -using std::vector; + namespace engine { namespace test { + using std::vector; + using std::memcpy; + + typedef boost::rand48 PseudoRandom; + namespace error = lumiera::error; namespace { // hidden local support facilities.... /** @internal helper for generating unique test frames. - * The "discriminator" is used as a stepping size when + * This "discriminator" is used as a random seed when * filling the test frame data buffers. It is generated * to be different on adjacent frames of the same series, * as well as to differ to all near by neighbouring channels. @@ -50,10 +55,10 @@ namespace test { * @param family the channel this frame belongs to */ uint64_t - generateDiscriminator(uint seq, uint family) + generateDistinction(uint seq, uint family) { // random offset, but fixed per executable run - static uint base(10 + rand() % 90); + static uint base(10 + rand() % 990); // use the family as stepping return (seq+1) * (base+family); @@ -128,6 +133,7 @@ namespace test { boost::scoped_ptr<TestFrames> testFrames; + TestFrame& accessTestFrame (uint seqNr, uint chanNr) { @@ -162,6 +168,7 @@ namespace test { + /* ===== TestFrame class ===== */ TestFrame::~TestFrame() { @@ -170,16 +177,16 @@ namespace test { TestFrame::TestFrame(uint seq, uint family) - : discriminator_(generateDiscriminator(seq,family)) + : distinction_(generateDistinction (seq,family)) , stage_(CREATED) { - ASSERT (0 < discriminator_); + ASSERT (0 < distinction_); buildData(); } TestFrame::TestFrame (TestFrame const& o) - : discriminator_(o.discriminator_) + : distinction_(o.distinction_) , stage_(CREATED) { memcpy (data_, o.data_, BUFFSIZ); @@ -192,7 +199,7 @@ namespace test { throw new error::Logic ("target TestFrame is already dead"); if (this != &o) { - discriminator_ = o.discriminator_; + distinction_ = o.distinction_; stage_ = CREATED; memcpy (data_, o.data_, BUFFSIZ); } @@ -242,9 +249,9 @@ namespace test { bool TestFrame::verifyData() const { - char c(0); + PseudoRandom gen(distinction_); for (uint i=0; i<BUFFSIZ; ++i) - if (data_[i] != (c=generate(c))) + if (data_[i] != (gen() % CHAR_MAX)) return false; return true; } @@ -252,15 +259,9 @@ namespace test { void TestFrame::buildData() { - char c(0); + PseudoRandom gen(distinction_); for (uint i=0; i<BUFFSIZ; ++i) - data_[i] = (c=generate(c)); - } - - char - TestFrame::generate (char prev) const - { - return (discriminator_ + prev) % CHAR_MAX; + data_[i] = (gen() % CHAR_MAX); } diff --git a/tests/components/proc/engine/testframe.hpp b/tests/components/proc/engine/testframe.hpp index 347e4b324..470b5e9ea 100644 --- a/tests/components/proc/engine/testframe.hpp +++ b/tests/components/proc/engine/testframe.hpp @@ -25,23 +25,14 @@ #define PROC_ENGINE_TESTFRAME_H -//#include "lib/time/timevalue.hpp" - -//#include <string> #include <cstdlib> #include <stdint.h> -//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. @@ -52,12 +43,13 @@ namespace test { * * Each TestFrame is automatically filled with pseudo random data; * multiple frames are arranged in sequences and channels, causing the random data - * to be reproducible yet different in each frame. The lifecycle of each TestFrame - * is tracked and marked in an embedded state field. Moreover, the contents of - * the data block can be verified, because the sequence of bytes is reproducible, + * to be reproducible yet different within each frame. TestFrame's lifecycle is + * tracked and marked in an embedded state field. Moreover, the contents of the + * data block can be verified, because the sequence of bytes is reproducible, * based on the channel and sequence number of the test frame. * - * @todo WIP-WIP-WIP 9/11 + * @see TestFrame_test + * @see OutputSlotProtocol_test * */ class TestFrame @@ -68,7 +60,7 @@ namespace test { static const size_t BUFFSIZ = 1024; - uint64_t discriminator_; + uint64_t distinction_; StageOfLife stage_; char data_[BUFFSIZ]; @@ -79,7 +71,7 @@ namespace test { TestFrame (TestFrame const&); TestFrame& operator= (TestFrame const&); - /** Helper to verify a given memory location holds + /** Helper to verify that a given memory location holds * an active TestFrame instance (created, not yet destroyed) * @return true if the TestFrame datastructure is intact and * marked as still alive. @@ -101,9 +93,7 @@ namespace test { private: bool contentEquals (TestFrame const& o) const; - bool verifyData() const; - char generate (char) const; - + bool verifyData() const; void buildData (); }; @@ -126,10 +116,5 @@ namespace test { - - /* == some test data to check == */ - // extern const lib::time::Duration LENGTH_TestClip; - - }} // namespace engine::test #endif From 0cd7bd2b4c29bc8cbdc32a4635df04675cd35912 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 1 Nov 2011 03:36:35 +0100 Subject: [PATCH 247/496] add test coverage for the test frame table --- .../components/proc/engine/testframe-test.cpp | 40 +++++++++++++++++-- tests/components/proc/engine/testframe.cpp | 2 +- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/tests/components/proc/engine/testframe-test.cpp b/tests/components/proc/engine/testframe-test.cpp index 29e96b9c5..a7e086fa9 100644 --- a/tests/components/proc/engine/testframe-test.cpp +++ b/tests/components/proc/engine/testframe-test.cpp @@ -23,6 +23,7 @@ #include "lib/test/run.hpp" #include "proc/engine/testframe.hpp" +#include "lib/util.hpp" #include <cstdlib> #include <limits.h> @@ -30,12 +31,13 @@ using test::Test; using std::rand; +using util::isSameObject; using boost::scoped_ptr; namespace engine{ namespace test { - + namespace { // used internally const uint CHAN_COUNT = 30; // independent families of test frames to generate @@ -76,6 +78,7 @@ namespace test { verifyBasicProperties(); verifyFrameLifecycle(); verifyFrameSeries(); + useFrameTable(); } @@ -155,14 +158,45 @@ namespace test { CHECK (prevFrames[i]->isSane()); CHECK (prevFrames[i]->isAlive()); - CHECK (*thisFrames[i] != *prevFrames[i]); // differs from predecessor in the same channel + CHECK (*thisFrames[i] != *prevFrames[i]); // differs from predecessor within the same channel for (uint j=0; j<i; ++j) { ENSURE (j!=i); CHECK (*thisFrames[i] != *thisFrames[j]); // differs from frames in other channels at this point - CHECK (*thisFrames[i] != *prevFrames[j], "nr=%d, i=%d, j=%d", nr, i,j); ///////FIXME getting matches here // differs cross wise from predecessors in other channels + CHECK (*thisFrames[i] != *prevFrames[j]); // differs cross wise from predecessors in other channels } } } + + + /** @test the table of test frames + * computed on demand */ + void + useFrameTable() + { + TestFrame& frX = testData(3,50); + TestFrame& frY = testData(3,25); + TestFrame& frZ = testData(3,50); + + CHECK (frX.isSane()); + CHECK (frY.isSane()); + CHECK (frZ.isSane()); + + CHECK (frX != frY); + CHECK (frX == frZ); + CHECK (frY != frZ); + + CHECK (isSameObject (frX, frZ)); + + corruptMemory(&frZ,40,20); + CHECK (!frX.isSane()); + CHECK (!testData(3,50).isSane()); + CHECK ( testData(3,51).isSane()); + CHECK ( testData(3,49).isSane()); + + resetTestFrames(); + + CHECK ( testData(3,50).isSane()); + } }; diff --git a/tests/components/proc/engine/testframe.cpp b/tests/components/proc/engine/testframe.cpp index 3822f4b30..0a051b74e 100644 --- a/tests/components/proc/engine/testframe.cpp +++ b/tests/components/proc/engine/testframe.cpp @@ -118,7 +118,7 @@ namespace test { "Growing channel #%d of test frames to %d elements, " "which is > the default (%d)", chanNr, seqNr, FRA); for (uint i=channel.size(); i<=seqNr; ++i) - channel.push_back (TestFrame (seqNr,chanNr)); + channel.push_back (TestFrame (i,chanNr)); } ENSURE (seqNr < channel.size()); From d6e88c85e01c02453e4c207afccc355b5544e64a Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 2 Nov 2011 01:34:05 +0100 Subject: [PATCH 248/496] finish buffer metadata; cover state transitions BufferMetadata complete and working for now --- src/proc/engine/buffer-metadata.hpp | 123 ++++++++++++---- tests/46engine.tests | 2 +- .../proc/engine/buffer-metadata-test.cpp | 137 ++++++++++++++---- 3 files changed, 204 insertions(+), 58 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index f86d1cdaf..9a73ba20a 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -31,7 +31,7 @@ ** - 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. + ** These three distinctions are applied in sequence, thus forming a type 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 @@ -85,15 +85,22 @@ namespace engine { + + /** + * Buffer states + * usable within BufferProvider + * and stored within the metadata + */ enum BufferState - { NIL, - FREE, - LOCKED, - EMITTED, - BLOCKED + { NIL, ///< abstract entry, not yet allocated + FREE, ///< allocated buffer, no longer in use + LOCKED, ///< allocated buffer actively in use + EMITTED, ///< allocated buffer, returned from client + BLOCKED ///< allocated buffer blocked by protocol failure }; + /** * an opaque ID to be used by the BufferProvider implementation. * Typically this will be used, to set apart some pre-registered @@ -131,7 +138,7 @@ namespace engine { }; - namespace { // Helpers for construction within the buffer... + namespace { // (optional) helpers to build an object embedded within the buffer... template<class X> inline void @@ -157,6 +164,7 @@ namespace engine { }//(End)placement-new helpers + /** * A pair of functors to maintain a datastructure within the buffer. * TypeHandler describes how to outfit the buffer in a specific way. @@ -263,7 +271,7 @@ namespace engine { - /* === Implementation === */ + /* === Metadata Implementation === */ namespace metadata { @@ -280,6 +288,7 @@ namespace engine { } } + /** * Description of a Buffer-"type". * Key elements will be used to generate hash IDs, @@ -372,6 +381,12 @@ namespace engine { } + LocalKey const& + localKey() const + { + return specifics_; + } + HashVal parentKey() const { return parent_;} operator HashVal() const { return hashID_;} }; @@ -425,7 +440,7 @@ namespace engine { bool isTypeKey() const { - return !bool(buffer_); + return NIL == state_ && !buffer_; } @@ -445,13 +460,15 @@ namespace engine { return buffer_; } + /** Buffer state machine */ Entry& mark (BufferState newState) { __must_not_be_NIL(); - __must_not_be_FREE(); - if ( (state_ == LOCKED && newState == EMITTED) + if ( (state_ == FREE && newState == LOCKED) + ||(state_ == LOCKED && newState == EMITTED) + ||(state_ == LOCKED && newState == BLOCKED) ||(state_ == LOCKED && newState == FREE) ||(state_ == EMITTED && newState == BLOCKED) ||(state_ == EMITTED && newState == FREE) @@ -460,13 +477,24 @@ namespace engine { // allowed transition if (newState == FREE) invokeEmbeddedDtor_and_clear(); + if (newState == LOCKED) + invokeEmbeddedCtor(); state_ = newState; return *this; } - throw error::Fatal ("Invalid buffer state encountered."); + throw error::Fatal ("Invalid buffer state transition."); } + Entry& + lock (void* newBuffer) + { + __must_be_FREE(); + buffer_ = newBuffer; + return mark (LOCKED); + } + + protected: /** @internal maybe invoke a registered TypeHandler's * constructor function, which typically builds some @@ -474,7 +502,7 @@ namespace engine { void invokeEmbeddedCtor() { - REQUIRE (buffer_); + __buffer_required(); if (nontrivial (instanceFunc_)) instanceFunc_.createAttached (buffer_); } @@ -485,7 +513,7 @@ namespace engine { void invokeEmbeddedDtor_and_clear() { - REQUIRE (buffer_); + __buffer_required(); if (nontrivial (instanceFunc_)) instanceFunc_.destroyAttached (buffer_); buffer_ = 0; @@ -510,9 +538,27 @@ namespace engine { "You should invoke markLocked(buffer) prior to access." , LUMIERA_ERROR_LIFECYCLE ); } + + void + __must_be_FREE() const + { + if (FREE != state_) + throw error::Logic ("Buffer already in use" + , LUMIERA_ERROR_LIFECYCLE ); + REQUIRE (!buffer_, "Buffer marked as free, " + "but buffer pointer is set."); + } + + void + __buffer_required() const + { + if (!buffer_) + throw error::Fatal ("Need concrete buffer for any further operations"); + } }; + /** * (Hash)Table to store and manage buffer metadata. * Buffer metadata entries are comprised of a Key part and an extended @@ -617,6 +663,21 @@ namespace engine { + /* ===== Buffer Metadata Frontend ===== */ + + /** + * Registry for managing buffer metadata. + * This is an implementation level service, + * used by the standard BufferProvider implementation. + * Each metadata registry (instance) defines and maintains + * a family of "buffer types"; beyond the buffer storage size, + * the concrete meaning of those types is tied to the corresponding + * BufferProvider implementation and remains opaque. These types are + * represented as hierarchically linked hash keys. The implementation + * may bind a TypeHandler to a specific type, allowing automatic invocation + * of a "constructor" and "destructor" function on each buffer of this type, + * when \em locking or \em freeing the corresponding buffer. + */ class BufferMetadata : boost::noncopyable { @@ -625,8 +686,8 @@ namespace engine { metadata::Table table_; - public: + public: typedef metadata::Key Key; typedef metadata::Entry Entry; @@ -634,8 +695,8 @@ namespace engine { * Such will maintain a family of buffer type entries * and provide a service for storing and retrieving metadata * for concrete buffer entries associated with these types. - * @param implementationID to distinguish families - * of type keys belonging to different registries. + * @param implementationID to distinguish families of + * type keys belonging to different registries. */ BufferMetadata (Literal implementationID) : id_(implementationID) @@ -661,14 +722,10 @@ namespace engine { Key typeKey = trackKey (family_, storageSize); if (nontrivial(instanceFunc)) - { typeKey = trackKey (typeKey, instanceFunc); - } if (nontrivial(specifics)) - { typeKey = trackKey (typeKey, specifics); - } return typeKey; } @@ -680,7 +737,8 @@ namespace engine { return trackKey (parentKey, instanceFunc); } - /** create a sub-type, using a different private-ID (implementation defined) */ + /** create a sub-type, + * using a different private-ID (implementation defined) */ Key key (Key const& parentKey, LocalKey specifics) { @@ -694,7 +752,11 @@ namespace engine { Key const& key (Key const& parentKey, void* concreteBuffer) { - return lock (parentKey,concreteBuffer); + Key derivedKey = Key::forEntry (parentKey, concreteBuffer); + Entry* existing = table_.fetch (derivedKey); + + return existing? *existing + : markLocked (parentKey,concreteBuffer); } /** core operation to access or create a concrete buffer metadata entry. @@ -728,15 +790,19 @@ namespace engine { Entry newEntry(parentKey, concreteBuffer); Entry* existing = table_.fetch (newEntry); + if (existing && onlyNew) throw error::Logic ("Attempt to lock a slot for a new buffer, " - "while actually the old buffer is still locked." + "while actually the old buffer is still locked" + , error::LUMIERA_ERROR_LIFECYCLE ); + if (existing && existing->isLocked()) + throw error::Logic ("Attempt to re-lock a buffer still in use" , error::LUMIERA_ERROR_LIFECYCLE ); if (!existing) return store_and_lock (newEntry); // actual creation else - return *existing; + return existing->lock (concreteBuffer); } /** access the metadata record registered with the given hash key. @@ -812,7 +878,7 @@ namespace engine { - private: + private: template<typename PAR, typename DEF> Key @@ -836,7 +902,9 @@ namespace engine { Entry& newEntry = table_.store (metadata); try { - newEntry.invokeEmbeddedCtor(); + newEntry.invokeEmbeddedCtor(); + ENSURE (LOCKED == newEntry.state()); + ENSURE (newEntry.access()); } catch(...) { @@ -845,7 +913,6 @@ namespace engine { } return newEntry; } - }; diff --git a/tests/46engine.tests b/tests/46engine.tests index 293dc00c7..46c1806db 100644 --- a/tests/46engine.tests +++ b/tests/46engine.tests @@ -17,7 +17,7 @@ return: 0 END -PLANNED "buffer metadata and state transitions" BufferMetadata_test <<END +TEST "buffer metadata and state transitions" BufferMetadata_test <<END END diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp index 85818ba6f..2ed136a9b 100644 --- a/tests/components/proc/engine/buffer-metadata-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -24,40 +24,25 @@ #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/testframe.hpp" -//#include "proc/engine/buffhandle.hpp" -//#include "proc/engine/bufftable.hpp" -//#include <boost/format.hpp> #include <boost/scoped_ptr.hpp> #include <cstdlib> #include <cstring> -//#include <iostream> -//using boost::format; -//using std::string; -//using std::cout; -//using util::for_each; using std::strncpy; using boost::scoped_ptr; using lib::test::randStr; -using util::isnil; using util::isSameObject; +using util::isnil; namespace engine{ namespace test { - -// using lib::AllocationCluster; -// using mobject::session::PEffect; -// using ::engine::BuffHandle; + using lumiera::error::LUMIERA_ERROR_FATAL; using lumiera::error::LUMIERA_ERROR_INVALID; using lumiera::error::LUMIERA_ERROR_LIFECYCLE; @@ -71,8 +56,6 @@ namespace test { HashVal JUST_SOMETHING = 123; void* const SOME_POINTER = &JUST_SOMETHING; -// const uint TEST_SIZE = 1024*1024; -// const uint TEST_ELMS = 20; template<typename TY> @@ -89,6 +72,7 @@ namespace test { + /******************************************************************* * @test verify the properties of the BufferMetadata records used * internally within BufferProvider to attach additional @@ -105,6 +89,7 @@ namespace test { CHECK (ensure_proper_fixture()); verifyBasicProperties(); verifyStandardCase(); + verifyStateMachine(); } @@ -151,8 +136,8 @@ namespace test { CHECK (NIL == m1.state()); CHECK (!meta_->isLocked(key)); - VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED) ); - VERIFY_ERROR (LIFECYCLE, m1.mark(LOCKED) ); + VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED)); + VERIFY_ERROR (LIFECYCLE, m1.mark(FREE) ); // now create an active (buffer) entry metadata::Entry& m2 = meta_->markLocked (key, SOME_POINTER); @@ -186,7 +171,7 @@ namespace test { CHECK ( meta_->isKnown(keyX)); CHECK ( meta_->isKnown(key1)); VERIFY_ERROR (LIFECYCLE, m2.access()); - VERIFY_ERROR (LIFECYCLE, m2.mark(LOCKED)); + VERIFY_ERROR (FATAL, m2.mark(LOCKED)); // buffer missing CHECK ( isSameObject (m2, meta_->get(keyX))); // still accessible // release buffer... @@ -202,8 +187,8 @@ namespace test { * @note to get the big picture, please refer to * BufferProviderProtocol_test#verifyStandardCase() * This testcase here performs precisely the metadata related - * operations necessary to carry out the standard case outlined - * in that more high level test. + * operations necessary to carry out the standard case + * outlined on a higher level in the mentioned test. */ void verifyStandardCase() @@ -238,13 +223,19 @@ namespace test { metadata::Entry& r0 = meta_->markLocked(rawBuffType, &rawbuf[0]); metadata::Entry& r1 = meta_->markLocked(rawBuffType, &rawbuf[1]); - + CHECK (LOCKED == f0.state()); CHECK (LOCKED == f1.state()); CHECK (LOCKED == f2.state()); CHECK (LOCKED == r0.state()); CHECK (LOCKED == r1.state()); + CHECK (transaction1 == f0.localKey()); + CHECK (transaction1 == f1.localKey()); + CHECK (transaction1 == f2.localKey()); + CHECK (transaction2 == r0.localKey()); + CHECK (transaction2 == r1.localKey()); + CHECK (f0.access() == frames+0); CHECK (f1.access() == frames+1); @@ -269,6 +260,10 @@ namespace test { accessAs<TestFrame> (f1) = testData(2); accessAs<TestFrame> (f2) = testData(3); + CHECK (testData(1) == frames[0]); + CHECK (testData(2) == frames[1]); + CHECK (testData(3) == frames[2]); + CHECK (TestFrame::isAlive (f0.access())); CHECK (TestFrame::isAlive (f1.access())); CHECK (TestFrame::isAlive (f2.access())); @@ -283,7 +278,7 @@ namespace test { // client uses the buffers---------------------(End) - f0.mark(FREE); // note: this implicitly invoked the embedded dtor + f0.mark(FREE); // note: implicitly invoking the embedded dtor f1.mark(FREE); f2.mark(FREE); r0.mark(FREE); @@ -296,7 +291,7 @@ namespace test { meta_->release(handle_r0); meta_->release(handle_r1); - CHECK (TestFrame::isDead (&frames[0])); + CHECK (TestFrame::isDead (&frames[0])); // was destroyed implicitly CHECK (TestFrame::isDead (&frames[1])); CHECK (TestFrame::isDead (&frames[2])); @@ -309,9 +304,93 @@ namespace test { CHECK (!meta_->isLocked(handle_f2)); CHECK (!meta_->isLocked(handle_r0)); CHECK (!meta_->isLocked(handle_r1)); + } + + + void + verifyStateMachine() + { + // start with building a type key.... + metadata::Key key = meta_->key(SIZE_A); + CHECK (NIL == meta_->get(key).state()); + CHECK (meta_->get(key).isTypeKey()); + CHECK (!meta_->isLocked(key)); -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834 -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834 + VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(LOCKED) ); + VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(EMITTED)); + VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(BLOCKED)); + VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(FREE) ); + VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(NIL) ); + + // now build a concrete buffer entry + metadata::Entry& entry = meta_->markLocked(key, SOME_POINTER); + CHECK (LOCKED == entry.state()); + CHECK (!entry.isTypeKey()); + + CHECK (SOME_POINTER == entry.access()); + + VERIFY_ERROR (FATAL, entry.mark(LOCKED) ); // invalid state transition + VERIFY_ERROR (FATAL, entry.mark(NIL) ); + + entry.mark (EMITTED); // valid transition + CHECK (EMITTED == entry.state()); + CHECK (entry.isLocked()); + + VERIFY_ERROR (FATAL, entry.mark(LOCKED) ); + VERIFY_ERROR (FATAL, entry.mark(EMITTED)); + VERIFY_ERROR (FATAL, entry.mark(NIL) ); + CHECK (EMITTED == entry.state()); + + entry.mark (FREE); + CHECK (FREE == entry.state()); + CHECK (!entry.isLocked()); + CHECK (!entry.isTypeKey()); + + VERIFY_ERROR (LIFECYCLE, entry.access() ); + VERIFY_ERROR (FATAL, entry.mark(LOCKED) ); + VERIFY_ERROR (FATAL, entry.mark(EMITTED)); + VERIFY_ERROR (FATAL, entry.mark(BLOCKED)); + VERIFY_ERROR (FATAL, entry.mark(FREE) ); + VERIFY_ERROR (FATAL, entry.mark(NIL) ); + + // re-use buffer slot, start new lifecycle + void* OTHER_LOCATION = this; + entry.lock (OTHER_LOCATION); + CHECK (LOCKED == entry.state()); + CHECK (entry.isLocked()); + + VERIFY_ERROR (LIFECYCLE, entry.lock(SOME_POINTER)); + + entry.mark (BLOCKED); // go directly to the blocked state + CHECK (BLOCKED == entry.state()); + VERIFY_ERROR (FATAL, entry.mark(LOCKED) ); + VERIFY_ERROR (FATAL, entry.mark(EMITTED) ); + VERIFY_ERROR (FATAL, entry.mark(BLOCKED) ); + VERIFY_ERROR (FATAL, entry.mark(NIL) ); + + CHECK (OTHER_LOCATION == entry.access()); + + entry.mark (FREE); + CHECK (!entry.isLocked()); + VERIFY_ERROR (LIFECYCLE, entry.access() ); + + meta_->lock(key, SOME_POINTER); + CHECK (entry.isLocked()); + + entry.mark (EMITTED); + entry.mark (BLOCKED); + CHECK (BLOCKED == entry.state()); + CHECK (SOME_POINTER == entry.access()); + + // can't discard metadata, need to free first + VERIFY_ERROR (LIFECYCLE, meta_->release(entry) ); + CHECK (meta_->isKnown(entry)); + CHECK (entry.isLocked()); + + entry.mark (FREE); + meta_->release(entry); + CHECK (!meta_->isKnown(entry)); + CHECK ( meta_->isKnown(key)); } }; From 63dbc89b6f4ae263b6c98ae86905c7dbc7def77d Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 5 Nov 2011 16:51:24 +0100 Subject: [PATCH 249/496] draft implementation setup for OutputSlot baseclass --- src/proc/engine/engine-service.cpp | 2 +- src/proc/engine/engine-service.hpp | 2 +- src/proc/play/output-slot.cpp | 71 ++++++++++++++++++- src/proc/play/output-slot.hpp | 24 ++++--- .../proc/engine/engine-interface-test.cpp | 2 +- .../proc/play/output-slot-protocol-test.cpp | 4 +- wiki/renderengine.html | 33 ++++----- 7 files changed, 107 insertions(+), 31 deletions(-) diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp index e9d188a78..a4e989a19 100644 --- a/src/proc/engine/engine-service.cpp +++ b/src/proc/engine/engine-service.cpp @@ -61,7 +61,7 @@ namespace engine{ CalcStream EngineService::calculate(ModelPort mPort, Timings nominalTimings, - OutputConnection output, + OutputConnection& output, Quality serviceQuality) { UNIMPLEMENTED ("build a standard calculation stream"); diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp index 6b0d84e5f..7e5c1339b 100644 --- a/src/proc/engine/engine-service.hpp +++ b/src/proc/engine/engine-service.hpp @@ -141,7 +141,7 @@ namespace engine{ CalcStream calculate(ModelPort mPort, Timings nominalTimings, - OutputConnection output, + OutputConnection& output, Quality serviceQuality =QoS_DEFAULT); CalcStream diff --git a/src/proc/play/output-slot.cpp b/src/proc/play/output-slot.cpp index ee04ed818..5f4ae9f7b 100644 --- a/src/proc/play/output-slot.cpp +++ b/src/proc/play/output-slot.cpp @@ -21,13 +21,20 @@ * *****************************************************/ +#include "lib/error.hpp" #include "proc/play/output-slot.hpp" +#include <boost/noncopyable.hpp> +#include <vector> namespace proc { namespace play { + using std::vector; + + namespace error = lumiera::error; + namespace { // hidden local details of the service implementation.... @@ -35,9 +42,56 @@ namespace play { } // (End) hidden service impl details + class Connection + { + public: + }; + + + class ConnectionState + : public OutputSlot::Allocation + , public vector<Connection> + , boost::noncopyable + { + + typedef OutputSlot::OpenedSinks OpenedSinks; + + + private: /* == Allocation Interface == */ + OpenedSinks + getOpenedSinks() + { + UNIMPLEMENTED ("yield all opened channels"); + } + + bool + isActive() + { + return 0 < size(); + } + + + public: + ConnectionState() + { + UNIMPLEMENTED ("immediately build up the necessary number of connections"); + } + + virtual + ~ConnectionState() + { + UNIMPLEMENTED ("shut down all connections"); + } + }; + + OutputSlot::~OutputSlot() { } // emit VTables here.... + OutputSlot::Allocation::~Allocation() { } + + + /** whether this output slot is occupied @@ -47,9 +101,22 @@ namespace play { bool OutputSlot::isFree() const { - UNIMPLEMENTED ("connection state"); + return ! this->state_; + } + + + /** */ + OutputSlot::Allocation& + OutputSlot::allocate() + { + if (!isFree()) + throw error::Logic ("Attempt to open/allocate an OutputSlot already in use."); + + UNIMPLEMENTED ("internal interface to determine the number of channel-connections"); + + this->state_.reset(new ConnectionState()); + return *state_; } - diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index ece7c4627..70e0c7bf4 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -45,10 +45,10 @@ //#include "lib/sync.hpp" #include <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> //#include <string> //#include <vector> //#include <tr1/memory> -//#include <boost/scoped_ptr.hpp> namespace proc { @@ -61,12 +61,16 @@ namespace play { //using std::vector; //using std::tr1::shared_ptr; -//using boost::scoped_ptr; + using boost::scoped_ptr; /** established output channel */ class Connection; + /** Table to maintain connection state */ + class ConnectionState; + + typedef int64_t FrameNr; @@ -90,25 +94,29 @@ namespace play { class OutputSlot : boost::noncopyable { + scoped_ptr<ConnectionState> state_; + + public: virtual ~OutputSlot(); typedef lib::IterSource<DataSink>::iterator OpenedSinks; - struct Allocation + class Allocation { - OpenedSinks getOpenedSinks(); - - bool isActive(); + public: + virtual OpenedSinks getOpenedSinks() =0; + virtual bool isActive() =0; /////TODO add here the getters for timing constraints + protected: + ~Allocation(); }; bool isFree() const; - Allocation - allocate(); + Allocation& allocate(); protected: friend class DataSink; diff --git a/tests/components/proc/engine/engine-interface-test.cpp b/tests/components/proc/engine/engine-interface-test.cpp index fd55ee76f..3b0e96bd8 100644 --- a/tests/components/proc/engine/engine-interface-test.cpp +++ b/tests/components/proc/engine/engine-interface-test.cpp @@ -93,7 +93,7 @@ namespace test { ModelPort port(pipe); OutputSlot& oSlot = DiagnosticOutputSlot::build(); - Allocation output = oSlot.allocate(); + Allocation& output = oSlot.allocate(); Timings timings; /////////TODO // Invoke test subject... diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp index ae3ec2ae5..29b1db615 100644 --- a/tests/components/proc/play/output-slot-protocol-test.cpp +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -71,7 +71,6 @@ 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) @@ -80,7 +79,7 @@ namespace test { // Client claims the OutputSlot // and opens it for exclusive use. - OutputSlot::Allocation alloc = oSlot.allocate(); + OutputSlot::Allocation& alloc = oSlot.allocate(); // Now the client is able to prepare // "calculation streams" for the individual @@ -139,6 +138,7 @@ namespace test { CHECK (*stream1++ == testData(1,0)); CHECK (*stream1++ == testData(1,1)); CHECK (!stream1); +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #819 #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #819 } }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 42a7cb353..6abd6656c 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1133,8 +1133,8 @@ Beyond that, it can be necessary to associate at least a state flag with //indiv __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. </pre> </div> -<div title="BufferProvider" modifier="Ichthyostega" modified="201109232347" created="201107082330" tags="Rendering spec draft" changecount="20"> -<pre>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... +<div title="BufferProvider" modifier="Ichthyostega" modified="201111050003" created="201107082330" tags="Rendering spec draft" changecount="21"> +<pre>It turns out that -- throughout the render engine implementation -- we never need direct access to the buffers holding actual 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. @@ -3337,7 +3337,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) </pre> </div> -<div title="OutputManagement" modifier="Ichthyostega" modified="201108251240" created="201007090155" tags="Model Rendering Player spec draft" changecount="37"> +<div title="OutputManagement" modifier="Ichthyostega" modified="201111040000" created="201007090155" tags="Model Rendering Player spec draft" changecount="38"> <pre>//writing down some thoughts// * ruled out the system outputs as OutputDesignation. @@ -3366,12 +3366,12 @@ Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputMa &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 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. +Within the model routing is done mostly just by referring to an OutputDesignation -- but at some point finally we need to map these abstract designations to real output capabilities. This happens at the //output managing elements.// This interface, OutputManager, exposes these mappings of logical to real outputs and allows to manage and control 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 and 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. +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 be 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?}}} +{{red{open question 11/11: is it possible to retrieve a slot from another peripheral node?}}} </pre> </div> <div title="OutputManager" modifier="Ichthyostega" modified="201106212317" created="201106122359" tags="Player Model def" changecount="7"> @@ -3423,33 +3423,34 @@ 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. </pre> </div> -<div title="OutputSlot" modifier="Ichthyostega" modified="201109021531" created="201106162339" tags="def Concepts Player spec" changecount="27"> +<div title="OutputSlot" modifier="Ichthyostega" modified="201111042355" created="201106162339" tags="def Concepts Player spec" changecount="40"> <pre>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) +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 (video in GUI window, video fullscreen, sound, Jack, rendering to file) 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, 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) +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. (in special cases, some 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 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. +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 are 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. 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 -: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. +: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 signalles 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, &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 //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. +The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is assumed to be 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. +{{red{WIP 11/11}}}meanwhile I've worked out the BufferProvider interface in deail. There's now a deatiled buffer handover protocol defined, which is supported by a little state engine tracking BufferMetadata. This mechanism provides a transition to {{{BLOCKED}}} state when order or timing constraints are being violated, which practically solves this problem. How to detect and resolve such a floundered state from the engine point of view still remains to be addressed. !!!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). +The concrete OutputSlot implementation is owned and managed by the facility actually providing the output possibility in question. For example, the GUI provides viewer widgets, while some sound output backend provides sound ports. The associated OutputSlot implementation object is required to stay alive as long as it's registered with some OutputManager. It needs to be de-registered explicitly prior to destruction -- and this deregistration may block until all clients using this slot did terminate. Beyond that, an output slot implementation is expected to handle all kinds of failures gracefully -- preferably just emitting a signal (callback functor). {{red{TODO 7/11: Deregistration is an unsolved problem....}}} ----- @@ -3466,9 +3467,9 @@ Solving this problem through //generic programming// -- i.e coding both cases ef ;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 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! +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 -- +On the other hand, 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 works like extending the shared buffer model to both cases. +&rArr; __conclusion__: go for the unified approach! !!!unified data exchange cycle The planned delivery time of a frame is used as an ID throughout that cycle From 7ab7c8073dc3bcc7b6aafdc6706253e0b7fa7ca5 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 6 Nov 2011 02:37:22 +0100 Subject: [PATCH 250/496] remold OutputSlot implementation structure Better solution where to place the extension points to be implemented by concrete output systems --- src/proc/play/output-slot-connection.hpp | 162 ++++++++++++++++++ src/proc/play/output-slot.cpp | 52 ++---- src/proc/play/output-slot.hpp | 58 ++++--- .../proc/play/diagnostic-output-slot.hpp | 8 +- 4 files changed, 210 insertions(+), 70 deletions(-) create mode 100644 src/proc/play/output-slot-connection.hpp diff --git a/src/proc/play/output-slot-connection.hpp b/src/proc/play/output-slot-connection.hpp new file mode 100644 index 000000000..c19080ed4 --- /dev/null +++ b/src/proc/play/output-slot-connection.hpp @@ -0,0 +1,162 @@ +/* + OUTPUT-SLOT-CONNECTION.hpp - implementation API for concrete output slots + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file output-slot-connection.hpp + ** Interface for concrete output implementations to talk to the OutputSlot frontend. + ** The OutputSlot concept helps to decouple the render engine implementation from the details + ** of handling external output connections. For this to work, a concrete implementation of such + ** an external output needs to integrate with the generic OutputSlot frontend, as used by the + ** engine. This generic frontend uses a PImpl, pointing to a ConnectionState object, which embodies + ** the actual implementation. Moreover, this actual implementation is free to use specifically crafted + ** OutputSlot::Connection elements to handle the ongoing output for individual channels. The latter + ** thus becomes the central implementation side API for providing actual output capabilities. + ** + ** @see OutputSlotProtocol_test + ** @see diagnostic-output-slot.hpp ////TODO + */ + + +#ifndef PROC_PLAY_OUTPUT_SLOT_CONNECTION_H +#define PROC_PLAY_OUTPUT_SLOT_CONNECTION_H + + +#include "lib/error.hpp" +#include "proc/play/output-slot.hpp" +#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/iter-adapter-stl.hpp" +//#include "lib/sync.hpp" + +#include <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> +//#include <string> +//#include <vector> +//#include <tr1/memory> + + +namespace proc { +namespace play { + +//using ::engine::BuffHandle; +//using ::engine::BufferProvider; +//using lib::time::Time; +//using std::string; + using lib::transform; + using lib::iter_stl::eachElm; + +//using std::vector; +//using std::tr1::shared_ptr; + using boost::scoped_ptr; + + + + + /** established output channel */ + class OutputSlot::Connection + { + public: + virtual ~Connection(); + + virtual void lock (FrameID) =0; + virtual void transfer (FrameID) =0; + virtual void pushout (FrameID) =0; + }; + + + + + class OutputSlot::ConnectionState + : public OutputSlot::Allocation + , boost::noncopyable + { + public: + virtual ~ConnectionState() { } + }; + + + template<class CON> + class ConnectionStateManager + : public OutputSlot::ConnectionState + , public vector<CON> + { + + typedef OutputSlot::OpenedSinks OpenedSinks; + + + /* == Allocation Interface == */ + + OpenedSinks + getOpenedSinks() + { + REQUIRE (this->isActive()); + return transform (eachElm(*this), connectOutputSink); + } + + bool + isActive() + { + return 0 < vector<CON>::size(); + } + + + public: + ConnectionStateManager() + { + UNIMPLEMENTED ("immediately build up the necessary number of connections"); + } + + virtual + ~ConnectionStateManager() + { + UNIMPLEMENTED ("shut down all connections"); + } + + virtual + CON + buildConnection() =0; + + + private: // Implementation details + + DataSink + connectOutputSink (CON& connection) + { + DataSink newSink; + newSink.activate(&connection, shutdownConnection); + return newSink; + } + + void + shutdownConnection (CON* toClose) + { + UNIMPLEMENTED ("how to mark a connection as closed"); + } + }; + + + + +}} // namespace proc::play +#endif diff --git a/src/proc/play/output-slot.cpp b/src/proc/play/output-slot.cpp index 5f4ae9f7b..6cd3c5aa4 100644 --- a/src/proc/play/output-slot.cpp +++ b/src/proc/play/output-slot.cpp @@ -23,6 +23,7 @@ #include "lib/error.hpp" #include "proc/play/output-slot.hpp" +#include "proc/play/output-slot-connection.hpp" #include <boost/noncopyable.hpp> #include <vector> @@ -42,48 +43,8 @@ namespace play { } // (End) hidden service impl details - class Connection - { - public: - }; - class ConnectionState - : public OutputSlot::Allocation - , public vector<Connection> - , boost::noncopyable - { - - typedef OutputSlot::OpenedSinks OpenedSinks; - - - private: /* == Allocation Interface == */ - OpenedSinks - getOpenedSinks() - { - UNIMPLEMENTED ("yield all opened channels"); - } - - bool - isActive() - { - return 0 < size(); - } - - - public: - ConnectionState() - { - UNIMPLEMENTED ("immediately build up the necessary number of connections"); - } - - virtual - ~ConnectionState() - { - UNIMPLEMENTED ("shut down all connections"); - } - }; - OutputSlot::~OutputSlot() { } // emit VTables here.... @@ -114,10 +75,19 @@ namespace play { UNIMPLEMENTED ("internal interface to determine the number of channel-connections"); - this->state_.reset(new ConnectionState()); + state_.reset (this->buildState()); return *state_; } + void + OutputSlot::disconnect() + { + if (!isFree()) + state_.reset(0); + } + + + }} // namespace proc::play diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index 70e0c7bf4..04754e4bd 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -38,7 +38,7 @@ #include "lib/error.hpp" #include "lib/handle.hpp" -#include "lib/time/timevalue.hpp" +//#include "lib/time/timevalue.hpp" #include "proc/engine/buffer-provider.hpp" #include "proc/play/timings.hpp" #include "lib/iter-source.hpp" @@ -56,7 +56,7 @@ namespace play { using ::engine::BuffHandle; using ::engine::BufferProvider; - using lib::time::Time; +//using lib::time::Time; //using std::string; //using std::vector; @@ -64,25 +64,10 @@ namespace play { using boost::scoped_ptr; - /** established output channel */ - class Connection; - /** Table to maintain connection state */ - class ConnectionState; - - - typedef int64_t FrameNr; - - - class DataSink - : public lib::Handle<Connection> - { - - public: - BuffHandle lockBufferFor(FrameNr); - void emit(FrameNr); - }; + class DataSink; + typedef int64_t FrameID; @@ -94,8 +79,16 @@ namespace play { class OutputSlot : boost::noncopyable { + + protected: + + /** Table to maintain connection state */ + class ConnectionState; + scoped_ptr<ConnectionState> state_; + virtual ConnectionState* buildState() =0; + public: virtual ~OutputSlot(); @@ -113,17 +106,19 @@ namespace play { ~Allocation(); }; + /** established output channel */ + class Connection; + + /** can this OutputSlot be allocated? */ bool isFree() const; + /** claim this slot for exclusive use */ 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; + /** disconnect from this OutputSlot + * @warning may block until DataSinks are gone */ + void disconnect(); private: @@ -131,5 +126,18 @@ namespace play { + class DataSink + : public lib::Handle<OutputSlot::Connection> + { + + public: + BuffHandle lockBufferFor(FrameID); + void emit(FrameID); + }; + + + + + }} // namespace proc::play #endif diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index 18a13c6b3..ffd1ce829 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -105,28 +105,28 @@ namespace play { bool - buffer_was_used (uint channel, FrameNr frame) + buffer_was_used (uint channel, FrameID frame) { UNIMPLEMENTED ("determine if the denoted buffer was indeed used"); } bool - buffer_unused (uint channel, FrameNr frame) + buffer_unused (uint channel, FrameID frame) { UNIMPLEMENTED ("determine if the specified buffer was never touched/locked for use"); } bool - buffer_was_closed (uint channel, FrameNr frame) + buffer_was_closed (uint channel, FrameID frame) { UNIMPLEMENTED ("determine if the specified buffer was indeed closed properly"); } bool - emitted (uint channel, FrameNr frame) + emitted (uint channel, FrameID frame) { UNIMPLEMENTED ("determine if the specivied buffer was indeed handed over for emitting output"); } From a88ccd219d3d2b3e9c158771a6e7195435563286 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 7 Nov 2011 00:07:53 +0100 Subject: [PATCH 251/496] extract the diagnostic BufferProvider into separate header --- .../engine/diagnostic-buffer-provider.cpp | 108 +------------- .../engine/diagnostic-buffer-provider.hpp | 20 +-- .../engine/tracking-heap-block-provider.cpp | 113 +++++++++++++++ .../engine/tracking-heap-block-provider.hpp | 137 ++++++++++++++++++ src/proc/play/output-slot-connection.hpp | 49 ++++++- 5 files changed, 311 insertions(+), 116 deletions(-) create mode 100644 src/proc/engine/tracking-heap-block-provider.cpp create mode 100644 src/proc/engine/tracking-heap-block-provider.hpp diff --git a/src/proc/engine/diagnostic-buffer-provider.cpp b/src/proc/engine/diagnostic-buffer-provider.cpp index 8a5efdfbe..d8933733c 100644 --- a/src/proc/engine/diagnostic-buffer-provider.cpp +++ b/src/proc/engine/diagnostic-buffer-provider.cpp @@ -22,17 +22,16 @@ #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 "proc/engine/tracking-heap-block-provider.hpp" -#include <boost/scoped_array.hpp> +//#include <boost/scoped_array.hpp> //#include <vector> -using lib::ScopedPtrVect; -using boost::scoped_array; +//using lib::ScopedPtrVect; +//using boost::scoped_array; @@ -43,110 +42,15 @@ namespace engine { lib::Singleton<DiagnosticBufferProvider> DiagnosticBufferProvider::diagnostics; - class Block - : boost::noncopyable - { - size_t size_; - scoped_array<char> storage_; - - bool was_locked_; - - public: - Block() - : size_(0) - , storage_() - , was_locked_(false) - { } - - bool - was_used() const - { - return was_locked_; - } - - bool - was_closed() const - { - return was_locked_; - } - - void* - accessMemory() const - { - return storage_.get(); - } - }; - namespace { // Details of allocation and accounting - const uint MAX_BUFFERS = 50; } // (END) Details of allocation and accounting - /** - * @internal DiagnosticBufferProvider's PImpl. - * Uses a linearly growing table of heap allocated buffer blocks, - * which will never be discarded, unless the PImpl is discarded as a whole. - * This way, the tracked usage information remains available after the fact. - */ - class DiagnosticBufferProvider::HeapMemProvider - : public BufferProvider - , public ScopedPtrVect<Block> - { - - virtual uint - announce (uint count, BufferDescriptor const& type) - { - UNIMPLEMENTED ("pre-register storage for buffers of a specific kind"); - } - - - virtual BuffHandle - lockBufferFor (BufferDescriptor const& descriptor) - { - UNIMPLEMENTED ("lock buffer for exclusive use"); - } - - virtual void - releaseBuffer (BuffHandle const& handle) - { - UNIMPLEMENTED ("release a buffer and invalidate the handle"); - } - - public: - HeapMemProvider() - : BufferProvider ("Diagnostic_HeapAllocated") - { } - - virtual ~HeapMemProvider() - { - INFO (proc_mem, "discarding %zu diagnostic buffer entries", HeapMemProvider::size()); - } - - Block& - access_or_create (uint bufferID) - { - while (!withinStorageSize (bufferID)) - manage (new Block); - - ENSURE (withinStorageSize (bufferID)); - return (*this)[bufferID]; - } - - private: - bool - withinStorageSize (uint bufferID) const - { - if (bufferID >= MAX_BUFFERS) - throw error::Fatal ("hardwired internal limit for test buffers exceeded"); - - return bufferID < size(); - } - }; @@ -177,10 +81,10 @@ namespace engine { - DiagnosticBufferProvider::HeapMemProvider& + TrackingHeapBlockProvider& DiagnosticBufferProvider::reset() { - pImpl_.reset(new HeapMemProvider()); + pImpl_.reset(new TrackingHeapBlockProvider()); return *pImpl_; } diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp index 0f80fea6e..83bb589ca 100644 --- a/src/proc/engine/diagnostic-buffer-provider.hpp +++ b/src/proc/engine/diagnostic-buffer-provider.hpp @@ -21,7 +21,7 @@ */ /** @file diagnostic-buffer-provider.hpp - ** An facility for writing unit-tests targetting the BufferProvider interface. + ** An facility for writing unit-tests targeting the BufferProvider interface. ** ** @see buffer-provider-protocol-test.cpp */ @@ -44,6 +44,13 @@ namespace engine { namespace error = lumiera::error; + /** + * simple BufferProvider implementation + * with additional allocation tracking + */ + class TrackingHeapBlockProvider; + + /******************************************************************** * Helper for unit tests: Buffer provider reference implementation. * @@ -53,18 +60,11 @@ namespace engine { : boost::noncopyable { - /** - * simple BufferProvider implementation - * with additional allocation tracking - */ - class HeapMemProvider; - - - boost::scoped_ptr<HeapMemProvider> pImpl_; + boost::scoped_ptr<TrackingHeapBlockProvider> pImpl_; static lib::Singleton<DiagnosticBufferProvider> diagnostics; - HeapMemProvider& reset(); + TrackingHeapBlockProvider& reset(); bool isCurrent (BufferProvider const&); diff --git a/src/proc/engine/tracking-heap-block-provider.cpp b/src/proc/engine/tracking-heap-block-provider.cpp new file mode 100644 index 000000000..947acc614 --- /dev/null +++ b/src/proc/engine/tracking-heap-block-provider.cpp @@ -0,0 +1,113 @@ +/* + TrackingHeapBlockProvider - plain heap allocating BufferProvider implementation for tests + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/error.hpp" +#include "include/logging.h" +//#include "lib/meta/function.hpp" +#include "lib/scoped-ptrvect.hpp" + +#include "proc/engine/tracking-heap-block-provider.hpp" + +#include <boost/noncopyable.hpp> +//#include <vector> + +using lib::ScopedPtrVect; + + + +namespace engine { + + + + + + namespace { // Details of allocation and accounting + + const uint MAX_BUFFERS = 50; + + } // (END) Details of allocation and accounting + + + + /** + * @internal create a memory tracking BufferProvider, + */ + TrackingHeapBlockProvider::TrackingHeapBlockProvider() + : BufferProvider ("Diagnostic_HeapAllocated") + { } + + TrackingHeapBlockProvider::~TrackingHeapBlockProvider() + { + INFO (proc_mem, "discarding %zu diagnostic buffer entries", TrackingHeapBlockProvider::size()); + } + + + /* ==== Implementation of the BufferProvider interface ==== */ + + uint + TrackingHeapBlockProvider::announce (uint count, BufferDescriptor const& type) + { + UNIMPLEMENTED ("pre-register storage for buffers of a specific kind"); + } + + + BuffHandle + TrackingHeapBlockProvider::lockBufferFor (BufferDescriptor const& descriptor) + { + UNIMPLEMENTED ("lock buffer for exclusive use"); + } + + + void + TrackingHeapBlockProvider::releaseBuffer (BuffHandle const& handle) + { + UNIMPLEMENTED ("release a buffer and invalidate the handle"); + } + + + + /* ==== Implementation details ==== */ + + diagn::Block& + TrackingHeapBlockProvider::access_or_create (uint bufferID) + { + while (!withinStorageSize (bufferID)) + this->manage (new diagn::Block); + + ENSURE (withinStorageSize (bufferID)); + return (*this)[bufferID]; + } + + bool + TrackingHeapBlockProvider::withinStorageSize (uint bufferID) const + { + if (bufferID >= MAX_BUFFERS) + throw error::Fatal ("hardwired internal limit for test buffers exceeded"); + + return bufferID < this->size(); + } + + + + +} // namespace engine diff --git a/src/proc/engine/tracking-heap-block-provider.hpp b/src/proc/engine/tracking-heap-block-provider.hpp new file mode 100644 index 000000000..af4f5d662 --- /dev/null +++ b/src/proc/engine/tracking-heap-block-provider.hpp @@ -0,0 +1,137 @@ +/* + TRACKING-HEAP-BLOCK-PROVIDER.hpp - plain heap allocating BufferProvider implementation for tests + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file tracking-heap-block-provider.hpp + ** Dummy implementation of the BufferProvider interface to support writing unit tests. + ** This BufferProvider is especially straight forward and brain dead: it just claims + ** more and more heap blocks and never releases any memory dynamically. This allows + ** to investigate additional tracking status flags for each allocated block after + ** the fact. + ** + ** The allocated buffers are numbered with a simple ascending sequence of integers, + ** used as LocalKey (see BufferMetadata). Clients can just request a Buffer with the + ** given number, causing that block to be allocated. There is a "backdoor", allowing + ** to access any allocated block, even if it is considered "released" by the terms + ** of the usual lifecycle. Only when the provider object itself gets destroyed, + ** all allocated blocks will be discarded. + ** + ** @see DiagnosticOutputSlot + ** @see DiagnosticBufferProvider + ** @see buffer-provider-protocol-test.cpp + */ + +#ifndef PROC_ENGINE_TRACKING_HEAP_BLOCK_PROVIDER_H +#define PROC_ENGINE_TRACKING_HEAP_BLOCK_PROVIDER_H + + +#include "lib/error.hpp" +#include "proc/engine/buffer-provider.hpp" +#include "lib/scoped-ptrvect.hpp" + +#include <boost/scoped_ptr.hpp> +#include <boost/scoped_array.hpp> + + +namespace engine { + + namespace error = lumiera::error; + + + namespace diagn { + + using boost::scoped_array; + + + /** + * Helper for a diagnostic BufferProvider: + * A block of heap allocated storage, with the capability + * to store some additional tracking information. + */ + class Block + : boost::noncopyable + { + size_t size_; + scoped_array<char> storage_; + + bool was_locked_; + + public: + Block() + : size_(0) + , storage_() + , was_locked_(false) + { } + + bool + was_used() const + { + return was_locked_; + } + + bool + was_closed() const + { + return was_locked_; + } + + void* + accessMemory() const + { + return storage_.get(); + } + }; + } + + /** + * simple BufferProvider implementation with additional allocation tracking. + * @internal used as PImpl by DiagnosticBufferProvider and DiagnosticOutputSlot. + * + * This dummy implementation of the BufferProvider interface uses a linearly growing + * table of heap allocated buffer blocks, which will never be discarded, unless the object + * is discarded as a whole. There is an additional testing/diagnostics API to access the + * tracked usage information, even when blocks are already marked as "released". + */ + class TrackingHeapBlockProvider + : public BufferProvider + , public lib::ScopedPtrVect<diagn::Block> + { + + virtual uint announce (uint count, BufferDescriptor const& type); + virtual BuffHandle lockBufferFor (BufferDescriptor const& descriptor); + virtual void releaseBuffer (BuffHandle const& handle); + + public: + TrackingHeapBlockProvider(); + virtual ~TrackingHeapBlockProvider(); + + diagn::Block& access_or_create (uint bufferID); + + private: + bool withinStorageSize (uint bufferID) const; + }; + + + + + +} // namespace engine +#endif diff --git a/src/proc/play/output-slot-connection.hpp b/src/proc/play/output-slot-connection.hpp index c19080ed4..570ca3ebf 100644 --- a/src/proc/play/output-slot-connection.hpp +++ b/src/proc/play/output-slot-connection.hpp @@ -73,7 +73,26 @@ namespace play { - /** established output channel */ + /** @internal represents the \em active + * point in each of the per-channel connections + * used when this OutputSlot is operational. + * + * \par OutputSlot Core API + * + * Actually, this extension point towards the implementation + * of the actual output handling carries the core API of OutputSlot. + * Thus, the task of actually implementing an OutputSlot boils down + * to implementing this interface and providing a ConnectionState. + * - \c lock() announces this FrameID and the corresponding buffer + * to be in exclusive use by the client from now on + * - \c transfer() ends the client sided processing and initiates + * the outputting of the data found in the corresponding buffer. + * - \c pushout() actually pushes the denoted buffer to the output. + * Typically, \c pushout() is called from the \c transfer() + * implementation; yet it may as well be called from a separate + * service thread or some kind of callback. + * @note the meaning of FrameID is implementation defined. + */ class OutputSlot::Connection { public: @@ -86,7 +105,13 @@ namespace play { - + /** + * Extension point for Implementation. + * The ConnectionState is where the concrete output + * handling implementation is expected to reside. + * OutputSlot is a frontend and accesses + * ConnectionState in the way of a PImpl. + */ class OutputSlot::ConnectionState : public OutputSlot::Allocation , boost::noncopyable @@ -96,12 +121,28 @@ namespace play { }; + /** + * Base class for the typical implementation approach. + * Using this class is \em not mandatory. But obviously, + * we'd get to manage a selection of Connection objects + * representing the "active points" in several media channels + * connected through this OutputSlot. These Connection subclasses + * are what is referenced by the DataSink smart-ptrs handed out + * to the client code. As ConnectionState implements the Allocation + * API, it has the liability to create these DataSink smart-ptrs, + * which means to wire them appropriately and also provide an + * deleter function (here #shutdownConnection) to be invoked + * when the last copy of the smart-handle goes out of scope. + * + * The typical standard/base implementation provided here + * manages a collection of active Connection subclass objects. + */ template<class CON> class ConnectionStateManager : public OutputSlot::ConnectionState , public vector<CON> { - + typedef OutputSlot::OpenedSinks OpenedSinks; @@ -126,7 +167,7 @@ namespace play { { UNIMPLEMENTED ("immediately build up the necessary number of connections"); } - + virtual ~ConnectionStateManager() { From c8458ab39728eda235086cb35f3e0ba4281ee084 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 7 Nov 2011 00:50:03 +0100 Subject: [PATCH 252/496] improved diagnostics --- src/include/logging.h | 3 +++ src/lib/error.hpp | 21 +++++++++++++++++++++ src/proc/engine/buffer-metadata.hpp | 14 +------------- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/include/logging.h b/src/include/logging.h index e2b9455ea..6fd024b97 100644 --- a/src/include/logging.h +++ b/src/include/logging.h @@ -90,6 +90,9 @@ NOBUG_CPP_DEFINE_FLAG_PARENT ( fileheader_dbg, backend_dbg); /** base of debug logging for the proc layer */ NOBUG_CPP_DEFINE_FLAG_PARENT ( proc_dbg, debugging); NOBUG_CPP_DEFINE_FLAG_PARENT ( command_dbg, proc_dbg); +NOBUG_CPP_DEFINE_FLAG_PARENT ( session_dbg, proc_dbg); +NOBUG_CPP_DEFINE_FLAG_PARENT ( player_dbg, proc_dbg); +NOBUG_CPP_DEFINE_FLAG_PARENT ( engine_dbg, proc_dbg); /** base of debug logging for the gui */ NOBUG_CPP_DEFINE_FLAG_PARENT ( gui_dbg, debugging); /** base if debug logging for the support library */ diff --git a/src/lib/error.hpp b/src/lib/error.hpp index 5e2427e0a..110ffe61b 100644 --- a/src/lib/error.hpp +++ b/src/lib/error.hpp @@ -215,6 +215,27 @@ namespace lumiera { } // namespace lumiera +/****************************************************** + * convenience shortcut for a sequence of catch blocks + * just logging and consuming an error. Typically + * this sequence will be used within destructors, + * which, by convention, must not throw + */ +#define ERROR_LOG_AND_IGNORE(_FLAG_,_OP_DESCR_) \ + catch (std::exception& problem) \ + { \ + const char* errID = lumiera_error(); \ + WARN (_FLAG_, "%s failed: %s", _OP_DESCR_, problem.what()); \ + TRACE (debugging, "Error flag was: %s", errID);\ + } \ + catch (...) \ + { \ + const char* errID = lumiera_error(); \ + ERROR (_FLAG_, "%s failed with unknown exception; " \ + "error flag is: %s" \ + , _OP_DESCR_, errID); \ + } + /****************************************************** diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 9a73ba20a..57420fe7f 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -635,19 +635,7 @@ namespace engine { { for_each (entries_, verify_is_free); } - catch (std::exception& problem) - { - const char* errID = lumiera_error(); - const char* operation = "Shutdown of BufferProvider metadata store"; - WARN (engine, "%s failed: %s", operation, problem.what()); - TRACE (debugging, "Error flag was: %s", errID); - } - catch (...) - { - const char* errID = lumiera_error(); - const char* operation = "Shutdown of BufferProvider metadata store"; - ERROR (engine, "%s failed with unknown exception; error flag is: %s", operation, errID); - } + ERROR_LOG_AND_IGNORE (engine,"Shutdown of BufferProvider metadata store") static void verify_is_free (std::pair<HashVal, Entry> const& e) From 59dfb7c660d693b78a20dd025f1b83af28f8f70d Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 7 Nov 2011 01:57:33 +0100 Subject: [PATCH 253/496] start drafting a (dummy) output slot implementation this implicates a first attempt to build a dummy buffer provider implementation. Mostly just defining stubs here.... --- src/proc/play/output-slot-connection.hpp | 7 +- .../proc/play/diagnostic-output-slot.hpp | 81 ++++++++++++++++++- 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/src/proc/play/output-slot-connection.hpp b/src/proc/play/output-slot-connection.hpp index 570ca3ebf..93efd8ca9 100644 --- a/src/proc/play/output-slot-connection.hpp +++ b/src/proc/play/output-slot-connection.hpp @@ -181,7 +181,7 @@ namespace play { private: // Implementation details - DataSink + static DataSink connectOutputSink (CON& connection) { DataSink newSink; @@ -189,9 +189,10 @@ namespace play { return newSink; } - void - shutdownConnection (CON* toClose) + static void + shutdownConnection (void* toClose) { + ///////////////////////////////////////////////////////////TODO problem: is it possible to pass the concrete type???? UNIMPLEMENTED ("how to mark a connection as closed"); } }; diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index ffd1ce829..39105874c 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -32,15 +32,19 @@ #include "lib/error.hpp" +#include "include/logging.h" #include "proc/play/output-slot.hpp" +#include "proc/play/output-slot-connection.hpp" +#include "proc/engine/buffhandle.hpp" +#include "proc/engine/tracking-heap-block-provider.hpp" #include "lib/iter-source.hpp" ////////////TODO really going down that path...? #include "proc/engine/testframe.hpp" //#include "lib/sync.hpp" -//#include <boost/noncopyable.hpp> +#include <boost/noncopyable.hpp> //#include <string> //#include <vector> -//#include <tr1/memory> +#include <tr1/memory> //#include <boost/scoped_ptr.hpp> @@ -48,14 +52,77 @@ namespace proc { namespace play { //using std::string; + using ::engine::BufferDescriptor; using ::engine::test::TestFrame; + using ::engine::TrackingHeapBlockProvider; //using std::vector; -//using std::tr1::shared_ptr; + using std::tr1::shared_ptr; //using boost::scoped_ptr; + class TrackingInMemoryBlockSequence + : public OutputSlot::Connection + { + + shared_ptr<BufferProvider> buffProvider_; + BufferDescriptor bufferType_; + + + /* === Connection API === */ + + void + lock (FrameID) + { + buffProvider_->lockBufferFor (bufferType_); + /////////////////////////////////////////////////TODO: should return that + } + + void + transfer (FrameID frameNr) + { + pushout (frameNr); + } + + void + pushout (FrameID) + { + UNIMPLEMENTED ("simulate output"); + } + + + public: + TrackingInMemoryBlockSequence() + : buffProvider_(new TrackingHeapBlockProvider()) + , bufferType_(buffProvider_->getDescriptor<TestFrame>()) + { + INFO (engine_dbg, "building in-memory diagnostic output sequence"); + } + + virtual + ~TrackingInMemoryBlockSequence() + { + INFO (engine_dbg, "releasing diagnostic output sequence"); + } + }; + + + class SimulatedOutputSequences + : public ConnectionStateManager<TrackingInMemoryBlockSequence> + , boost::noncopyable + { + TrackingInMemoryBlockSequence + buildConnection() + { + return TrackingInMemoryBlockSequence(); + } + }; + + + + + /******************************************************************** * Helper for unit tests: Mock output sink. * @@ -64,6 +131,14 @@ namespace play { class DiagnosticOutputSlot : public OutputSlot { + /* === hook into the OutputSlot frontend === */ + ConnectionState* + buildState() + { + return new SimulatedOutputSequences(); + } + + public: /** build a new Diagnostic Output Slot instance, * discard the existing one. Use the static query API From f75e55a0607b342e146c6a47b57b5fde4505ce13 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 8 Nov 2011 02:59:56 +0100 Subject: [PATCH 254/496] nail down a lot of OutputSlot implementation details --- src/proc/engine/buffer-provider.hpp | 1 + .../engine/tracking-heap-block-provider.cpp | 9 +++- .../engine/tracking-heap-block-provider.hpp | 1 + src/proc/play/output-slot-connection.hpp | 44 ++++++++++------- src/proc/play/output-slot.cpp | 24 ++++++++++ src/proc/play/output-slot.hpp | 6 +-- .../proc/play/diagnostic-output-slot.hpp | 47 +++++++++++++++---- .../proc/play/output-slot-protocol-test.cpp | 6 +-- 8 files changed, 105 insertions(+), 33 deletions(-) diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 97712c712..ba4a4dd7f 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -83,6 +83,7 @@ namespace engine { virtual uint announce (uint count, BufferDescriptor const&) =0; virtual BuffHandle lockBufferFor (BufferDescriptor const&) =0; + virtual void mark_emitted (BuffHandle const&) =0; virtual void releaseBuffer (BuffHandle const&) =0; template<typename BU> diff --git a/src/proc/engine/tracking-heap-block-provider.cpp b/src/proc/engine/tracking-heap-block-provider.cpp index 947acc614..7fcd6ade4 100644 --- a/src/proc/engine/tracking-heap-block-provider.cpp +++ b/src/proc/engine/tracking-heap-block-provider.cpp @@ -78,10 +78,17 @@ namespace engine { } + void + TrackingHeapBlockProvider::mark_emitted (BuffHandle const& handle) + { + UNIMPLEMENTED ("commit a buffer to the protocol section"); + } + + void TrackingHeapBlockProvider::releaseBuffer (BuffHandle const& handle) { - UNIMPLEMENTED ("release a buffer and invalidate the handle"); + UNIMPLEMENTED ("mark a buffer as officially discarded"); } diff --git a/src/proc/engine/tracking-heap-block-provider.hpp b/src/proc/engine/tracking-heap-block-provider.hpp index af4f5d662..decdc5915 100644 --- a/src/proc/engine/tracking-heap-block-provider.hpp +++ b/src/proc/engine/tracking-heap-block-provider.hpp @@ -117,6 +117,7 @@ namespace engine { virtual uint announce (uint count, BufferDescriptor const& type); virtual BuffHandle lockBufferFor (BufferDescriptor const& descriptor); + virtual void mark_emitted (BuffHandle const& handle); virtual void releaseBuffer (BuffHandle const& handle); public: diff --git a/src/proc/play/output-slot-connection.hpp b/src/proc/play/output-slot-connection.hpp index 93efd8ca9..34a574d9d 100644 --- a/src/proc/play/output-slot-connection.hpp +++ b/src/proc/play/output-slot-connection.hpp @@ -59,7 +59,7 @@ namespace proc { namespace play { -//using ::engine::BuffHandle; + using ::engine::BuffHandle; //using ::engine::BufferProvider; //using lib::time::Time; //using std::string; @@ -98,9 +98,12 @@ namespace play { public: virtual ~Connection(); - virtual void lock (FrameID) =0; - virtual void transfer (FrameID) =0; - virtual void pushout (FrameID) =0; + virtual BuffHandle claimBufferFor(FrameID) =0; + virtual bool isTimely (FrameID, TimeValue) =0; + virtual void transfer (BuffHandle const&) =0; + virtual void pushout (BuffHandle const&) =0; + virtual void discard (BuffHandle const&) =0; + virtual void shutDown () =0; }; @@ -164,19 +167,24 @@ namespace play { public: ConnectionStateManager() - { - UNIMPLEMENTED ("immediately build up the necessary number of connections"); - } - - virtual - ~ConnectionStateManager() - { - UNIMPLEMENTED ("shut down all connections"); - } + { } virtual - CON - buildConnection() =0; + ~ConnectionStateManager() + { } + + + void + init (uint numChannels) + { + for (uint i=0; i<numChannels; ++i) + push_back(buildConnection()); + } + + + /** factory function to build the actual + * connection handling objects per channel */ + virtual CON buildConnection() =0; private: // Implementation details @@ -190,10 +198,10 @@ namespace play { } static void - shutdownConnection (void* toClose) + shutdownConnection (OutputSlot::Connection* toClose) { - ///////////////////////////////////////////////////////////TODO problem: is it possible to pass the concrete type???? - UNIMPLEMENTED ("how to mark a connection as closed"); + REQUIRE (toClose); + toClose->shutDown(); } }; diff --git a/src/proc/play/output-slot.cpp b/src/proc/play/output-slot.cpp index 6cd3c5aa4..e93acb278 100644 --- a/src/proc/play/output-slot.cpp +++ b/src/proc/play/output-slot.cpp @@ -51,6 +51,8 @@ namespace play { OutputSlot::Allocation::~Allocation() { } + OutputSlot::Connection::~Connection() { } + @@ -86,6 +88,28 @@ namespace play { if (!isFree()) state_.reset(0); } + + + + /* === DataSink frontend === */ + + BuffHandle + DataSink::lockBufferFor(FrameID frameNr) + { + return impl().claimBufferFor(frameNr); + } + + + void + DataSink::emit (FrameID frameNr, BuffHandle const& data2emit, TimeValue currentTime) + { + OutputSlot::Connection& connection = impl(); + if (connection.isTimely(frameNr,currentTime)) + connection.transfer(data2emit); + else + connection.discard(data2emit); + } + diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index 04754e4bd..bd51a03b9 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -38,7 +38,7 @@ #include "lib/error.hpp" #include "lib/handle.hpp" -//#include "lib/time/timevalue.hpp" +#include "lib/time/timevalue.hpp" #include "proc/engine/buffer-provider.hpp" #include "proc/play/timings.hpp" #include "lib/iter-source.hpp" @@ -56,7 +56,7 @@ namespace play { using ::engine::BuffHandle; using ::engine::BufferProvider; -//using lib::time::Time; + using lib::time::TimeValue; //using std::string; //using std::vector; @@ -132,7 +132,7 @@ namespace play { public: BuffHandle lockBufferFor(FrameID); - void emit(FrameID); + void emit(FrameID, BuffHandle const&, TimeValue currentTime = Time::MAX); ///////////////TICKET #855 }; diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index 39105874c..ff0c96486 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -72,25 +72,47 @@ namespace play { /* === Connection API === */ - void - lock (FrameID) + BuffHandle + claimBufferFor(FrameID frameNr) { buffProvider_->lockBufferFor (bufferType_); - /////////////////////////////////////////////////TODO: should return that + } + + + bool + isTimely (FrameID frameNr, TimeValue currentTime) + { + if (Time::MAX == currentTime) + return true; + + UNIMPLEMENTED ("find out about timings"); + return false; } void - transfer (FrameID frameNr) + transfer (BuffHandle const& filledBuffer) { - pushout (frameNr); + pushout (filledBuffer); } void - pushout (FrameID) + pushout (BuffHandle const& data4output) { - UNIMPLEMENTED ("simulate output"); + buffProvider_->mark_emitted (data4output); + buffProvider_->releaseBuffer (data4output); } + void + discard (BuffHandle const& superseededData) + { + buffProvider_->releaseBuffer (superseededData); + } + + void + shutDown () + { + buffProvider_.reset(); + } public: TrackingInMemoryBlockSequence() @@ -117,6 +139,12 @@ namespace play { { return TrackingInMemoryBlockSequence(); } + + public: + SimulatedOutputSequences (uint numChannels) + { + init (numChannels); + } }; @@ -131,11 +159,14 @@ namespace play { class DiagnosticOutputSlot : public OutputSlot { + + static const uint MAX_CHANNELS = 5; + /* === hook into the OutputSlot frontend === */ ConnectionState* buildState() { - return new SimulatedOutputSequences(); + return new SimulatedOutputSequences(MAX_CHANNELS); } diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp index 29b1db615..ae0319b6d 100644 --- a/tests/components/proc/play/output-slot-protocol-test.cpp +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -106,9 +106,9 @@ namespace test { buff10.accessAs<TestFrame>() = testData(1,0); // Now it's time to emit the output - sink2.emit (frameNr-1); - sink2.emit (frameNr ); - sink1.emit (frameNr-1); + sink2.emit (frameNr-1, buff10); + sink2.emit (frameNr , buff11); + sink1.emit (frameNr-1, buff00); // that's all for the client // Verify sane operation.... From fe1ae51b494caf81c4673fb7c32940185037e32d Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 11 Nov 2011 01:44:01 +0100 Subject: [PATCH 255/496] WIP draft test for internal test buffer provider a test to cover a helper for writing tests ;-) --- src/proc/engine/buffer-provider.hpp | 2 +- tests/46engine.tests | 5 + .../tracking-heap-block-provider-test.cpp | 202 ++++++++++++++++++ 3 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 tests/components/proc/engine/tracking-heap-block-provider-test.cpp diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index ba4a4dd7f..368d54a4c 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -95,7 +95,7 @@ namespace engine { template<typename BU> BufferDescriptor getDescriptor(); - + /* === API for BuffHandle internal access === */ diff --git a/tests/46engine.tests b/tests/46engine.tests index 46c1806db..07fac0119 100644 --- a/tests/46engine.tests +++ b/tests/46engine.tests @@ -7,6 +7,11 @@ return: 0 END +TEST "Test support: dummy buffer provider" TrackingHeapBlockProvider_test <<END +return: 0 +END + + PLANNED "Buffer provider diagnostics" BufferProviderProtocol_test <<END return: 0 END diff --git a/tests/components/proc/engine/tracking-heap-block-provider-test.cpp b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp new file mode 100644 index 000000000..48b385085 --- /dev/null +++ b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp @@ -0,0 +1,202 @@ +/*u4 + TrackingHeapBlockProvider(Test) - verify a support facility for diagnostic/test purposes + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/error.hpp" +#include "lib/test/run.hpp" +//#include "lib/test/test-helper.hpp" +//#include "lib/util-foreach.hpp" +//#include "proc/play/diagnostic-output-slot.hpp" +#include "proc/engine/tracking-heap-block-provider.hpp" +#include "proc/engine/testframe.hpp" +//#include "proc/engine/diagnostic-buffer-provider.hpp" +#include "proc/engine/buffhandle.hpp" +//#include "proc/engine/bufftable.hpp" + +//#include <boost/format.hpp> +//#include <iostream> +#include <vector> +#include <cstdlib> + +//using boost::format; +//using std::string; +//using std::cout; +using std::rand; +//using util::for_each; + + +namespace engine{ +namespace test { + +// using lib::AllocationCluster; +// using mobject::session::PEffect; +// using ::engine::BuffHandle; +// using lumiera::error::LUMIERA_ERROR_LIFECYCLE; + + + namespace { // Test fixture + + const size_t TEST_ELM_SIZE = sizeof(uint); + const uint MAX_ELMS = 50; + + std::vector<uint> testNumbers(MAX_ELMS); + + + bool + has_expectedContent (uint nr, diagn::Block& memoryBlock) + { + void* mem = memoryBlock.accessMemory(); + uint data = *static_cast<uint*> (mem); + + return data == testNumbers[nr]; + } + + bool + verifyUsedBlock (uint nr, diagn::Block& memoryBlock) + { + return memoryBlock.was_closed() + && has_expectedContent (nr, memoryBlock); + } + } + + + /********************************************************************** + * @test verify a test support facility, used to write mock components + * to test the lumiera engine. The TrackingHeapBlockProvider is a + * braindead implementation of the BufferProvider interface: it just + * claims new heap blocks and never de-allocates them, allowing other + * test and mock objects to verify allocated buffers after the fact. + */ + class TrackingHeapBlockProvider_test : public Test + { + virtual void + run (Arg) + { + UNIMPLEMENTED ("verify test helper"); + simpleExample(); + verifyStandardCase(); + verifyTestProtocol(); + } + + + void + simpleExample() + { + TrackingHeapBlockProvider provider; + + BuffHandle testBuff = provider.lockBufferFor<TestFrame>(); + CHECK (testBuff); + CHECK (testBuff.accessAs<TestFrame>().isSane()); + + uint dataID = 1 + rand() % 29; + testBuff.accessAs<TestFrame>() = testData(dataID); + + provider.mark_emitted (testBuff); + provider.releaseBuffer(testBuff); + + diagn::Block& block0 = provider.access_or_create(0); + CHECK (block0.was_used()); + CHECK (block0.was_closed()); + + CHECK (testData(dataID) == block0.accessMemory()); + } + + + void + verifyStandardCase() + { + TrackingHeapBlockProvider provider; + + BufferDescriptor buffType = provider.getDescriptorFor(TEST_ELM_SIZE); + uint numElms = provider.announce(MAX_ELMS, buffType); + CHECK (0 < numElms); + CHECK (numElms <= MAX_ELMS); + + for (uint i=0; i<numElms; ++i) + { + BuffHandle buff = provider.lockBufferFor(buffType); + buff.accessAs<uint>() = testNumbers[i] = rand() % 100000; + provider.mark_emitted (buff); + provider.releaseBuffer(buff); + } + + for (uint nr=0; nr<numElms; ++nr) + { + CHECK (verifyUsedBlock (nr, provider.access_or_create(nr))); + } + } + + + void + verifyTestProtocol() + { + TrackingHeapBlockProvider provider; + + BufferDescriptor buffType = provider.getDescriptorFor(TEST_ELM_SIZE); + + BuffHandle bu1 = provider.lockBufferFor(buffType); + BuffHandle bu2 = provider.lockBufferFor(buffType); + BuffHandle bu3 = provider.lockBufferFor(buffType); + BuffHandle bu4 = provider.lockBufferFor(buffType); + BuffHandle bu5 = provider.lockBufferFor(buffType); + + CHECK (5 == provider.size()); + + provider.access<uint>(0) = 20; + provider.access<uint>(1) = 21; + provider.access<uint>(2) = 22; + provider.access<uint>(3) = 23; + provider.access<uint>(4) = 24; + + bu1.accessAs<uint>() = 1; + bu2.accessAs<uint>() = 2; + bu3.accessAs<uint>() = 3; + bu4.accessAs<uint>() = 4; + bu5.accessAs<uint>() = 5; + + CHECK (20 == provider.access<uint>(0)); + CHECK (21 == provider.access<uint>(1)); + CHECK (22 == provider.access<uint>(2)); + CHECK (23 == provider.access<uint>(3)); + CHECK (24 == provider.access<uint>(4)); + + provider.mark_emitted (bu3); + provider.mark_emitted (bu1); + provider.mark_emitted (bu5); + provider.mark_emitted (bu4); + provider.mark_emitted (bu2); + + CHECK (3 == provider.access<uint>(0)); + CHECK (1 == provider.access<uint>(1)); + CHECK (5 == provider.access<uint>(2)); + CHECK (4 == provider.access<uint>(3)); + CHECK (2 == provider.access<uint>(4)); + } + }; + + + /** Register this test class... */ + LAUNCHER (TrackingHeapBlockProvider_test, "unit player"); + + + +}} // namespace engine::test From fd94367b9e6a1751972d50412e2c10684aa0260d Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 11 Nov 2011 23:33:59 +0100 Subject: [PATCH 256/496] stubs and changes to make the test compile --- .../engine/tracking-heap-block-provider.hpp | 33 +++++++++++++++++ .../tracking-heap-block-provider-test.cpp | 37 +++++++++---------- .../proc/play/diagnostic-output-slot.hpp | 2 +- 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/proc/engine/tracking-heap-block-provider.hpp b/src/proc/engine/tracking-heap-block-provider.hpp index decdc5915..a7065252a 100644 --- a/src/proc/engine/tracking-heap-block-provider.hpp +++ b/src/proc/engine/tracking-heap-block-provider.hpp @@ -46,6 +46,7 @@ #include "lib/error.hpp" #include "proc/engine/buffer-provider.hpp" #include "lib/scoped-ptrvect.hpp" +#include "lib/access-casted.hpp" #include <boost/scoped_ptr.hpp> #include <boost/scoped_array.hpp> @@ -115,6 +116,10 @@ namespace engine { , public lib::ScopedPtrVect<diagn::Block> { + public: + /* === BufferProvider interface === */ + + using BufferProvider::lockBufferFor; virtual uint announce (uint count, BufferDescriptor const& type); virtual BuffHandle lockBufferFor (BufferDescriptor const& descriptor); virtual void mark_emitted (BuffHandle const& handle); @@ -126,12 +131,40 @@ namespace engine { diagn::Block& access_or_create (uint bufferID); + template<typename TY> + TY& accessAs (uint bufferID); + private: bool withinStorageSize (uint bufferID) const; }; + /** convenience shortcut: access the buffer with the given number, + * then try to convert the raw memory to the templated type. + * @throw error::Invalid if the required fame number is beyond + * the number of buffers marked as "emitted" + * @throw error::Fatal if conversion is not possible or the + * conversion path chosen doesn't work (which might + * be due to RTTI indicating an incompatible type). + */ + template<typename TY> + TY& + TrackingHeapBlockProvider::accessAs (uint bufferID) + { + if (!withinStorageSize (bufferID)) + throw error::Invalid ("Buffer with the given ID not yet emitted"); + + diagn::Block& memoryBlock = access_or_create (bufferID); + TY* converted = util::AccessCasted<TY*>::access (memoryBlock.accessMemory()); + + if (!converted) + throw error::Fatal ("unable to access the target location with the required conversion"); + else + return *converted; + } + + } // namespace engine diff --git a/tests/components/proc/engine/tracking-heap-block-provider-test.cpp b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp index 48b385085..47fd1bcd4 100644 --- a/tests/components/proc/engine/tracking-heap-block-provider-test.cpp +++ b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp @@ -73,7 +73,8 @@ namespace test { bool verifyUsedBlock (uint nr, diagn::Block& memoryBlock) { - return memoryBlock.was_closed() + return memoryBlock.was_used() + && memoryBlock.was_closed() && has_expectedContent (nr, memoryBlock); } } @@ -91,7 +92,6 @@ namespace test { virtual void run (Arg) { - UNIMPLEMENTED ("verify test helper"); simpleExample(); verifyStandardCase(); verifyTestProtocol(); @@ -114,9 +114,6 @@ namespace test { provider.releaseBuffer(testBuff); diagn::Block& block0 = provider.access_or_create(0); - CHECK (block0.was_used()); - CHECK (block0.was_closed()); - CHECK (testData(dataID) == block0.accessMemory()); } @@ -161,11 +158,11 @@ namespace test { CHECK (5 == provider.size()); - provider.access<uint>(0) = 20; - provider.access<uint>(1) = 21; - provider.access<uint>(2) = 22; - provider.access<uint>(3) = 23; - provider.access<uint>(4) = 24; + provider.accessAs<uint>(0) = 20; + provider.accessAs<uint>(1) = 21; + provider.accessAs<uint>(2) = 22; + provider.accessAs<uint>(3) = 23; + provider.accessAs<uint>(4) = 24; bu1.accessAs<uint>() = 1; bu2.accessAs<uint>() = 2; @@ -173,11 +170,11 @@ namespace test { bu4.accessAs<uint>() = 4; bu5.accessAs<uint>() = 5; - CHECK (20 == provider.access<uint>(0)); - CHECK (21 == provider.access<uint>(1)); - CHECK (22 == provider.access<uint>(2)); - CHECK (23 == provider.access<uint>(3)); - CHECK (24 == provider.access<uint>(4)); + CHECK (20 == provider.accessAs<uint>(0)); + CHECK (21 == provider.accessAs<uint>(1)); + CHECK (22 == provider.accessAs<uint>(2)); + CHECK (23 == provider.accessAs<uint>(3)); + CHECK (24 == provider.accessAs<uint>(4)); provider.mark_emitted (bu3); provider.mark_emitted (bu1); @@ -185,11 +182,11 @@ namespace test { provider.mark_emitted (bu4); provider.mark_emitted (bu2); - CHECK (3 == provider.access<uint>(0)); - CHECK (1 == provider.access<uint>(1)); - CHECK (5 == provider.access<uint>(2)); - CHECK (4 == provider.access<uint>(3)); - CHECK (2 == provider.access<uint>(4)); + CHECK (3 == provider.accessAs<uint>(0)); + CHECK (1 == provider.accessAs<uint>(1)); + CHECK (5 == provider.accessAs<uint>(2)); + CHECK (4 == provider.accessAs<uint>(3)); + CHECK (2 == provider.accessAs<uint>(4)); } }; diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index ff0c96486..571c1dad0 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -66,7 +66,7 @@ namespace play { : public OutputSlot::Connection { - shared_ptr<BufferProvider> buffProvider_; + shared_ptr<TrackingHeapBlockProvider> buffProvider_; BufferDescriptor bufferType_; From ca0aa234799940edceaa90ddfdb91b98a528c6cd Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 12 Nov 2011 00:36:53 +0100 Subject: [PATCH 257/496] BufferProvider default impl: attaching a type --- src/proc/engine/buffer-metadata.hpp | 118 +----------------- src/proc/engine/buffer-provider.cpp | 11 +- src/proc/engine/buffer-provider.hpp | 11 +- src/proc/engine/type-handler.hpp | 182 ++++++++++++++++++++++++++++ 4 files changed, 201 insertions(+), 121 deletions(-) create mode 100644 src/proc/engine/type-handler.hpp diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 57420fe7f..b06f01035 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -56,13 +56,11 @@ #include "lib/error.hpp" #include "lib/symbol.hpp" -#include "lib/functor-util.hpp" #include "lib/util-foreach.hpp" #include "include/logging.h" +#include "proc/engine/type-handler.hpp" -#include <tr1/functional> #include <tr1/unordered_map> -#include <boost/functional/hash.hpp> #include <boost/noncopyable.hpp> @@ -71,9 +69,6 @@ namespace engine { using lib::HashVal; using lib::Literal; using util::for_each; - using std::tr1::bind; - using std::tr1::function; - using std::tr1::placeholders::_1; namespace error = lumiera::error; @@ -138,117 +133,6 @@ namespace engine { }; - namespace { // (optional) helpers to build an object embedded within the buffer... - - template<class X> - inline void - buildIntoBuffer (void* storageBuffer) - { - new(storageBuffer) X(); - } - - template<class X, typename A1> - inline void - buildIntoBuffer_A1 (void* storageBuffer, A1 arg1) - { - new(storageBuffer) X(arg1); - } - - template<class X> - inline void - destroyInBuffer (void* storageBuffer) - { - X* embedded = static_cast<X*> (storageBuffer); - embedded->~X(); - } - }//(End)placement-new helpers - - - - /** - * A pair of functors to maintain a datastructure within the buffer. - * TypeHandler describes how to outfit the buffer in a specific way. - * When defined, the buffer will be prepared when locking and cleanup - * will be invoked automatically when releasing. Especially, this - * can be used to \em attach an object to the buffer (placement-new) - */ - struct TypeHandler - { - typedef function<void(void*)> DoInBuffer; - - DoInBuffer createAttached; - DoInBuffer destroyAttached; - - /** build an invalid NIL TypeHandler */ - TypeHandler() - : createAttached() - , destroyAttached() - { } - - /** build a TypeHandler - * binding to arbitrary constructor and destructor functions. - * On invocation, these functions get a void* to the buffer. - * @note the functor objects created from these operations - * might be shared for handling multiple buffers. - * Be careful with any state or arguments. - */ - template<typename CTOR, typename DTOR> - TypeHandler(CTOR ctor, DTOR dtor) - : createAttached (ctor) - , destroyAttached (dtor) - { } - - /** builder function defining a TypeHandler - * to place a default-constructed object - * into the buffer. */ - template<class X> - static TypeHandler - create () - { - return TypeHandler (buildIntoBuffer<X>, destroyInBuffer<X>); - } - - template<class X, typename A1> - static TypeHandler - create (A1 a1) - { - return TypeHandler ( bind (buildIntoBuffer_A1<X,A1>, _1, a1) - , destroyInBuffer<X>); - } - - bool - isValid() const - { - return bool(createAttached) - && bool(destroyAttached); - } - - friend HashVal - hash_value (TypeHandler const& handler) - { - HashVal hash(0); - if (handler.isValid()) - { - boost::hash_combine(hash, handler.createAttached); - boost::hash_combine(hash, handler.destroyAttached); - } - return hash; - } - - friend bool - operator== (TypeHandler const& left, TypeHandler const& right) - { - return (!left.isValid() && !right.isValid()) - || ( util::rawComparison(left.createAttached, right.createAttached) - && util::rawComparison(left.destroyAttached, right.destroyAttached) - ); - } - friend bool - operator!= (TypeHandler const& left, TypeHandler const& right) - { - return !(left == right); - } - }; namespace { // internal constants to mark the default case diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp index f0d1b337f..ea5e4f5f0 100644 --- a/src/proc/engine/buffer-provider.cpp +++ b/src/proc/engine/buffer-provider.cpp @@ -58,8 +58,15 @@ namespace engine { { return BufferDescriptor (*this, meta_->key (storageSize)); } - - + + + BufferDescriptor + BufferProvider::getDescriptorFor(size_t storageSize, TypeHandler specialTreatment) + { + return BufferDescriptor (*this, meta_->key (storageSize, specialTreatment)); + } + + /* === BufferDescriptor and BuffHandle === */ diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 368d54a4c..174555acf 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -45,6 +45,7 @@ #include "lib/error.hpp" #include "lib/symbol.hpp" #include "proc/engine/buffhandle.hpp" +#include "proc/engine/type-handler.hpp" #include <boost/noncopyable.hpp> #include <boost/scoped_ptr.hpp> @@ -92,6 +93,7 @@ namespace engine { /** describe the kind of buffer managed by this provider */ BufferDescriptor getDescriptorFor(size_t storageSize=0); + BufferDescriptor getDescriptorFor(size_t storageSize, TypeHandler specialTreatment); template<typename BU> BufferDescriptor getDescriptor(); @@ -120,15 +122,20 @@ namespace engine { BuffHandle BufferProvider::lockBufferFor() { - UNIMPLEMENTED ("convenience shortcut to announce and lock for a specific object type"); + BufferDescriptor buffer_to_attach_object = getDescriptor<BU>(); + return lockBufferFor (buffer_to_attach_object); } + /** define a "buffer type" for automatically creating + * an instance of the template type embedded into the buffer + * and destroying that embedded object when releasing the buffer. + */ template<typename BU> BufferDescriptor BufferProvider::getDescriptor() { - UNIMPLEMENTED ("build descriptor for automatically placing an object instance into the buffer"); + return getDescriptorFor (sizeof(BU), TypeHandler::create<BU>()); } diff --git a/src/proc/engine/type-handler.hpp b/src/proc/engine/type-handler.hpp new file mode 100644 index 000000000..d7130d308 --- /dev/null +++ b/src/proc/engine/type-handler.hpp @@ -0,0 +1,182 @@ +/* + TYPE-HANDLER.hpp - a functor pair for setup and destruction + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file type-handler.hpp + ** Helper holding a pair of type-build-up and destruction functors. + ** Basically these two functors embody all type specific knowledge required + ** to place an object into some buffer space and to clean up later. They may even + ** be used in a more unspecific way, e.g. just to "prepare" a buffer or frame and to + ** "clean up" after usage. + ** + ** Within the Lumiera Engine, the BufferProvider default implementation utilises instances + ** of TypeHandler to \em describe specific buffer types capable of managing an attached object, + ** or requiring some other kind of special treatment of the memory area used for the buffer. + ** This BufferDescriptor is embodied into the BufferMetadata::Key and used later on to invoke + ** the contained ctor / dtor functors, passing a concrete buffer (memory area). + ** + ** @see buffer-metadata.hpp + ** @see buffer-provider.hpp + ** @see BufferMetadataKey_test#verifyTypeHandler unit-test + */ + +#ifndef PROC_ENGINE_TYPE_HANDLER_H +#define PROC_ENGINE_TYPE_HANDLER_H + + +#include "lib/error.hpp" +#include "lib/functor-util.hpp" + +#include <tr1/functional> +#include <boost/functional/hash.hpp> + + +namespace engine { + + using lib::HashVal; + using std::tr1::bind; + using std::tr1::function; + using std::tr1::placeholders::_1; + + namespace error = lumiera::error; + + + namespace { // (optional) helpers to build an object embedded into a buffer... + + template<class X> + inline void + buildIntoBuffer (void* storageBuffer) + { + new(storageBuffer) X(); + } + + template<class X, typename A1> + inline void + buildIntoBuffer_A1 (void* storageBuffer, A1 arg1) + { + new(storageBuffer) X(arg1); + } + + template<class X> + inline void + destroyInBuffer (void* storageBuffer) + { + X* embedded = static_cast<X*> (storageBuffer); + embedded->~X(); + } + }//(End)placement-new helpers + + + + /** + * A pair of functors to maintain a datastructure within a buffer. + * TypeHandler describes how to outfit the buffer in a specific way. + * Special convenience builder function(s) are provided to create a + * TypeHandler performing placement-new into a buffer given on invocation. + * @note engine::BufferMetadata uses a TypeHandler to represent any + * special treatment of a buffer space. When defined, the buffer + * will be prepared on locking and cleanup will be invoked + * automatically when releasing. + * @warning comparison and hash values rely on internals of the + * tr1::function implementation and might not be 100% accurate + */ + struct TypeHandler + { + typedef function<void(void*)> DoInBuffer; + + DoInBuffer createAttached; + DoInBuffer destroyAttached; + + /** build an invalid NIL TypeHandler */ + TypeHandler() + : createAttached() + , destroyAttached() + { } + + /** build a TypeHandler + * binding to arbitrary constructor and destructor functions. + * On invocation, these functions get a void* to the buffer. + * @note the functor objects created from these operations + * might be shared for handling multiple buffers. + * Be careful with any state or arguments. + */ + template<typename CTOR, typename DTOR> + TypeHandler(CTOR ctor, DTOR dtor) + : createAttached (ctor) + , destroyAttached (dtor) + { } + + /** builder function defining a TypeHandler + * to place a default-constructed object + * into the buffer. */ + template<class X> + static TypeHandler + create () + { + return TypeHandler (buildIntoBuffer<X>, destroyInBuffer<X>); + } + + template<class X, typename A1> + static TypeHandler + create (A1 a1) + { + return TypeHandler ( bind (buildIntoBuffer_A1<X,A1>, _1, a1) + , destroyInBuffer<X>); + } + + bool + isValid() const + { + return bool(createAttached) + && bool(destroyAttached); + } + + friend HashVal + hash_value (TypeHandler const& handler) + { + HashVal hash(0); + if (handler.isValid()) + { + boost::hash_combine(hash, handler.createAttached); + boost::hash_combine(hash, handler.destroyAttached); + } + return hash; + } + + friend bool + operator== (TypeHandler const& left, TypeHandler const& right) + { + return (!left.isValid() && !right.isValid()) + || ( util::rawComparison(left.createAttached, right.createAttached) + && util::rawComparison(left.destroyAttached, right.destroyAttached) + ); + } + friend bool + operator!= (TypeHandler const& left, TypeHandler const& right) + { + return !(left == right); + } + }; + + + +} // namespace engine +#endif From 890b6e8366a252e24ce9735ad5b64f12f073568e Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 13 Nov 2011 04:20:14 +0100 Subject: [PATCH 258/496] WIP implementation details of diagnostic BufferProvider --- src/proc/engine/buffer-provider.cpp | 23 ++++ src/proc/engine/buffer-provider.hpp | 4 + .../engine/tracking-heap-block-provider.cpp | 118 +++++++++++++++++- .../engine/tracking-heap-block-provider.hpp | 9 +- 4 files changed, 148 insertions(+), 6 deletions(-) diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp index ea5e4f5f0..2e8ace5bd 100644 --- a/src/proc/engine/buffer-provider.cpp +++ b/src/proc/engine/buffer-provider.cpp @@ -23,7 +23,9 @@ #include "proc/engine/buffer-provider.hpp" #include "proc/engine/buffer-metadata.hpp" +#include "lib/util.hpp" +using util::isSameObject; namespace engine { @@ -67,6 +69,27 @@ namespace engine { } + BuffHandle + BufferProvider::buildHandle (BufferDescriptor const& type, void* storage) + { + REQUIRE (was_created_by_this_provider (type)); + + HashVal typeID (type.subClassification_); + metadata::Key& typeKey = meta_->get (typeID); + metadata::Entry& entry = meta_->markLocked(typeKey, newBlock); + + return BuffHandle (BufferDescriptor(*this, entry), newBlock); + } + + + bool + was_created_by_this_provider (BufferDescriptor const& descr) const + { + return isSameObject (this, descr.provider_); + } + + + /* === BufferDescriptor and BuffHandle === */ diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 174555acf..2e7709d6f 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -104,6 +104,10 @@ namespace engine { bool verifyValidity (BufferDescriptor const&); + protected: + BuffHandle buildHandle (BufferDescriptor const& type, void* storage); + + bool was_created_by_this_provider (BufferDescriptor const&) const; }; diff --git a/src/proc/engine/tracking-heap-block-provider.cpp b/src/proc/engine/tracking-heap-block-provider.cpp index 7fcd6ade4..43d6cda23 100644 --- a/src/proc/engine/tracking-heap-block-provider.cpp +++ b/src/proc/engine/tracking-heap-block-provider.cpp @@ -24,20 +24,124 @@ #include "lib/error.hpp" #include "include/logging.h" //#include "lib/meta/function.hpp" -#include "lib/scoped-ptrvect.hpp" +//#include "lib/scoped-ptrvect.hpp" +#include "lib/scoped-holder.hpp" +#include "lib/util-foreach.hpp" #include "proc/engine/tracking-heap-block-provider.hpp" #include <boost/noncopyable.hpp> -//#include <vector> +#include <vector> -using lib::ScopedPtrVect; +using util::for_each; +using std::vector; +using std::auto_ptr; +using lib::ScopedHolder; namespace engine { - + namespace diagn { + + typedef vector<Block*> PoolVec; + typedef ScopedHolder<PoolVec> PoolHolder; + + /** + * @internal Pool of allocated buffer Blocks of a specific size. + * Helper for implementing a Diagnostic BufferProvider; actually does + * just heap allocations for the Blocks, but keeps a collection of + * allocated Blocks around. Individual entries can be retrieved + * and thus removed from the responsibility of BlockPool. + * + * The idea is that each buffer starts its lifecycle within some pool + * and later gets "emitted" to an output sequence, where it remains for + * later investigation and diagnostics. + */ + class BlockPool + { + size_t memBlockSize_; + PoolHolder blockList_; + + public: + BlockPool() + : memBlockSize_(0) + , blockList_() + { } + + void + initialise (size_t blockSize) + { + blockList_.create(); + memBlockSize_(blockSize); + } + // standard copy operations are valid, but will + // raise an runtime error, once BlockPool is initialised. + + ~BlockPool() + try { + if (blockList_) + for_each (*blockList_, discardBuffer); + } + ERROR_LOG_AND_IGNORE (test, "Shutdown of diagnostic BufferProvider allocation pool"); + + + Block& + createBlock() + { + Block* newBlock(0); + try + { + newBlock = new Block(); ////////////////////////////TODO pass size as ctor param + blockList_->push_back (newBlock); + } + catch(...) + { + if (newBlock) + delete newBlock; + throw; + } + ENSURE (newBlock); + return *newBlock; + } + + + auto_ptr<Block> + transferResponsibility (Block* allocatedBlock) + { + auto_ptr<Block> extracted; + PoolVec& vec = *blockList_; + PoolVec::iterator pos = find (vec.begin(),vec.end(), allocatedBlock); + if (pos != vec.end()) + { + extracted.reset (allocatedBlock); + vec.erase(pos); + } + return extracted; + } + + + size_t + size() const + { + return blockList_->size(); + } + + private: + static void + discardBuffer (Block* block) + { + if (!block) return; + + if (block->was_used() && !block->was_closed()) + ERROR (test, "Block actively in use while shutting down BufferProvider " + "allocation pool. This might lead to Segfault and memory leaks."); + + delete block; + } + }; + + } @@ -54,6 +158,7 @@ namespace engine { */ TrackingHeapBlockProvider::TrackingHeapBlockProvider() : BufferProvider ("Diagnostic_HeapAllocated") + , pool_() { } TrackingHeapBlockProvider::~TrackingHeapBlockProvider() @@ -72,9 +177,12 @@ namespace engine { BuffHandle - TrackingHeapBlockProvider::lockBufferFor (BufferDescriptor const& descriptor) + TrackingHeapBlockProvider::lockBufferFor (BufferDescriptor const& type) { UNIMPLEMENTED ("lock buffer for exclusive use"); + diagn::BlockPool& blocks = getBlockPoolFor (type); + diagn::Block* newBlock = blocks.createBlock(); + return buildHandle (type, newBlock); } diff --git a/src/proc/engine/tracking-heap-block-provider.hpp b/src/proc/engine/tracking-heap-block-provider.hpp index a7065252a..157aeeb5c 100644 --- a/src/proc/engine/tracking-heap-block-provider.hpp +++ b/src/proc/engine/tracking-heap-block-provider.hpp @@ -48,6 +48,7 @@ #include "lib/scoped-ptrvect.hpp" #include "lib/access-casted.hpp" +#include <tr1/unordered_map> #include <boost/scoped_ptr.hpp> #include <boost/scoped_array.hpp> @@ -100,8 +101,13 @@ namespace engine { return storage_.get(); } }; + + class BlockPool; + + typedef std::tr1::unordered_map<HashVal,BlockPool> PoolTable; } + /** * simple BufferProvider implementation with additional allocation tracking. * @internal used as PImpl by DiagnosticBufferProvider and DiagnosticOutputSlot. @@ -115,13 +121,14 @@ namespace engine { : public BufferProvider , public lib::ScopedPtrVect<diagn::Block> { + diagn::PoolTable pool_; public: /* === BufferProvider interface === */ using BufferProvider::lockBufferFor; virtual uint announce (uint count, BufferDescriptor const& type); - virtual BuffHandle lockBufferFor (BufferDescriptor const& descriptor); + virtual BuffHandle lockBufferFor (BufferDescriptor const& type); virtual void mark_emitted (BuffHandle const& handle); virtual void releaseBuffer (BuffHandle const& handle); From 6bc94cccb5886bef40a884472c54e37e5f531020 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 14 Nov 2011 01:43:29 +0100 Subject: [PATCH 259/496] WIP refactor BufferProvider public and protected API --- src/proc/engine/buffer-local-key.hpp | 89 +++++++++++++++++ src/proc/engine/buffer-metadata.hpp | 60 ++++-------- src/proc/engine/buffer-provider.cpp | 97 +++++++++++++++++-- src/proc/engine/buffer-provider.hpp | 31 ++++-- src/proc/engine/buffhandle.hpp | 7 +- .../engine/tracking-heap-block-provider.cpp | 26 +++-- .../engine/tracking-heap-block-provider.hpp | 1 + 7 files changed, 244 insertions(+), 67 deletions(-) create mode 100644 src/proc/engine/buffer-local-key.hpp diff --git a/src/proc/engine/buffer-local-key.hpp b/src/proc/engine/buffer-local-key.hpp new file mode 100644 index 000000000..aa620aa68 --- /dev/null +++ b/src/proc/engine/buffer-local-key.hpp @@ -0,0 +1,89 @@ +/* + BUFFER-LOCAL-KEY.hpp - opaque data for BufferProvider implementation + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef PROC_ENGINE_BUFFR_LOCAL_KEY_H +#define PROC_ENGINE_BUFFR_LOCAL_KEY_H + + +#include "lib/error.hpp" + +#include <boost/functional/hash.hpp> + + +namespace lib { + typedef size_t HashVal; +} + +namespace engine { + + namespace metadata { + class Key; + class Entry; + } + class BufferMetadata; + + using lib::HashVal; + + + + /** + * an opaque ID to be used by the BufferProvider implementation. + * Typically this will be used, to set apart some pre-registered + * kinds of buffers. It is treated as being part of the buffer type. + * LocalKey objects may be copied but not re-assigned or changed. + */ + class LocalKey + { + uint64_t privateID_; + + public: + LocalKey (uint64_t opaqueValue=0) + : privateID_(opaqueValue) + { } + + operator uint64_t() const { return privateID_; } + + bool isDefined() const { return bool(privateID_); } + + friend size_t + hash_value (LocalKey const& lkey) + { + boost::hash<uint64_t> hashFunction; + return hashFunction(lkey.privateID_); + } + + private: + /** assignment usually prohibited */ + LocalKey& operator= (LocalKey const& o) + { + privateID_ = o.privateID_; + return *this; + } + + /** but Key assignments are acceptable */ + friend class metadata::Key; + }; + + +} // namespace engine +#endif diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index b06f01035..87ce5e59c 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -59,6 +59,7 @@ #include "lib/util-foreach.hpp" #include "include/logging.h" #include "proc/engine/type-handler.hpp" +#include "proc/engine/buffer-local-key.hpp" #include <tr1/unordered_map> #include <boost/noncopyable.hpp> @@ -96,42 +97,6 @@ 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_; - - public: - LocalKey (uint64_t opaqueValue=0) - : privateID_(opaqueValue) - { } - - operator uint64_t() const { return privateID_; } - - friend size_t - hash_value (LocalKey const& lkey) - { - boost::hash<uint64_t> hashFunction; - return hashFunction(lkey.privateID_); - } - - private: - /** assignment usually prohibited */ - LocalKey& operator= (LocalKey const& o) - { - privateID_ = o.privateID_; - return *this; - } - - /** but Key assignments are acceptable */ - friend class metadata::Key; - }; - @@ -253,14 +218,20 @@ namespace engine { * For NULL buffer a copy of the parent is returned. */ static Key - forEntry (Key const& parent, const void* bufferAddr) + forEntry (Key const& parent, const void* bufferAddr, LocalKey const& implID =UNSPECIFIC) { Key newKey(parent); if (bufferAddr) { newKey.parent_ = HashVal(parent); newKey.hashID_ = chainedHash(parent, bufferAddr); - } + if (nontrivial(implID)) + { + REQUIRE (!newKey.specifics_.isDefined(), + "Implementation defined local key should not be overridden. " + "Underlying buffer type already defines a nontrivial LocalKey"); + newKey.specifics_ = implID; + } } return newKey; } @@ -297,8 +268,8 @@ namespace engine { void* buffer_; protected: - Entry (Key const& parent, void* bufferPtr =0) - : Key (Key::forEntry (parent, bufferPtr)) + Entry (Key const& parent, void* bufferPtr =0, LocalKey const& implID =UNSPECIFIC) + : Key (Key::forEntry (parent, bufferPtr, implID)) , state_(bufferPtr? LOCKED:NIL) , buffer_(bufferPtr) { } @@ -654,7 +625,10 @@ namespace engine { * buffer is released or re-used later. */ Entry& - lock (Key const& parentKey, void* concreteBuffer, bool onlyNew =false) + lock (Key const& parentKey + ,void* concreteBuffer + ,LocalKey const& implID + ,bool onlyNew =false) { if (!concreteBuffer) throw error::Invalid ("Attempt to lock a slot for a NULL buffer" @@ -724,13 +698,13 @@ namespace engine { * created, but is marked as FREE */ Entry& - markLocked (Key const& parentKey, void* buffer) + markLocked (Key const& parentKey, void* buffer, LocalKey const& implID) { if (!buffer) throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?" , error::LUMIERA_ERROR_BOTTOM_VALUE); - return this->lock(parentKey, buffer, true); // force creation of a new entry + return this->lock(parentKey, buffer, implID, true); // force creation of a new entry } /** purge the bare metadata Entry from the metadata tables. diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp index 2e8ace5bd..ddb4ade3a 100644 --- a/src/proc/engine/buffer-provider.cpp +++ b/src/proc/engine/buffer-provider.cpp @@ -35,6 +35,9 @@ namespace engine { const uint DEFAULT_DESCRIPTOR = 0; } + + LUMIERA_ERROR_DEFINE (BUFFER_MANAGEMENT, "Problem providing working buffers"); + BufferProvider::BufferProvider (Literal implementationID) @@ -70,20 +73,95 @@ namespace engine { BuffHandle - BufferProvider::buildHandle (BufferDescriptor const& type, void* storage) + BufferProvider::buildHandle (HashVal typeID, void* storage, LocalKey const& implID) { - REQUIRE (was_created_by_this_provider (type)); - - HashVal typeID (type.subClassification_); metadata::Key& typeKey = meta_->get (typeID); - metadata::Entry& entry = meta_->markLocked(typeKey, newBlock); + metadata::Entry& entry = meta_->markLocked(typeKey, newBlock, implID); return BuffHandle (BufferDescriptor(*this, entry), newBlock); } + /** BufferProvider API: declare in advance the need for working buffers. + * This optional call allows client code to ensure the availability of the + * necessary working space, prior to starting the actual operations. The + * client may reasonably assume to get the actual number of buffers, as + * indicated by the return value. A provider may be able to handle + * various kinds of buffers (e.g. of differing size), which are + * distinguished by the \em type embodied into the BufferDescriptor. + * @return maximum number of simultaneously usable buffers of this type, + * to be retrieved later through calls to #lockBuffer. + * @throw error::State when no buffer of this kind can be provided + * @note the returned count may differ from the requested count. + */ + uint + BufferProvider::announce (uint count, BufferDescriptor const& type) + { + uint actually_possible = prepareBuffers (count, type); + if (!actually_possible) + throw error::State ("unable to fulfil request for buffers" + ,LUMIERA_ERROR_BUFFER_MANAGEMENT); + return actually_possible; + } + + + /** BufferProvider API: retrieve a single buffer for exclusive use. + * This call actually claims a buffer of this type and marks it for + * use by client code. The returned handle allows for convenient access, + * but provides no automatic tracking or memory management. The client is + * explicitly responsible to invoke #releaseBuffer (which can be done directly + * on the BuffHandle). + * @return a copyable handle, representing this buffer and this usage transaction. + * @throw error::State when unable to provide this buffer + * @note this function may be used right away, without prior announcing, but then + * the client should be prepared for exceptions. The #announce operation allows + * to establish a reliably available baseline. + */ + BuffHandle + BufferProvider::lockBuffer (BufferDescriptor const& type) + { + REQUIRE (was_created_by_this_provider (type)); + + return provideLockedBuffer (type); + } // is expected to call buildHandle() --> state transition + + + /** BufferProvider API: state transition to \em emitted state. + * Client code may signal a state transition through this optional operation. + * The actual meaning of an "emitted" buffer is implementation defined; similarly, + * some back-ends may actually do something when emitting a buffer (e.g. commit data + * to cache), while others just set a flag or do nothing at all. This state transition + * may be invoked at most once per locked buffer. + * @throw error::Fatal in case of invalid state transition sequence. Only a locked buffer + * may be emitted, and at most once. + * @warning by convention, emitting a buffer implies that the contained data is ready and + * might be used by other parts of the application. + * An emitted buffer should not be modified anymore. + */ + void + BufferProvider::emitBuffer (BuffHandle const&) + { + + } + + + /** BufferProvider API: declare done and detach. + * Client code is required to release \em each previously locked buffer eventually. + * @warning invalidates the BuffHandle, clients mustn't access the buffer anymore. + * Right after releasing, an access through the handle will throw; + * yet the buffer might be re-used and the handle become valid + * later on accidentally. + * @note EX_FREE + */ + void + BufferProvider::releaseBuffer (BuffHandle const&) + { + + } + + bool - was_created_by_this_provider (BufferDescriptor const& descr) const + BufferProvider::was_created_by_this_provider (BufferDescriptor const& descr) const { return isSameObject (this, descr.provider_); } @@ -101,6 +179,13 @@ namespace engine { } + void + BuffHandle::emit() + { + UNIMPLEMENTED ("forward buffer emit call to buffer provider"); + } + + void BuffHandle::release() { diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 2e7709d6f..a18b6cc30 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -46,6 +46,7 @@ #include "lib/symbol.hpp" #include "proc/engine/buffhandle.hpp" #include "proc/engine/type-handler.hpp" +#include "proc/engine/buffer-local-key.hpp" #include <boost/noncopyable.hpp> #include <boost/scoped_ptr.hpp> @@ -60,6 +61,9 @@ namespace engine { class BufferMetadata; + LUMIERA_ERROR_DECLARE (BUFFER_MANAGEMENT); ///< Problem providing working buffers + + /** * Interface: a facility providing and managing working buffers for media calculations. * The pointer to actual buffer storage can be retrieved by @@ -74,18 +78,27 @@ namespace engine { { scoped_ptr<BufferMetadata> meta_; - protected: + + protected: /* === for Implementation by concrete providers === */ + BufferProvider (Literal implementationID); + virtual uint prepareBuffers (uint count, HashVal typeID) =0; + + virtual BuffHandle provideLockedBuffer (HashVal typeID) =0; + virtual void mark_emitted (HashVal typeID, LocalKey const&) =0; + virtual void detachBuffer (HashVal typeID, LocalKey const&) =0; + + public: - virtual ~BufferProvider(); ///< this is an interface + virtual ~BufferProvider(); ///< this is an ABC - virtual uint announce (uint count, BufferDescriptor const&) =0; + uint announce (uint count, BufferDescriptor const&); - virtual BuffHandle lockBufferFor (BufferDescriptor const&) =0; - virtual void mark_emitted (BuffHandle const&) =0; - virtual void releaseBuffer (BuffHandle const&) =0; + BuffHandle lockBuffer (BufferDescriptor const&); + void emitBuffer (BuffHandle const&); + void releaseBuffer (BuffHandle const&); template<typename BU> BuffHandle lockBufferFor (); @@ -105,7 +118,7 @@ namespace engine { bool verifyValidity (BufferDescriptor const&); protected: - BuffHandle buildHandle (BufferDescriptor const& type, void* storage); + BuffHandle buildHandle (HashVal typeID, void* storage, LocalKey const&); bool was_created_by_this_provider (BufferDescriptor const&) const; }; @@ -126,8 +139,8 @@ namespace engine { BuffHandle BufferProvider::lockBufferFor() { - BufferDescriptor buffer_to_attach_object = getDescriptor<BU>(); - return lockBufferFor (buffer_to_attach_object); + BufferDescriptor attach_object_automatically = getDescriptor<BU>(); + return lockBuffer (attach_object_automatically); } diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index 14845a849..e1bd025ac 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -52,6 +52,8 @@ namespace engine { + typedef size_t HashVal; ////////////TICKET #722 + class BufferProvider; @@ -70,7 +72,7 @@ namespace engine { class BufferDescriptor { BufferProvider* provider_; - uint64_t subClassification_; + HashVal subClassification_; BufferDescriptor(BufferProvider& manager, uint64_t detail) : provider_(&manager) @@ -83,6 +85,8 @@ namespace engine { // using standard copy operations bool verifyValidity() const; + + operator HashVal() const { return subClassification_; } }; @@ -136,6 +140,7 @@ namespace engine { + void emit(); void release(); diff --git a/src/proc/engine/tracking-heap-block-provider.cpp b/src/proc/engine/tracking-heap-block-provider.cpp index 43d6cda23..c40a2a56d 100644 --- a/src/proc/engine/tracking-heap-block-provider.cpp +++ b/src/proc/engine/tracking-heap-block-provider.cpp @@ -35,7 +35,6 @@ using util::for_each; using std::vector; -using std::auto_ptr; using lib::ScopedHolder; @@ -106,15 +105,15 @@ namespace engine { } - auto_ptr<Block> + Block* transferResponsibility (Block* allocatedBlock) { - auto_ptr<Block> extracted; + Block* extracted; PoolVec& vec = *blockList_; PoolVec::iterator pos = find (vec.begin(),vec.end(), allocatedBlock); if (pos != vec.end()) { - extracted.reset (allocatedBlock); + extracted = *pos; vec.erase(pos); } return extracted; @@ -179,24 +178,29 @@ namespace engine { BuffHandle TrackingHeapBlockProvider::lockBufferFor (BufferDescriptor const& type) { - UNIMPLEMENTED ("lock buffer for exclusive use"); diagn::BlockPool& blocks = getBlockPoolFor (type); diagn::Block* newBlock = blocks.createBlock(); - return buildHandle (type, newBlock); + return buildHandle (type, newBlock->accessMemory(), newBlock); } void TrackingHeapBlockProvider::mark_emitted (BuffHandle const& handle) { - UNIMPLEMENTED ("commit a buffer to the protocol section"); + //TODO mark metadata + diagn::Block* block4buffer = locateBlock (handle); + diagn::BlockPool& pool = getBlockPoolFor (handle); + this->manage (pool.transferResponsibility (block4buffer)); } + /** mark a buffer as officially discarded */ void TrackingHeapBlockProvider::releaseBuffer (BuffHandle const& handle) { - UNIMPLEMENTED ("mark a buffer as officially discarded"); + //TODO mark metadata + diagn::Block* block4buffer = locateBlock (handle); + block4buffer->markReleased(); } @@ -222,6 +226,12 @@ namespace engine { return bufferID < this->size(); } + diagn::BlockPool& + TrackingHeapBlockProvider::getBlockPoolFor (BufferDescriptor const& type) + { + UNIMPLEMENTED ("access correct block pool, based on metadata"); + } + diff --git a/src/proc/engine/tracking-heap-block-provider.hpp b/src/proc/engine/tracking-heap-block-provider.hpp index 157aeeb5c..25f89fada 100644 --- a/src/proc/engine/tracking-heap-block-provider.hpp +++ b/src/proc/engine/tracking-heap-block-provider.hpp @@ -143,6 +143,7 @@ namespace engine { private: bool withinStorageSize (uint bufferID) const; + diagn::BlockPool& getBlockPoolFor (BufferDescriptor const&); }; From d1d0f66fa8f1916154c72c224fef232820149ae2 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 15 Nov 2011 04:46:00 +0100 Subject: [PATCH 260/496] allow LocalKey to carry arbitrary pointers or hashes as payload ...used to piggyback opaque implementation data, deliberately no typecheck here --- src/proc/engine/buffer-local-key.hpp | 51 ++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/src/proc/engine/buffer-local-key.hpp b/src/proc/engine/buffer-local-key.hpp index aa620aa68..15bfe07ef 100644 --- a/src/proc/engine/buffer-local-key.hpp +++ b/src/proc/engine/buffer-local-key.hpp @@ -54,24 +54,63 @@ namespace engine { */ class LocalKey { - uint64_t privateID_; + union OpaqueData + { + uint64_t _as_number; + void* _as_pointer; + }; + + OpaqueData privateID_; public: + explicit LocalKey (uint64_t opaqueValue=0) - : privateID_(opaqueValue) - { } + { + privateID_._as_number = opaqueValue; + } - operator uint64_t() const { return privateID_; } + LocalKey (void* impl_related_ptr) + { + privateID_._as_number = 0; + privateID_._as_pointer = impl_related_ptr; + } - bool isDefined() const { return bool(privateID_); } + + operator uint64_t() const + { + return privateID_._as_number; + } + + operator void*() const + { + return privateID_._as_pointer; + } + + bool + isDefined() const + { + return bool(privateID_._as_number); + } friend size_t hash_value (LocalKey const& lkey) { boost::hash<uint64_t> hashFunction; - return hashFunction(lkey.privateID_); + return hashFunction(lkey.privateID_._as_number); } + friend bool + operator== (LocalKey const& left, LocalKey const& right) + { + return uint64_t(left) == uint64_t(right); + } + friend bool + operator!= (LocalKey const& left, LocalKey const& right) + { + return uint64_t(left) != uint64_t(right); + } + + private: /** assignment usually prohibited */ LocalKey& operator= (LocalKey const& o) From 623bb401af168d3c16d3f6fc51d0635889cd2a27 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 15 Nov 2011 04:47:31 +0100 Subject: [PATCH 261/496] clarify implementation extension points of BufferProvider decide especially how to pass on the implementation defined data like e.g. pointers to storage extents --- src/proc/engine/buffer-metadata.hpp | 10 ++--- src/proc/engine/buffer-provider.cpp | 20 ++++++--- src/proc/engine/buffhandle.hpp | 14 ++++-- .../engine/tracking-heap-block-provider.cpp | 43 ++++++++++++------- .../engine/tracking-heap-block-provider.hpp | 21 ++++++--- .../tracking-heap-block-provider-test.cpp | 26 +++++------ .../proc/play/diagnostic-output-slot.hpp | 6 +-- 7 files changed, 87 insertions(+), 53 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 87ce5e59c..47da8060b 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -593,13 +593,13 @@ namespace engine { * @note might create/register a new Entry as a side-effect */ Key const& - key (Key const& parentKey, void* concreteBuffer) + key (Key const& parentKey, void* concreteBuffer, LocalKey const& implID =UNSPECIFIC) { Key derivedKey = Key::forEntry (parentKey, concreteBuffer); Entry* existing = table_.fetch (derivedKey); return existing? *existing - : markLocked (parentKey,concreteBuffer); + : markLocked (parentKey,concreteBuffer,implID); } /** core operation to access or create a concrete buffer metadata entry. @@ -627,14 +627,14 @@ namespace engine { Entry& lock (Key const& parentKey ,void* concreteBuffer - ,LocalKey const& implID + ,LocalKey const& implID =UNSPECIFIC ,bool onlyNew =false) { if (!concreteBuffer) throw error::Invalid ("Attempt to lock a slot for a NULL buffer" , error::LUMIERA_ERROR_BOTTOM_VALUE); - Entry newEntry(parentKey, concreteBuffer); + Entry newEntry(parentKey, concreteBuffer, implID); Entry* existing = table_.fetch (newEntry); if (existing && onlyNew) @@ -698,7 +698,7 @@ namespace engine { * created, but is marked as FREE */ Entry& - markLocked (Key const& parentKey, void* buffer, LocalKey const& implID) + markLocked (Key const& parentKey, void* buffer, LocalKey const& implID =UNSPECIFIC) { if (!buffer) throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?" diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp index ddb4ade3a..af2fe02af 100644 --- a/src/proc/engine/buffer-provider.cpp +++ b/src/proc/engine/buffer-provider.cpp @@ -21,6 +21,7 @@ * *****************************************************/ +#include "lib/error.hpp" #include "proc/engine/buffer-provider.hpp" #include "proc/engine/buffer-metadata.hpp" #include "lib/util.hpp" @@ -76,9 +77,9 @@ namespace engine { BufferProvider::buildHandle (HashVal typeID, void* storage, LocalKey const& implID) { metadata::Key& typeKey = meta_->get (typeID); - metadata::Entry& entry = meta_->markLocked(typeKey, newBlock, implID); + metadata::Entry& entry = meta_->markLocked(typeKey, storage, implID); - return BuffHandle (BufferDescriptor(*this, entry), newBlock); + return BuffHandle (BufferDescriptor(*this, entry), storage); } @@ -139,9 +140,11 @@ namespace engine { * An emitted buffer should not be modified anymore. */ void - BufferProvider::emitBuffer (BuffHandle const&) + BufferProvider::emitBuffer (BuffHandle const& handle) { - + metadata::Entry& metaEntry = meta_->get (handle.entryID()); + mark_emitted (metaEntry.parentKey(), metaEntry.localKey()); + metaEntry.mark(EMITTED); } @@ -154,10 +157,13 @@ namespace engine { * @note EX_FREE */ void - BufferProvider::releaseBuffer (BuffHandle const&) - { - + BufferProvider::releaseBuffer (BuffHandle const& handle) + try { + metadata::Entry& metaEntry = meta_->get (handle.entryID()); + detachBuffer (metaEntry.parentKey(), metaEntry.localKey()); + metaEntry.mark(FREE); } + ERROR_LOG_AND_IGNORE (engine, "releasing a buffer from BufferProvider") bool diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index e1bd025ac..a12d26df8 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -74,7 +74,7 @@ namespace engine { BufferProvider* provider_; HashVal subClassification_; - BufferDescriptor(BufferProvider& manager, uint64_t detail) + BufferDescriptor(BufferProvider& manager, HashVal detail) : provider_(&manager) , subClassification_(detail) { } @@ -131,9 +131,9 @@ namespace engine { /** @internal a buffer handle may be obtained by "locking" * a buffer from the corresponding BufferProvider */ - BuffHandle(BufferDescriptor const& typeInfo, PBuff storage = 0) + BuffHandle(BufferDescriptor const& typeInfo, void* storage = 0) : descriptor_(typeInfo) - , pBuffer_(storage) + , pBuffer_(static_cast<PBuff>(storage)) { } // using standard copy operations @@ -151,6 +151,8 @@ namespace engine { BU& accessAs(); + //////////////////////////////////////////TICKET #249 this operator looks obsolete. The Buff type is a placeholder type, + //////////////////////////////////////////TODO it should never be accessed directly from within Lumiera engine code Buff& operator* () const { @@ -165,6 +167,12 @@ namespace engine { && descriptor_.verifyValidity(); } + HashVal + entryID() const + { + return descriptor_; + } + size_t size() const { diff --git a/src/proc/engine/tracking-heap-block-provider.cpp b/src/proc/engine/tracking-heap-block-provider.cpp index c40a2a56d..1ba17d181 100644 --- a/src/proc/engine/tracking-heap-block-provider.cpp +++ b/src/proc/engine/tracking-heap-block-provider.cpp @@ -41,6 +41,8 @@ using lib::ScopedHolder; namespace engine { + namespace error = lumiera::error; + namespace diagn { typedef vector<Block*> PoolVec; @@ -72,7 +74,7 @@ namespace engine { initialise (size_t blockSize) { blockList_.create(); - memBlockSize_(blockSize); + memBlockSize_ = blockSize; } // standard copy operations are valid, but will // raise an runtime error, once BlockPool is initialised. @@ -157,7 +159,7 @@ namespace engine { */ TrackingHeapBlockProvider::TrackingHeapBlockProvider() : BufferProvider ("Diagnostic_HeapAllocated") - , pool_() + , pool_(new diagn::PoolTable) { } TrackingHeapBlockProvider::~TrackingHeapBlockProvider() @@ -169,37 +171,39 @@ namespace engine { /* ==== Implementation of the BufferProvider interface ==== */ uint - TrackingHeapBlockProvider::announce (uint count, BufferDescriptor const& type) + TrackingHeapBlockProvider::prepareBuffers(uint, lib::HashVal) { UNIMPLEMENTED ("pre-register storage for buffers of a specific kind"); } BuffHandle - TrackingHeapBlockProvider::lockBufferFor (BufferDescriptor const& type) + TrackingHeapBlockProvider::provideLockedBuffer(HashVal typeID) { - diagn::BlockPool& blocks = getBlockPoolFor (type); - diagn::Block* newBlock = blocks.createBlock(); - return buildHandle (type, newBlock->accessMemory(), newBlock); + diagn::BlockPool& blocks = getBlockPoolFor (typeID); + diagn::Block& newBlock = blocks.createBlock(); + return buildHandle (typeID, newBlock.accessMemory(), &newBlock); } void - TrackingHeapBlockProvider::mark_emitted (BuffHandle const& handle) + TrackingHeapBlockProvider::mark_emitted (HashVal typeID, LocalKey const& implID) { - //TODO mark metadata - diagn::Block* block4buffer = locateBlock (handle); - diagn::BlockPool& pool = getBlockPoolFor (handle); + diagn::Block* block4buffer = locateBlock (typeID, implID); + if (!block4buffer) + throw error::Logic ("Attempt to emit a buffer not known to this BufferProvider" + , LUMIERA_ERROR_BUFFER_MANAGEMENT); + diagn::BlockPool& pool = getBlockPoolFor (typeID); this->manage (pool.transferResponsibility (block4buffer)); } /** mark a buffer as officially discarded */ void - TrackingHeapBlockProvider::releaseBuffer (BuffHandle const& handle) + TrackingHeapBlockProvider::detachBuffer (HashVal typeID, LocalKey const& implID) { - //TODO mark metadata - diagn::Block* block4buffer = locateBlock (handle); + diagn::Block* block4buffer = locateBlock (typeID, implID); + REQUIRE (block4buffer, "releasing a buffer not allocated through this provider"); block4buffer->markReleased(); } @@ -227,11 +231,20 @@ namespace engine { } diagn::BlockPool& - TrackingHeapBlockProvider::getBlockPoolFor (BufferDescriptor const& type) + TrackingHeapBlockProvider::getBlockPoolFor (HashVal typeID) { UNIMPLEMENTED ("access correct block pool, based on metadata"); } + diagn::Block* + TrackingHeapBlockProvider::locateBlock (HashVal typeID, void* storage) + { + diagn::BlockPool& pool = getBlockPoolFor (typeID); + ////TODO: step 1: try to access from pool + ////TODO: step 2: otherwise search already emitted blocks + UNIMPLEMENTED ("access correct block pool, based on metadata"); + } + diff --git a/src/proc/engine/tracking-heap-block-provider.hpp b/src/proc/engine/tracking-heap-block-provider.hpp index 25f89fada..53fffa17d 100644 --- a/src/proc/engine/tracking-heap-block-provider.hpp +++ b/src/proc/engine/tracking-heap-block-provider.hpp @@ -60,6 +60,7 @@ namespace engine { namespace diagn { + using boost::scoped_ptr; using boost::scoped_array; @@ -100,6 +101,12 @@ namespace engine { { return storage_.get(); } + + void + markReleased() + { + UNIMPLEMENTED ("diagn::Block accounting functionality"); + } }; class BlockPool; @@ -121,16 +128,15 @@ namespace engine { : public BufferProvider , public lib::ScopedPtrVect<diagn::Block> { - diagn::PoolTable pool_; + scoped_ptr<diagn::PoolTable> pool_; public: /* === BufferProvider interface === */ - using BufferProvider::lockBufferFor; - virtual uint announce (uint count, BufferDescriptor const& type); - virtual BuffHandle lockBufferFor (BufferDescriptor const& type); - virtual void mark_emitted (BuffHandle const& handle); - virtual void releaseBuffer (BuffHandle const& handle); + virtual uint prepareBuffers (uint count, HashVal typeID); + virtual BuffHandle provideLockedBuffer (HashVal typeID); + virtual void mark_emitted (HashVal entryID, LocalKey const&); + virtual void detachBuffer (HashVal entryID, LocalKey const&); public: TrackingHeapBlockProvider(); @@ -143,7 +149,8 @@ namespace engine { private: bool withinStorageSize (uint bufferID) const; - diagn::BlockPool& getBlockPoolFor (BufferDescriptor const&); + diagn::BlockPool& getBlockPoolFor (HashVal typeID); + diagn::Block* locateBlock (HashVal typeID, void*); }; diff --git a/tests/components/proc/engine/tracking-heap-block-provider-test.cpp b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp index 47fd1bcd4..cd31351ef 100644 --- a/tests/components/proc/engine/tracking-heap-block-provider-test.cpp +++ b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp @@ -110,7 +110,7 @@ namespace test { uint dataID = 1 + rand() % 29; testBuff.accessAs<TestFrame>() = testData(dataID); - provider.mark_emitted (testBuff); + provider.emitBuffer (testBuff); provider.releaseBuffer(testBuff); diagn::Block& block0 = provider.access_or_create(0); @@ -130,9 +130,9 @@ namespace test { for (uint i=0; i<numElms; ++i) { - BuffHandle buff = provider.lockBufferFor(buffType); + BuffHandle buff = provider.lockBuffer(buffType); buff.accessAs<uint>() = testNumbers[i] = rand() % 100000; - provider.mark_emitted (buff); + provider.emitBuffer (buff); provider.releaseBuffer(buff); } @@ -150,11 +150,11 @@ namespace test { BufferDescriptor buffType = provider.getDescriptorFor(TEST_ELM_SIZE); - BuffHandle bu1 = provider.lockBufferFor(buffType); - BuffHandle bu2 = provider.lockBufferFor(buffType); - BuffHandle bu3 = provider.lockBufferFor(buffType); - BuffHandle bu4 = provider.lockBufferFor(buffType); - BuffHandle bu5 = provider.lockBufferFor(buffType); + BuffHandle bu1 = provider.lockBuffer (buffType); + BuffHandle bu2 = provider.lockBuffer (buffType); + BuffHandle bu3 = provider.lockBuffer (buffType); + BuffHandle bu4 = provider.lockBuffer (buffType); + BuffHandle bu5 = provider.lockBuffer (buffType); CHECK (5 == provider.size()); @@ -176,11 +176,11 @@ namespace test { CHECK (23 == provider.accessAs<uint>(3)); CHECK (24 == provider.accessAs<uint>(4)); - provider.mark_emitted (bu3); - provider.mark_emitted (bu1); - provider.mark_emitted (bu5); - provider.mark_emitted (bu4); - provider.mark_emitted (bu2); + provider.emitBuffer (bu3); + provider.emitBuffer (bu1); + provider.emitBuffer (bu5); + provider.emitBuffer (bu4); + provider.emitBuffer (bu2); CHECK (3 == provider.accessAs<uint>(0)); CHECK (1 == provider.accessAs<uint>(1)); diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index 571c1dad0..1c566d198 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -75,7 +75,7 @@ namespace play { BuffHandle claimBufferFor(FrameID frameNr) { - buffProvider_->lockBufferFor (bufferType_); + return buffProvider_->lockBuffer (bufferType_); } @@ -98,8 +98,8 @@ namespace play { void pushout (BuffHandle const& data4output) { - buffProvider_->mark_emitted (data4output); - buffProvider_->releaseBuffer (data4output); + buffProvider_->emitBuffer (data4output); + buffProvider_->releaseBuffer(data4output); } void From 27449c143875b72055625a6089b568fda404c785 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 18 Nov 2011 01:23:50 +0100 Subject: [PATCH 262/496] implementation details of a test/dummy provider --- src/proc/engine/buffer-metadata.hpp | 7 +-- src/proc/engine/buffer-provider.cpp | 8 +++ src/proc/engine/buffer-provider.hpp | 1 + .../engine/tracking-heap-block-provider.cpp | 60 ++++++++++++++----- .../engine/tracking-heap-block-provider.hpp | 17 +++--- .../tracking-heap-block-provider-test.cpp | 2 +- 6 files changed, 68 insertions(+), 27 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 47da8060b..484450a10 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -236,11 +236,8 @@ namespace engine { } - LocalKey const& - localKey() const - { - return specifics_; - } + LocalKey const& localKey() const { return specifics_;} + size_t storageSize() const { return storageSize_; } HashVal parentKey() const { return parent_;} operator HashVal() const { return hashID_;} diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp index af2fe02af..a32639550 100644 --- a/src/proc/engine/buffer-provider.cpp +++ b/src/proc/engine/buffer-provider.cpp @@ -73,6 +73,14 @@ namespace engine { } + size_t + BufferProvider::getBufferSize (HashVal typeID) const + { + metadata::Key& typeKey = meta_->get (typeID); + return typeKey.storageSize(); + } + + BuffHandle BufferProvider::buildHandle (HashVal typeID, void* storage, LocalKey const& implID) { diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index a18b6cc30..ce67928bb 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -118,6 +118,7 @@ namespace engine { bool verifyValidity (BufferDescriptor const&); protected: + size_t getBufferSize (HashVal typeID) const; BuffHandle buildHandle (HashVal typeID, void* storage, LocalKey const&); bool was_created_by_this_provider (BufferDescriptor const&) const; diff --git a/src/proc/engine/tracking-heap-block-provider.cpp b/src/proc/engine/tracking-heap-block-provider.cpp index 1ba17d181..559a6c2c3 100644 --- a/src/proc/engine/tracking-heap-block-provider.cpp +++ b/src/proc/engine/tracking-heap-block-provider.cpp @@ -24,6 +24,7 @@ #include "lib/error.hpp" #include "include/logging.h" //#include "lib/meta/function.hpp" +#include "lib/bool-checkable.hpp" //#include "lib/scoped-ptrvect.hpp" #include "lib/scoped-holder.hpp" #include "lib/util-foreach.hpp" @@ -31,6 +32,7 @@ #include "proc/engine/tracking-heap-block-provider.hpp" #include <boost/noncopyable.hpp> +#include <algorithm> #include <vector> using util::for_each; @@ -60,6 +62,7 @@ namespace engine { * later investigation and diagnostics. */ class BlockPool + : public lib::BoolCheckable<BlockPool> { size_t memBlockSize_; PoolHolder blockList_; @@ -93,7 +96,7 @@ namespace engine { Block* newBlock(0); try { - newBlock = new Block(); ////////////////////////////TODO pass size as ctor param + newBlock = new Block(memBlockSize_); blockList_->push_back (newBlock); } catch(...) @@ -107,12 +110,19 @@ namespace engine { } + Block* + find (void* bufferStorage) + { + UNIMPLEMENTED ("find a block based on storage location"); + } + + Block* transferResponsibility (Block* allocatedBlock) { Block* extracted; PoolVec& vec = *blockList_; - PoolVec::iterator pos = find (vec.begin(),vec.end(), allocatedBlock); + PoolVec::iterator pos = std::find (vec.begin(),vec.end(), allocatedBlock); if (pos != vec.end()) { extracted = *pos; @@ -128,6 +138,12 @@ namespace engine { return blockList_->size(); } + bool + isValid() const + { + return blockList_; + } + private: static void discardBuffer (Block* block) @@ -160,11 +176,12 @@ namespace engine { TrackingHeapBlockProvider::TrackingHeapBlockProvider() : BufferProvider ("Diagnostic_HeapAllocated") , pool_(new diagn::PoolTable) + , outSeq_() { } TrackingHeapBlockProvider::~TrackingHeapBlockProvider() { - INFO (proc_mem, "discarding %zu diagnostic buffer entries", TrackingHeapBlockProvider::size()); + INFO (proc_mem, "discarding %zu diagnostic buffer entries", outSeq_.size()); } @@ -194,7 +211,7 @@ namespace engine { throw error::Logic ("Attempt to emit a buffer not known to this BufferProvider" , LUMIERA_ERROR_BUFFER_MANAGEMENT); diagn::BlockPool& pool = getBlockPoolFor (typeID); - this->manage (pool.transferResponsibility (block4buffer)); + outSeq_.manage (pool.transferResponsibility (block4buffer)); } @@ -211,38 +228,53 @@ namespace engine { /* ==== Implementation details ==== */ + size_t + TrackingHeapBlockProvider::emittedCnt() const + { + return outSeq_.size(); + } + diagn::Block& TrackingHeapBlockProvider::access_or_create (uint bufferID) { - while (!withinStorageSize (bufferID)) - this->manage (new diagn::Block); + while (!withinOutputSequence (bufferID)) + outSeq_.manage (new diagn::Block(0)); /////////TICKET #856 really need a better way of returning a fallback - ENSURE (withinStorageSize (bufferID)); - return (*this)[bufferID]; + ENSURE (withinOutputSequence (bufferID)); + return outSeq_[bufferID]; } bool - TrackingHeapBlockProvider::withinStorageSize (uint bufferID) const + TrackingHeapBlockProvider::withinOutputSequence (uint bufferID) const { if (bufferID >= MAX_BUFFERS) throw error::Fatal ("hardwired internal limit for test buffers exceeded"); - return bufferID < this->size(); + return bufferID < outSeq_.size(); } diagn::BlockPool& TrackingHeapBlockProvider::getBlockPoolFor (HashVal typeID) { - UNIMPLEMENTED ("access correct block pool, based on metadata"); + diagn::BlockPool& pool = (*pool_)[typeID]; + if (!pool) + pool.initialise(getBufferSize(typeID)); + return pool; } diagn::Block* TrackingHeapBlockProvider::locateBlock (HashVal typeID, void* storage) { diagn::BlockPool& pool = getBlockPoolFor (typeID); - ////TODO: step 1: try to access from pool - ////TODO: step 2: otherwise search already emitted blocks - UNIMPLEMENTED ("access correct block pool, based on metadata"); + diagn::Block* block4buffer = pool.find (storage); + return block4buffer? block4buffer + : searchInOutSeqeuence (storage); + } + + diagn::Block* + TrackingHeapBlockProvider::searchInOutSeqeuence (void* storage) + { + UNIMPLEMENTED ("find block by storage location"); } diff --git a/src/proc/engine/tracking-heap-block-provider.hpp b/src/proc/engine/tracking-heap-block-provider.hpp index 53fffa17d..bdda28c4a 100644 --- a/src/proc/engine/tracking-heap-block-provider.hpp +++ b/src/proc/engine/tracking-heap-block-provider.hpp @@ -57,6 +57,7 @@ namespace engine { namespace error = lumiera::error; + using lib::ScopedPtrVect; namespace diagn { @@ -72,15 +73,14 @@ namespace engine { class Block : boost::noncopyable { - size_t size_; scoped_array<char> storage_; bool was_locked_; public: - Block() - : size_(0) - , storage_() + explicit + Block(size_t bufferSize) + : storage_(new char[bufferSize]) , was_locked_(false) { } @@ -126,9 +126,9 @@ namespace engine { */ class TrackingHeapBlockProvider : public BufferProvider - , public lib::ScopedPtrVect<diagn::Block> { scoped_ptr<diagn::PoolTable> pool_; + ScopedPtrVect<diagn::Block> outSeq_; public: /* === BufferProvider interface === */ @@ -142,15 +142,18 @@ namespace engine { TrackingHeapBlockProvider(); virtual ~TrackingHeapBlockProvider(); + size_t emittedCnt() const; + diagn::Block& access_or_create (uint bufferID); template<typename TY> TY& accessAs (uint bufferID); private: - bool withinStorageSize (uint bufferID) const; + bool withinOutputSequence (uint bufferID) const; diagn::BlockPool& getBlockPoolFor (HashVal typeID); diagn::Block* locateBlock (HashVal typeID, void*); + diagn::Block* searchInOutSeqeuence (void* storage); }; @@ -167,7 +170,7 @@ namespace engine { TY& TrackingHeapBlockProvider::accessAs (uint bufferID) { - if (!withinStorageSize (bufferID)) + if (!withinOutputSequence (bufferID)) throw error::Invalid ("Buffer with the given ID not yet emitted"); diagn::Block& memoryBlock = access_or_create (bufferID); diff --git a/tests/components/proc/engine/tracking-heap-block-provider-test.cpp b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp index cd31351ef..405c4ceb9 100644 --- a/tests/components/proc/engine/tracking-heap-block-provider-test.cpp +++ b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp @@ -156,7 +156,7 @@ namespace test { BuffHandle bu4 = provider.lockBuffer (buffType); BuffHandle bu5 = provider.lockBuffer (buffType); - CHECK (5 == provider.size()); + CHECK (5 == provider.emittedCnt()); provider.accessAs<uint>(0) = 20; provider.accessAs<uint>(1) = 21; From ca689ae5e056292390d2f463f932ec7677adaeec Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 18 Nov 2011 01:24:43 +0100 Subject: [PATCH 263/496] WIP some musing about building an option monad --- src/lib/maybe.hpp | 122 +++++++++++++++++++++++ tests/lib/maybe-value-test.cpp | 176 +++++++++++++++++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 src/lib/maybe.hpp create mode 100644 tests/lib/maybe-value-test.cpp diff --git a/src/lib/maybe.hpp b/src/lib/maybe.hpp new file mode 100644 index 000000000..085160a92 --- /dev/null +++ b/src/lib/maybe.hpp @@ -0,0 +1,122 @@ +/* + MAYBE.hpp - dealing with optional values + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file maybe.hpp + ** Support for representation of optional values. + ** This implements a concept ("option monad") known from functional programming, + ** allowing to express the fact of some value possibly be unavailable. Using this + ** approach allows to avoid the dangerous technique of (ab)using NULL pointers to + ** represent missing values. + ** + ** While a NULL pointer carries this special meaning just by convention, marking a + ** parameter or return value as optional states this fact first class, and enforces + ** the necessary "is available" check through the type system. Surprisingly, this + ** leads not only to more secure, but also much more compact code, as we're now + ** able to substitute a fallback just by a "or else use this" clause. + ** Basically, there are different ways to access the actual value + ** - access through implicit conversion raises an exception for missing values + ** - evaluation as boolean allows to check, if the value is available + ** - an alternative or fallback value may be attached. + ** + ** @todo WIP and rather brainstorming as of 2/10 + ** + ** @see backend::ThreadJob usage example + */ + + + +#ifndef LIB_MAYBE_H +#define LIB_MAYBE_H + +//#include "pre.hpp" +#include "lib/error.hpp" +//#include "lib/wrapper.hpp" +#include "lib/util.hpp" + +#include <string> + + + +namespace lib { + + using util::isnil; + using std::string; + namespace error = lumiera::error; + + + namespace maybe { + + } + + + /** + * A value, which might be unavailable + * @throw error::State on any attempt to access a missing value + * without prior checking the availability + */ + template<typename VAL> + class Maybe + { + VAL value_; + + public: + /** mark an invalid/failed result */ + Maybe () + { } + + /** standard case: valid result */ + Maybe (VAL const& value) + : value_(value) + { } + + + + bool + isValid() const + { + UNIMPLEMENTED ("check if optional value is available"); + } + + void + maybeThrow(Literal explanation =0) const + { + if (!isValid()) + throw error::State (explanation.empty()? "optional value not available" : string(explanation), + error::LUMIERA_ERROR_BOTTOM_VALUE); + } + + + VAL + get() const + { + maybeThrow(); + return value_; + } + }; + + + + + +} // namespace lib +#endif diff --git a/tests/lib/maybe-value-test.cpp b/tests/lib/maybe-value-test.cpp new file mode 100644 index 000000000..28e778269 --- /dev/null +++ b/tests/lib/maybe-value-test.cpp @@ -0,0 +1,176 @@ +/* + MaybeValue(Test) - considerations for dealing with optional values + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/maybe.hpp" +#include "lib/util.hpp" + +//#include <tr1/functional> +//#include <cstdlib> + + +namespace lib { +namespace test{ + + namespace error = lumiera::error; + +// using util::isSameObject; +// using std::rand; + using util::isnil; + using error::LUMIERA_ERROR_BOTTOM_VALUE; + + + namespace { // test data and helpers... + + + uint INVOCATION_CNT(0); + + /** helper for testing delayed evaluation */ + template<typename VAL> + class Delayed + { + VAL v_; + + public: + Delayed (VAL val) : v_(val) { } + + VAL + operator() () const + { + ++INVOCATION_CNT; + return v_; + } + }; + + template<typename VAL> + inline Delayed<VAL> + yield (VAL val) + { + + } + } + + + + /*************************************************************************************** + * @test Investigate various situations of using a Maybe value or option monad. + * @note this is a testbed for experiments for the time being 11/2011 + * + * @see lib::Maybe + * @see null-value-test.cpp + * @see util::AccessCasted + */ + class MaybeValue_test : public Test + { + + void + run (Arg) + { + show_basicOperations(); + show_delayedAccess(); + } + + + void + show_basicOperations() + { +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #856 + Maybe<int> one(1); + Maybe<int> opt(5); + Maybe<int> nil; + + CHECK (opt); CHECK (!isnil(opt)); + CHECK (!nil); CHECK ( isnil(nil)); + + // access the optional value + CHECK (1 == *one); + CHECK (5 == *opt); + + // can't access an bottom value + VERIFY_ERROR (BOTTOM_VALUE, *nil); + + // flatMap operation (apply a function) + CHECK (7 == *(opt >>= inc2)); + CHECK (9 == *(opt >>= inc2 >>= inc2)); + + // alternatives + CHECK (1 == *(one || opt)); + CHECK (5 == *(nil || opt)); + CHECK (1 == *(nil || one || opt)); + + CHECK (1 == one.get()); + CHECK (1 == one.getOrElse(9)); + CHECK (9 == nil.getOrElse(9)); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #856 + } + + + void + show_delayedAccess() + { + INVOCATION_CNT = 0; + + Maybe<int> nil; + Maybe<int> two(2); +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #856 + Maybe<int(void)> later(yield(5)); + CHECK (0 == INVOCATION_CNT); + + CHECK (2 == *(two || later)); + CHECK (0 == INVOCATION_CNT); + + CHECK (5 == *(nil || later)); + CHECK (1 == INVOCATION_CNT); + + later.get(); + CHECK (2 == INVOCATION_CNT); + + CHECK (2 == two.getOrElse(later)); + CHECK (2 == INVOCATION_CNT); + + CHECK (5 == nil.getOrElse(later)); + CHECK (3 == INVOCATION_CNT); + + // obviously, this also works just with a function + CHECK (7 == nil.getOrElse(yield(7))); + CHECK (4 == INVOCATION_CNT); + + // stripping the delayed evaluation + Maybe<int> some = later; + CHECK (5 == INVOCATION_CNT); + CHECK (5 == some); + + CHECK (5 == INVOCATION_CNT); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #856 + } + }; + + + /** Register this test class... */ + LAUNCHER (MaybeValue_test, "unit common"); + + +}} // namespace lib::test From 1f966a02ecd612788a439a63bb658eb330891458 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 19 Nov 2011 17:46:24 +0100 Subject: [PATCH 264/496] some rather tricky adjustments to PtrDerefIter ...allowing to use that custom iterator together with STL algorithms --- src/lib/iter-adapter.hpp | 32 ++++++++++++++++++++++++++++++-- src/lib/scoped-ptrvect.hpp | 12 ++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index a8306a774..b573ec7e7 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -408,7 +408,7 @@ namespace lib { typedef typename RemovePtr<TY>::Type ValueType; template<class T2> - struct SimilarIter + struct SimilarIter ///< rebind to a similarly structured Iterator with value type T2 { typedef Iter<T2,CON> Type; }; @@ -445,7 +445,12 @@ namespace lib { public: typedef typename IT::value_type pointer; typedef typename RemovePtr<pointer>::Type value_type; - typedef value_type& reference; + typedef value_type& reference; + + // for use with STL algorithms + typedef void difference_type; + typedef std::forward_iterator_tag iterator_category; + // the purpose of the following typedefs is to ease building a correct "const iterator" @@ -462,6 +467,7 @@ namespace lib { /** PtrDerefIter is always created * by wrapping an existing iterator. */ + explicit PtrDerefIter (IT srcIter) : i_(srcIter) { } @@ -485,9 +491,31 @@ namespace lib { operator= (PtrDerefIter<WrappedIterType> const& ref) { i_ = reinterpret_cast<IT const&> (ref.getBase()); + return *this; } + /** explicit builder to allow creating a const variant from the basic srcIter type. + * Again, the reason necessitating this "backdoor" is that we want to swallow one level + * of indirection. Generally speaking \code const T ** \endcode is not the same as + * \code T * const * \endcode, but in our specific case the API ensures that a + * PtrDerefIter<WrappedConstIterType> only exposes const elements. + */ + static PtrDerefIter + build_by_cast (WrappedIterType const& srcIter) + { + return PtrDerefIter (reinterpret_cast<IT const&> (srcIter)); + } + + static PtrDerefIter + nil() + { + return PtrDerefIter (IT()); + } + + + + /* === lumiera forward iterator concept === */ diff --git a/src/lib/scoped-ptrvect.hpp b/src/lib/scoped-ptrvect.hpp index 8bb713082..389456ae1 100644 --- a/src/lib/scoped-ptrvect.hpp +++ b/src/lib/scoped-ptrvect.hpp @@ -48,8 +48,10 @@ #include "include/logging.h" #include "lib/iter-adapter.hpp" #include "lib/error.hpp" +#include "lib/util.hpp" #include <vector> +#include <algorithm> #include <boost/noncopyable.hpp> @@ -155,9 +157,9 @@ namespace lib { typedef ConstIterType const_iterator; iterator begin() { return iterator (allPtrs()); } - const_iterator begin() const { return const_iterator (allPtrs()); } + const_iterator begin() const { return const_iterator::build_by_cast (allPtrs()); } iterator end() { return iterator ( RIter() ); } - const_iterator end() const { return const_iterator (RcIter() ); } + const_iterator end() const { return const_iterator::nil(); } @@ -188,6 +190,12 @@ namespace lib { { return RIter (_Vec::begin(), _Vec::end()); } + RIter + allPtrs () const + { + _Vec& elements = util::unConst(*this); + return RIter (elements.begin(), elements.end()); + } }; From 5947e0e3e150e8efde908673d98ec00b689fce43 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 19 Nov 2011 17:48:04 +0100 Subject: [PATCH 265/496] allow to detach an object managed by ScopedPtrVect --- src/lib/scoped-ptrvect.hpp | 42 ++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/lib/scoped-ptrvect.hpp b/src/lib/scoped-ptrvect.hpp index 389456ae1..8ed96b0db 100644 --- a/src/lib/scoped-ptrvect.hpp +++ b/src/lib/scoped-ptrvect.hpp @@ -125,22 +125,42 @@ namespace lib { } } + /** withdraw responsibility for a specific object. + * This object will be removed form this collection + * and returned as-is; it won't be deleted when the + * ScopedPtrVect goes out of scope. + * @param obj address of the object in question. + * @return pointer to the object, if found. + * Otherwise, NULL will be returned and the + * collection of managed objects remains unaltered + * @note EX_STRONG + */ + T* + detach (void* objAddress) + { + T* extracted = static_cast<T*> (objAddress); + VIter pos = std::find (_Vec::begin(),_Vec::end(), extracted); + if (pos != _Vec::end() && bool(*pos)) + { + extracted = *pos; + _Vec::erase(pos); // EX_STRONG + return extracted; + } + return NULL; + } + + void clear() { VIter e = _Vec::end(); for (VIter i = _Vec::begin(); i!=e; ++i) - { - if (*i) - try - { - delete *i; - *i = 0; - } - catch(std::exception& ex) - { - WARN (library, "Problem while deallocating ScopedPtrVect: %s", ex.what()); - } } + if (*i) + try { + delete *i; + *i = 0; + } + ERROR_LOG_AND_IGNORE (library, "Clean-up of ScopedPtrVect"); _Vec::clear(); } From 3830ff7ce1f9e9d17bcca4969590a9f13b4d4438 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 19 Nov 2011 17:51:08 +0100 Subject: [PATCH 266/496] refactor the test/dummy provider, based on ScopedPtrVect solely instead of using a vector directy, move the additionally required functinoality in the utility class. This forced me to adjust the IterAdapter, to allow working with STL algorithms --- .../engine/tracking-heap-block-provider.cpp | 119 +++++++++++------- 1 file changed, 74 insertions(+), 45 deletions(-) diff --git a/src/proc/engine/tracking-heap-block-provider.cpp b/src/proc/engine/tracking-heap-block-provider.cpp index 559a6c2c3..88769ff1a 100644 --- a/src/proc/engine/tracking-heap-block-provider.cpp +++ b/src/proc/engine/tracking-heap-block-provider.cpp @@ -25,19 +25,25 @@ #include "include/logging.h" //#include "lib/meta/function.hpp" #include "lib/bool-checkable.hpp" -//#include "lib/scoped-ptrvect.hpp" +#include "lib/scoped-ptrvect.hpp" #include "lib/scoped-holder.hpp" #include "lib/util-foreach.hpp" #include "proc/engine/tracking-heap-block-provider.hpp" #include <boost/noncopyable.hpp> +#include <tr1/functional> #include <algorithm> #include <vector> -using util::for_each; +//using util::for_each; +using util::and_all; using std::vector; using lib::ScopedHolder; +using lib::ScopedPtrVect; +using std::tr1::function; +using std::tr1::bind; +using std::tr1::placeholders::_1; @@ -45,9 +51,46 @@ namespace engine { namespace error = lumiera::error; + + namespace { // implementation helpers... + + using diagn::Block; + + /** helper to find Block entries + * based on the storage used by the buffer, + * which is maintained by this Block entry */ + inline bool + identifyBlock (Block const& inQuestion, void* storage) + { + return inQuestion.accessMemory() == storage; + } + + /** build a searching predicate */ + inline function<bool(Block const&)> + search_for_block_using_this_storage (void* storage) + { + return bind (identifyBlock, _1, storage); + } + + template<class VEC> + inline Block* + pick_Block_by_storage (VEC& vec, void* bufferLocation) + { + typename VEC::iterator pos + = std::find_if (vec.begin(),vec.end() + ,search_for_block_using_this_storage(bufferLocation)); + if (pos!=vec.end()) + return &(*pos); + else + return NULL; + } + } + + + namespace diagn { - typedef vector<Block*> PoolVec; + typedef ScopedPtrVect<Block> PoolVec; typedef ScopedHolder<PoolVec> PoolHolder; /** @@ -83,52 +126,31 @@ namespace engine { // raise an runtime error, once BlockPool is initialised. ~BlockPool() - try { - if (blockList_) - for_each (*blockList_, discardBuffer); - } - ERROR_LOG_AND_IGNORE (test, "Shutdown of diagnostic BufferProvider allocation pool"); + { + if (!verify_all_children_closed()) + ERROR (test, "Block actively in use while shutting down BufferProvider " + "allocation pool. This might lead to Segfault and memory leaks."); + } + - Block& createBlock() { - Block* newBlock(0); - try - { - newBlock = new Block(memBlockSize_); - blockList_->push_back (newBlock); - } - catch(...) - { - if (newBlock) - delete newBlock; - throw; - } - ENSURE (newBlock); - return *newBlock; + return blockList_->manage (new Block(memBlockSize_)); } Block* - find (void* bufferStorage) + find (void* bufferLocation) { - UNIMPLEMENTED ("find a block based on storage location"); + return pick_Block_by_storage (*blockList_, bufferLocation); } Block* transferResponsibility (Block* allocatedBlock) { - Block* extracted; - PoolVec& vec = *blockList_; - PoolVec::iterator pos = std::find (vec.begin(),vec.end(), allocatedBlock); - if (pos != vec.end()) - { - extracted = *pos; - vec.erase(pos); - } - return extracted; + return blockList_->detach (allocatedBlock); } @@ -145,16 +167,23 @@ namespace engine { } private: - static void - discardBuffer (Block* block) + bool + verify_all_children_closed() { - if (!block) return; - - if (block->was_used() && !block->was_closed()) - ERROR (test, "Block actively in use while shutting down BufferProvider " - "allocation pool. This might lead to Segfault and memory leaks."); - - delete block; + try { + if (blockList_) + return and_all (*blockList_, is_in_sane_state); + } + ERROR_LOG_AND_IGNORE (test, "State verification of diagnostic BufferProvider allocation pool"); + return true; + } + + + static bool + is_in_sane_state (Block const& block) + { + return !block.was_used() + || block.was_closed(); } }; @@ -272,9 +301,9 @@ namespace engine { } diagn::Block* - TrackingHeapBlockProvider::searchInOutSeqeuence (void* storage) + TrackingHeapBlockProvider::searchInOutSeqeuence (void* bufferLocation) { - UNIMPLEMENTED ("find block by storage location"); + return pick_Block_by_storage (outSeq_, bufferLocation); } From 13b795016b4b3809604de933e26cb0ce69689417 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 19 Nov 2011 18:01:48 +0100 Subject: [PATCH 267/496] comment rewording --- src/lib/iter-adapter.hpp | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index b573ec7e7..1b92fd8ac 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -22,13 +22,14 @@ /** @file iter-adapter.hpp ** Helper template(s) for creating <b>lumiera forward iterators</b>. + ** These are the foundation to build up iterator like types from scratch. ** Usually, these templates will be created and provided by a custom ** container type and accessed by the client through a typedef name ** "iterator" (similar to the usage within the STL). For more advanced ** usage, the providing container might want to subclass these iterators, ** e.g. to provide an additional, specialised API. ** - ** Depending on the concrete situation, there are several flavours + ** Depending on the concrete situation, several flavours are provided: ** - the IterAdapter retains an active callback connection to the ** controlling container, thus allowing arbitrary complex behaviour. ** - the RangeIter allows just to expose a range of elements defined @@ -36,21 +37,32 @@ ** - often, objects are managed internally by pointers, while allowing ** the clients to use direct references; to support this usage scenario, ** PtrDerefIter wraps an existing iterator, while dereferencing any value - ** automatically on access. + ** automatically on access. + ** + ** There are many further ways of yielding a Lumiera forward iterator. + ** For example, lib::IterSource builds a "iterable" source of data elements, + ** while hiding the actual container or generator implementation behind a + ** vtable call. Besides, there are adapters for the most common usages + ** with STL containers, and such iterators can also be combined and + ** extended with the help of itertools.hpp + ** + ** Basically every class in compliance with our specific iterator concept + ** can be used as a building block in this framework. ** ** ** \par Lumiera forward iterator concept ** ** Similar to the STL, instead of using a common "Iterator" base class, - ** instead we define a common set of functions and behaviour which can + ** we rather define a common set of functions and behaviour which can ** be expected from any such iterator. These rules are similar to STL's ** "forward iterator", with the addition of an bool check to detect - ** iteration end. The latter s inspired by the \c hasNext() function + ** iteration end. The latter is inspired by the \c hasNext() function ** found in many current languages supporting iterators. In a similar ** vein (inspired from functional programming), we deliberately don't ** support the various extended iterator concepts from STL and boost - ** (random access iterators, output iterators and the like). According - ** to this concept, <i>an iterator is a promise for pulling values,</i> + ** (random access iterators, output iterators, arithmetics, difference + ** between iterators and the like). According to this concept, + ** <i>an iterator is a promise for pulling values,</i> ** and nothing beyond that. ** ** - Any Lumiera forward iterator can be in a "exhausted" (invalid) state, @@ -58,7 +70,7 @@ ** created by the default ctor is always fixed to that state. This ** state is final and can't be reset, meaning that any iterator is ** a disposable one-way-off object. - ** - iterators are copyable and comparable + ** - iterators are copyable and equality comparable ** - when an iterator is \em not in the exhausted state, it may be ** \em dereferenced to yield the "current" value. ** - moreover, iterators may be incremented until exhaustion. From 0a1256f4e31e1881245f02a0ab26021cd753505c Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 19 Nov 2011 21:41:50 +0100 Subject: [PATCH 268/496] cover and verify detaching from ScopedPtrVect --- src/lib/scoped-ptrvect.hpp | 3 +- tests/lib/scoped-ptrvect-test.cpp | 46 +++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/lib/scoped-ptrvect.hpp b/src/lib/scoped-ptrvect.hpp index 8ed96b0db..dd04492a6 100644 --- a/src/lib/scoped-ptrvect.hpp +++ b/src/lib/scoped-ptrvect.hpp @@ -134,6 +134,7 @@ namespace lib { * Otherwise, NULL will be returned and the * collection of managed objects remains unaltered * @note EX_STRONG + * @todo TICKET #856 better return a Maybe<T&> instead of a pointer? */ T* detach (void* objAddress) @@ -177,8 +178,8 @@ namespace lib { typedef ConstIterType const_iterator; iterator begin() { return iterator (allPtrs()); } - const_iterator begin() const { return const_iterator::build_by_cast (allPtrs()); } iterator end() { return iterator ( RIter() ); } + const_iterator begin() const { return const_iterator::build_by_cast (allPtrs()); } const_iterator end() const { return const_iterator::nil(); } diff --git a/tests/lib/scoped-ptrvect-test.cpp b/tests/lib/scoped-ptrvect-test.cpp index 48461e56b..63772334b 100644 --- a/tests/lib/scoped-ptrvect-test.cpp +++ b/tests/lib/scoped-ptrvect-test.cpp @@ -43,7 +43,9 @@ namespace test{ /******************************************************************** * @test ScopedPtrVect manages the lifecycle of a number of objects. - * @todo implement detaching of objects + * The API is similar to a vector and allows for element access + * and iteration. Individual elements can be detached and thus + * removed from the responsibility of the container. */ class ScopedPtrVect_test : public Test { @@ -53,7 +55,7 @@ namespace test{ { simpleUsage(); iterating(); -// detaching(); + detaching(); } @@ -91,7 +93,7 @@ namespace test{ holder.manage (new Dummy); holder.manage (new Dummy); CHECK (9 == holder.size()); - CHECK (0!=checksum); + CHECK (0 < checksum); } CHECK (0==checksum); } @@ -143,8 +145,46 @@ namespace test{ CHECK (0==checksum); } + + void + detaching() + { + int id2, id3; + Dummy* extracted(0); + CHECK (0==checksum); + { + VectD holder; + CHECK (0 == checksum); + CHECK (isnil (holder)); + + holder.manage (new Dummy); + holder.manage (new Dummy); + holder.manage (new Dummy); + holder.manage (new Dummy); + holder.manage (new Dummy); + CHECK (5 == holder.size()); + CHECK (0 < checksum); + + id2 = holder[2].getVal(); + id3 = holder[3].getVal(); + + extracted = holder.detach(& holder[2]); + CHECK (id2 == extracted->getVal()); + CHECK (id3 == holder[2].getVal()); + CHECK (4 == holder.size()); + } + CHECK (0 < checksum); // not all dummies are dead + CHECK (id2 == checksum); // #2 is alive! + + extracted->setVal(id2+id3); + CHECK (id2+id3 == checksum); + + delete extracted; + CHECK (0 == checksum); + } }; + LAUNCHER (ScopedPtrVect_test, "unit common"); From 91b74ad7bdbcb73da66cd9850cd8b48c7df897b9 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 20 Nov 2011 01:35:52 +0100 Subject: [PATCH 269/496] implement missing parts of test/dummy buffer provider --- src/proc/engine/buffer-provider.cpp | 32 +++++++++++++------ src/proc/engine/buffer-provider.hpp | 5 ++- src/proc/engine/buffhandle.hpp | 18 +++++++++-- .../engine/tracking-heap-block-provider.cpp | 27 +++++++++++++--- wiki/renderengine.html | 4 +-- 5 files changed, 66 insertions(+), 20 deletions(-) diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp index a32639550..b4eb794ca 100644 --- a/src/proc/engine/buffer-provider.cpp +++ b/src/proc/engine/buffer-provider.cpp @@ -39,8 +39,12 @@ namespace engine { LUMIERA_ERROR_DEFINE (BUFFER_MANAGEMENT, "Problem providing working buffers"); - + /** build a new provider instance, managing a family of buffers. + * The metadata of these buffers is organised hierarchically based on + * chained hash values, using the #implementationID as a seed. + * @param implementationID symbolic ID setting these family of buffers apart. + */ BufferProvider::BufferProvider (Literal implementationID) : meta_(new BufferMetadata (implementationID)) { } @@ -53,9 +57,9 @@ namespace engine { * currently locked and usable by client code */ bool - BufferProvider::verifyValidity (BufferDescriptor const&) + BufferProvider::verifyValidity (BufferDescriptor const& bufferID) { - UNIMPLEMENTED ("BufferProvider basic and default implementation"); + return meta_->isLocked (bufferID); } @@ -81,6 +85,12 @@ namespace engine { } + /** callback from implementation to build and enrol a BufferHandle, + * to be returned to the client as result of the #lockBuffer call. + * Performs the necessary metadata state transition leading from an + * abstract buffer type to a metadata::Entry corresponding to an + * actual buffer, which is locked for exclusive use by one client. + */ BuffHandle BufferProvider::buildHandle (HashVal typeID, void* storage, LocalKey const& implID) { @@ -177,7 +187,7 @@ namespace engine { bool BufferProvider::was_created_by_this_provider (BufferDescriptor const& descr) const { - return isSameObject (this, descr.provider_); + return isSameObject (*this, *descr.provider_); } @@ -194,18 +204,20 @@ namespace engine { void - BuffHandle::emit() + BufferDescriptor::emit (BuffHandle const& handle) const { - UNIMPLEMENTED ("forward buffer emit call to buffer provider"); + REQUIRE (verifyValidity()); + provider_->emitBuffer(handle); } void - BuffHandle::release() + BufferDescriptor::release (BuffHandle const& handle) const { - UNIMPLEMENTED ("forward buffer release call to buffer provider"); + REQUIRE (verifyValidity()); + provider_->releaseBuffer(handle); } - - + + } // namespace engine diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index ce67928bb..933841423 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -22,7 +22,7 @@ /** @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 + ** It turns out that -- throughout the render engine implementation -- we never need ** direct access to the buffers holding media data. Buffers are just some entity to be \em managed, ** i.e. "allocated", "locked" and "released"; the actual meaning of these operations is an implementation detail. ** The code within the render engine just pushes around BufferHandle objects, which act as a front-end, @@ -71,7 +71,10 @@ namespace engine { * - "locking" a buffer to yield a buffer handle * - dereferencing this smart-handle class * + * @warning all of BufferProvider is assumed to run within a threadsafe environment. + * * @todo as of 6/2011 buffer management within the engine is still a bit vague + * @todo as of 11/11 thread safety within the engine remains to be clarified */ class BufferProvider : boost::noncopyable diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index a12d26df8..a1c64ac17 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -54,6 +54,7 @@ namespace engine { typedef size_t HashVal; ////////////TICKET #722 + class BuffHandle; class BufferProvider; @@ -86,6 +87,9 @@ namespace engine { bool verifyValidity() const; + void emit (BuffHandle const&) const; + void release (BuffHandle const&) const; + operator HashVal() const { return subClassification_; } }; @@ -140,8 +144,18 @@ namespace engine { - void emit(); - void release(); + void + emit() + { + REQUIRE (pBuffer_); + descriptor_.emit (*this); + } + + void release() + { + REQUIRE (pBuffer_); + descriptor_.release (*this); + } template<typename BU> diff --git a/src/proc/engine/tracking-heap-block-provider.cpp b/src/proc/engine/tracking-heap-block-provider.cpp index 88769ff1a..15c1e24d1 100644 --- a/src/proc/engine/tracking-heap-block-provider.cpp +++ b/src/proc/engine/tracking-heap-block-provider.cpp @@ -107,12 +107,14 @@ namespace engine { class BlockPool : public lib::BoolCheckable<BlockPool> { + uint maxAllocCount_; size_t memBlockSize_; PoolHolder blockList_; public: BlockPool() - : memBlockSize_(0) + : maxAllocCount_(0) // unlimited by default + , memBlockSize_(0) , blockList_() { } @@ -127,12 +129,26 @@ namespace engine { ~BlockPool() { - if (!verify_all_children_closed()) + if (!verify_all_children_idle()) ERROR (test, "Block actively in use while shutting down BufferProvider " "allocation pool. This might lead to Segfault and memory leaks."); } + uint + prepare_for (uint number_of_expected_buffers) + { + if (maxAllocCount_ && + maxAllocCount_ < blockList_->size() + number_of_expected_buffers) + { + ASSERT (maxAllocCount_ >= blockList_->size()); + return maxAllocCount_ - blockList_->size(); + } + // currently no hard limit imposed + return number_of_expected_buffers; + } + + Block& createBlock() { @@ -168,7 +184,7 @@ namespace engine { private: bool - verify_all_children_closed() + verify_all_children_idle() { try { if (blockList_) @@ -217,9 +233,10 @@ namespace engine { /* ==== Implementation of the BufferProvider interface ==== */ uint - TrackingHeapBlockProvider::prepareBuffers(uint, lib::HashVal) + TrackingHeapBlockProvider::prepareBuffers(uint requestedAmount, HashVal typeID) { - UNIMPLEMENTED ("pre-register storage for buffers of a specific kind"); + diagn::BlockPool& responsiblePool = getBlockPoolFor (typeID); + return responsiblePool.prepare_for (requestedAmount); } diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 6abd6656c..a1a95a954 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1133,7 +1133,7 @@ Beyond that, it can be necessary to associate at least a state flag with //indiv __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. </pre> </div> -<div title="BufferProvider" modifier="Ichthyostega" modified="201111050003" created="201107082330" tags="Rendering spec draft" changecount="21"> +<div title="BufferProvider" modifier="Ichthyostega" modified="201111192223" created="201107082330" tags="Rendering spec draft" changecount="22"> <pre>It turns out that -- throughout the render engine implementation -- we never need direct access to the buffers holding actual 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 @@ -1142,7 +1142,7 @@ __Note__: while the API to access this service is uniform, conceptually there is !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 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) +: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. Thus the announced amount of buffers always denotes //additional buffers,// on top of what is actively used at the moment. This safety margin of available buffers usually is accounted separately for each distinct kind of buffer (buffer type). There is no tracking as to which specific client requested buffers, beyond the buffer type. ;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 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 From d6de8c8d1a42b6a4a8230dc13d4359a8f1d42169 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 21 Nov 2011 02:28:44 +0100 Subject: [PATCH 270/496] implement some missing nits and bits --- src/proc/engine/buffer-provider.cpp | 7 ++++++ src/proc/engine/buffer-provider.hpp | 2 +- src/proc/engine/buffhandle.hpp | 13 ++++++----- .../engine/tracking-heap-block-provider.hpp | 22 +++++++++---------- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp index b4eb794ca..e7f7f9cb2 100644 --- a/src/proc/engine/buffer-provider.cpp +++ b/src/proc/engine/buffer-provider.cpp @@ -203,6 +203,13 @@ namespace engine { } + size_t + BufferDescriptor::determineBufferSize() const + { + return provider_->getBufferSize (*this); + } + + void BufferDescriptor::emit (BuffHandle const& handle) const { diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 933841423..b52948911 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -119,9 +119,9 @@ namespace engine { /* === API for BuffHandle internal access === */ bool verifyValidity (BufferDescriptor const&); + size_t getBufferSize (HashVal typeID) const; protected: - size_t getBufferSize (HashVal typeID) const; BuffHandle buildHandle (HashVal typeID, void* storage, LocalKey const&); bool was_created_by_this_provider (BufferDescriptor const&) const; diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index a1c64ac17..2e33b54d6 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -86,6 +86,7 @@ namespace engine { // using standard copy operations bool verifyValidity() const; + size_t determineBufferSize() const; void emit (BuffHandle const&) const; void release (BuffHandle const&) const; @@ -190,7 +191,7 @@ namespace engine { size_t size() const { - UNIMPLEMENTED ("forward to the buffer provider for storage size diagnostics"); + return descriptor_.determineBufferSize(); } }; @@ -212,15 +213,17 @@ namespace engine { } - /** convenience shortcut: access the buffer contents in a typesafe fashion. - * This is equivalent to a plain dereferentiation with additional metadata check - * @throw error::Logic in case of type mismatch \c LUMIERA_ERROR_WRONG_TYPE + /** convenience shortcut: access the buffer contents casted to a specific type. + * @warning this is a \em blind cast, there is no type safety. + * @note clients can utilise the metadata::LocalKey to keep track of some + * specific property of the buffer, like e.g. the type of object. */ template<typename BU> BU& BuffHandle::accessAs() { - UNIMPLEMENTED ("convenience shortcut to access buffer contents typesafe"); + REQUIRE (pBuffer_); + return *reinterpret_cast<BU*> (pBuffer_); } diff --git a/src/proc/engine/tracking-heap-block-provider.hpp b/src/proc/engine/tracking-heap-block-provider.hpp index bdda28c4a..70dec474d 100644 --- a/src/proc/engine/tracking-heap-block-provider.hpp +++ b/src/proc/engine/tracking-heap-block-provider.hpp @@ -46,7 +46,6 @@ #include "lib/error.hpp" #include "proc/engine/buffer-provider.hpp" #include "lib/scoped-ptrvect.hpp" -#include "lib/access-casted.hpp" #include <tr1/unordered_map> #include <boost/scoped_ptr.hpp> @@ -75,37 +74,38 @@ namespace engine { { scoped_array<char> storage_; - bool was_locked_; + bool was_released_; public: explicit Block(size_t bufferSize) - : storage_(new char[bufferSize]) - , was_locked_(false) + : storage_(bufferSize? new char[bufferSize] : NULL) + , was_released_(false) { } bool was_used() const { - return was_locked_; + return bool(storage_); } bool was_closed() const { - return was_locked_; + return was_released_; } void* accessMemory() const { + REQUIRE (storage_, "Block was never prepared for use"); return storage_.get(); } void markReleased() { - UNIMPLEMENTED ("diagn::Block accounting functionality"); + was_released_ = true; } }; @@ -174,12 +174,10 @@ namespace engine { throw error::Invalid ("Buffer with the given ID not yet emitted"); diagn::Block& memoryBlock = access_or_create (bufferID); - TY* converted = util::AccessCasted<TY*>::access (memoryBlock.accessMemory()); + TY* converted = reinterpret_cast<TY*> (memoryBlock.accessMemory()); - if (!converted) - throw error::Fatal ("unable to access the target location with the required conversion"); - else - return *converted; + REQUIRE (converted); + return *converted; } From 2ae1e3c8f937d1d1cc5fe51a21f7db2dcfb303dc Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 21 Nov 2011 03:26:08 +0100 Subject: [PATCH 271/496] test/dummy buffer provider finished and passes test --- src/proc/engine/buffhandle.hpp | 9 +- .../engine/diagnostic-buffer-provider.cpp | 6 +- .../engine/tracking-heap-block-provider.cpp | 34 ++++---- .../engine/tracking-heap-block-provider.hpp | 4 +- .../tracking-heap-block-provider-test.cpp | 87 ++++++++++++------- 5 files changed, 82 insertions(+), 58 deletions(-) diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index 2e33b54d6..affbf8dd6 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -154,8 +154,13 @@ namespace engine { void release() { - REQUIRE (pBuffer_); - descriptor_.release (*this); + if (pBuffer_) + { + REQUIRE (isValid()); + descriptor_.release (*this); + pBuffer_ = 0; + } + ENSURE (!isValid()); } diff --git a/src/proc/engine/diagnostic-buffer-provider.cpp b/src/proc/engine/diagnostic-buffer-provider.cpp index d8933733c..f00dcb8a3 100644 --- a/src/proc/engine/diagnostic-buffer-provider.cpp +++ b/src/proc/engine/diagnostic-buffer-provider.cpp @@ -103,21 +103,21 @@ namespace engine { bool DiagnosticBufferProvider::buffer_was_used (uint bufferID) const { - return pImpl_->access_or_create(bufferID).was_used(); + return pImpl_->access_emitted(bufferID).was_used(); } bool DiagnosticBufferProvider::buffer_was_closed (uint bufferID) const { - return pImpl_->access_or_create(bufferID).was_closed(); + return pImpl_->access_emitted(bufferID).was_closed(); } void* DiagnosticBufferProvider::accessMemory (uint bufferID) const { - return pImpl_->access_or_create(bufferID).accessMemory(); + return pImpl_->access_emitted(bufferID).accessMemory(); } diff --git a/src/proc/engine/tracking-heap-block-provider.cpp b/src/proc/engine/tracking-heap-block-provider.cpp index 15c1e24d1..f9e951889 100644 --- a/src/proc/engine/tracking-heap-block-provider.cpp +++ b/src/proc/engine/tracking-heap-block-provider.cpp @@ -32,7 +32,6 @@ #include "proc/engine/tracking-heap-block-provider.hpp" #include <boost/noncopyable.hpp> -#include <tr1/functional> #include <algorithm> #include <vector> @@ -41,9 +40,6 @@ using util::and_all; using std::vector; using lib::ScopedHolder; using lib::ScopedPtrVect; -using std::tr1::function; -using std::tr1::bind; -using std::tr1::placeholders::_1; @@ -57,12 +53,11 @@ namespace engine { using diagn::Block; /** helper to find Block entries - * based on the storage used by the buffer, - * which is maintained by this Block entry */ + * based on their raw memory address */ inline bool identifyBlock (Block const& inQuestion, void* storage) { - return inQuestion.accessMemory() == storage; + return storage == &inQuestion; } /** build a searching predicate */ @@ -74,11 +69,11 @@ namespace engine { template<class VEC> inline Block* - pick_Block_by_storage (VEC& vec, void* bufferLocation) + pick_Block_by_storage (VEC& vec, void* blockLocation) { typename VEC::iterator pos = std::find_if (vec.begin(),vec.end() - ,search_for_block_using_this_storage(bufferLocation)); + ,search_for_block_using_this_storage(blockLocation)); if (pos!=vec.end()) return &(*pos); else @@ -157,9 +152,9 @@ namespace engine { Block* - find (void* bufferLocation) + find (void* blockLocation) { - return pick_Block_by_storage (*blockList_, bufferLocation); + return pick_Block_by_storage (*blockList_, blockLocation); } @@ -210,6 +205,8 @@ namespace engine { namespace { // Details of allocation and accounting const uint MAX_BUFFERS = 50; + + diagn::Block emptyPlaceholder(0); } // (END) Details of allocation and accounting @@ -281,13 +278,12 @@ namespace engine { } diagn::Block& - TrackingHeapBlockProvider::access_or_create (uint bufferID) + TrackingHeapBlockProvider::access_emitted (uint bufferID) { - while (!withinOutputSequence (bufferID)) - outSeq_.manage (new diagn::Block(0)); /////////TICKET #856 really need a better way of returning a fallback - - ENSURE (withinOutputSequence (bufferID)); - return outSeq_[bufferID]; + if (!withinOutputSequence (bufferID)) + return emptyPlaceholder; + else + return outSeq_[bufferID]; } bool @@ -318,9 +314,9 @@ namespace engine { } diagn::Block* - TrackingHeapBlockProvider::searchInOutSeqeuence (void* bufferLocation) + TrackingHeapBlockProvider::searchInOutSeqeuence (void* blockLocation) { - return pick_Block_by_storage (outSeq_, bufferLocation); + return pick_Block_by_storage (outSeq_, blockLocation); } diff --git a/src/proc/engine/tracking-heap-block-provider.hpp b/src/proc/engine/tracking-heap-block-provider.hpp index 70dec474d..a42fb65f8 100644 --- a/src/proc/engine/tracking-heap-block-provider.hpp +++ b/src/proc/engine/tracking-heap-block-provider.hpp @@ -144,7 +144,7 @@ namespace engine { size_t emittedCnt() const; - diagn::Block& access_or_create (uint bufferID); + diagn::Block& access_emitted (uint bufferID); template<typename TY> TY& accessAs (uint bufferID); @@ -173,7 +173,7 @@ namespace engine { if (!withinOutputSequence (bufferID)) throw error::Invalid ("Buffer with the given ID not yet emitted"); - diagn::Block& memoryBlock = access_or_create (bufferID); + diagn::Block& memoryBlock = access_emitted (bufferID); TY* converted = reinterpret_cast<TY*> (memoryBlock.accessMemory()); REQUIRE (converted); diff --git a/tests/components/proc/engine/tracking-heap-block-provider-test.cpp b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp index 405c4ceb9..2d48be480 100644 --- a/tests/components/proc/engine/tracking-heap-block-provider-test.cpp +++ b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp @@ -23,34 +23,19 @@ #include "lib/error.hpp" #include "lib/test/run.hpp" -//#include "lib/test/test-helper.hpp" -//#include "lib/util-foreach.hpp" -//#include "proc/play/diagnostic-output-slot.hpp" #include "proc/engine/tracking-heap-block-provider.hpp" #include "proc/engine/testframe.hpp" -//#include "proc/engine/diagnostic-buffer-provider.hpp" #include "proc/engine/buffhandle.hpp" -//#include "proc/engine/bufftable.hpp" -//#include <boost/format.hpp> -//#include <iostream> -#include <vector> #include <cstdlib> +#include <vector> -//using boost::format; -//using std::string; -//using std::cout; using std::rand; -//using util::for_each; namespace engine{ namespace test { -// using lib::AllocationCluster; -// using mobject::session::PEffect; -// using ::engine::BuffHandle; -// using lumiera::error::LUMIERA_ERROR_LIFECYCLE; namespace { // Test fixture @@ -62,7 +47,7 @@ namespace test { bool - has_expectedContent (uint nr, diagn::Block& memoryBlock) + has_expectedContent (uint nr, diagn::Block& memoryBlock) { void* mem = memoryBlock.accessMemory(); uint data = *static_cast<uint*> (mem); @@ -71,7 +56,7 @@ namespace test { } bool - verifyUsedBlock (uint nr, diagn::Block& memoryBlock) + verifyUsedBlock (uint nr, diagn::Block& memoryBlock) { return memoryBlock.was_used() && memoryBlock.was_closed() @@ -113,7 +98,7 @@ namespace test { provider.emitBuffer (testBuff); provider.releaseBuffer(testBuff); - diagn::Block& block0 = provider.access_or_create(0); + diagn::Block& block0 = provider.access_emitted(0); CHECK (testData(dataID) == block0.accessMemory()); } @@ -138,7 +123,7 @@ namespace test { for (uint nr=0; nr<numElms; ++nr) { - CHECK (verifyUsedBlock (nr, provider.access_or_create(nr))); + CHECK (verifyUsedBlock (nr, provider.access_emitted(nr))); } } @@ -156,37 +141,75 @@ namespace test { BuffHandle bu4 = provider.lockBuffer (buffType); BuffHandle bu5 = provider.lockBuffer (buffType); - CHECK (5 == provider.emittedCnt()); - - provider.accessAs<uint>(0) = 20; - provider.accessAs<uint>(1) = 21; - provider.accessAs<uint>(2) = 22; - provider.accessAs<uint>(3) = 23; - provider.accessAs<uint>(4) = 24; + // buffers are locked, + // but still within the per-type allocation pool + // while the output sequence is still empty + CHECK (!provider.access_emitted(0).was_used()); + CHECK (!provider.access_emitted(1).was_used()); + CHECK (!provider.access_emitted(2).was_used()); + CHECK (!provider.access_emitted(3).was_used()); + CHECK (!provider.access_emitted(4).was_used()); + // can use the buffers for real bu1.accessAs<uint>() = 1; bu2.accessAs<uint>() = 2; bu3.accessAs<uint>() = 3; bu4.accessAs<uint>() = 4; bu5.accessAs<uint>() = 5; - CHECK (20 == provider.accessAs<uint>(0)); - CHECK (21 == provider.accessAs<uint>(1)); - CHECK (22 == provider.accessAs<uint>(2)); - CHECK (23 == provider.accessAs<uint>(3)); - CHECK (24 == provider.accessAs<uint>(4)); + CHECK (0 == provider.emittedCnt()); + // now emit buffers in shuffled order provider.emitBuffer (bu3); provider.emitBuffer (bu1); provider.emitBuffer (bu5); provider.emitBuffer (bu4); provider.emitBuffer (bu2); + CHECK (5 == provider.emittedCnt()); + CHECK (3 == provider.accessAs<uint>(0)); CHECK (1 == provider.accessAs<uint>(1)); CHECK (5 == provider.accessAs<uint>(2)); CHECK (4 == provider.accessAs<uint>(3)); CHECK (2 == provider.accessAs<uint>(4)); + + CHECK ( provider.access_emitted(0).was_used()); + CHECK ( provider.access_emitted(1).was_used()); + CHECK ( provider.access_emitted(2).was_used()); + CHECK ( provider.access_emitted(3).was_used()); + CHECK ( provider.access_emitted(4).was_used()); + + CHECK (!provider.access_emitted(0).was_closed()); + CHECK (!provider.access_emitted(1).was_closed()); + CHECK (!provider.access_emitted(2).was_closed()); + CHECK (!provider.access_emitted(3).was_closed()); + CHECK (!provider.access_emitted(4).was_closed()); + + bu5.release(); + CHECK (!provider.access_emitted(0).was_closed()); + CHECK (!provider.access_emitted(1).was_closed()); + CHECK ( provider.access_emitted(2).was_closed()); + CHECK (!provider.access_emitted(3).was_closed()); + CHECK (!provider.access_emitted(4).was_closed()); + + bu2.release(); + bu2.release(); + bu5.release(); + CHECK (!provider.access_emitted(0).was_closed()); + CHECK (!provider.access_emitted(1).was_closed()); + CHECK ( provider.access_emitted(2).was_closed()); + CHECK (!provider.access_emitted(3).was_closed()); + CHECK ( provider.access_emitted(4).was_closed()); + + CHECK (!bu2); + CHECK (bu3); + + bu1.release(); + bu3.release(); + bu4.release(); + + CHECK (5 == provider.emittedCnt()); } }; From 74702ebecdd06ba7ec081c6588e836812b2ffc0a Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 24 Nov 2011 03:33:23 +0100 Subject: [PATCH 272/496] address the next test... --- src/proc/engine/buffhandle.hpp | 7 ++++++- .../proc/engine/buffer-provider-protocol-test.cpp | 7 +++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index affbf8dd6..eb87ffec2 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -52,6 +52,9 @@ namespace engine { + namespace error = lumiera::error; + using error::LUMIERA_ERROR_LIFECYCLE; + typedef size_t HashVal; ////////////TICKET #722 class BuffHandle; @@ -227,7 +230,9 @@ namespace engine { BU& BuffHandle::accessAs() { - REQUIRE (pBuffer_); + if (!pBuffer_) + throw error::Logic ("buffer not (yet) locked for access by clients" + , LUMIERA_ERROR_LIFECYCLE); return *reinterpret_cast<BU*> (pBuffer_); } diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp index fdfba1e67..6732668b6 100644 --- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp +++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp @@ -76,7 +76,6 @@ namespace test { virtual void run (Arg) { - UNIMPLEMENTED ("build a diagnostic buffer provider and perform a full lifecycle"); verifySimpleUsage(); verifyStandardCase(); } @@ -93,10 +92,10 @@ namespace test { BuffHandle buff = provider.lockBufferFor<TestFrame>(); CHECK (buff.isValid()); CHECK (sizeof(TestFrame) <= buff.size()); - buff.create<TestFrame>() = testData(0); + buff.accessAs<TestFrame>() = testData(0); - TestFrame& storage = buff.accessAs<TestFrame>(); - CHECK (testData(0) == storage); + TestFrame& content = buff.accessAs<TestFrame>(); + CHECK (testData(0) == content); buff.release(); CHECK (!buff.isValid()); From eed6d8cd1e17f6aad8377cc5acd1901423bca730 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 25 Nov 2011 17:16:33 +0100 Subject: [PATCH 273/496] split out obsolete ChannelDescriptor for later refactoring Meanwhile, BuffHanle became way more concrete, making the separation of any kind of buffer or channel type management concievable. Thus: extract the obsolete ChannelDescriptor and use switch at any location where the old (superseeded) buffer handle is still referred --- src/proc/engine/bufftable-obsolete.hpp | 2 +- src/proc/engine/channel-descriptor.hpp | 242 ++++++++++++++++++ src/proc/engine/nodeinvocation.hpp | 2 +- src/proc/engine/nodeoperation.hpp | 2 +- .../proc/engine/node-basic-test.cpp | 2 +- 5 files changed, 246 insertions(+), 4 deletions(-) create mode 100644 src/proc/engine/channel-descriptor.hpp diff --git a/src/proc/engine/bufftable-obsolete.hpp b/src/proc/engine/bufftable-obsolete.hpp index bd79683ee..2b43ee6d1 100644 --- a/src/proc/engine/bufftable-obsolete.hpp +++ b/src/proc/engine/bufftable-obsolete.hpp @@ -26,7 +26,7 @@ #include "lib/error.hpp" -#include "proc/engine/buffhandle.hpp" +#include "proc/engine/channel-descriptor.hpp" #include "proc/engine/procnode.hpp" #include <boost/noncopyable.hpp> diff --git a/src/proc/engine/channel-descriptor.hpp b/src/proc/engine/channel-descriptor.hpp new file mode 100644 index 000000000..1631690dc --- /dev/null +++ b/src/proc/engine/channel-descriptor.hpp @@ -0,0 +1,242 @@ +/* + CHANNEL-DESCRIPTOR.hpp - Channel / Buffer type representation for the engine + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file buffhandle.hpp + ** 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). 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 + ** @see engine::RenderInvocation + */ + +#ifndef ENGINE_CHANNEL_DESCRIPTOR_H +#define ENGINE_CHANNEL_DESCRIPTOR_H + + +#include "lib/error.hpp" +#include "lib/streamtype.hpp" +#include "lib/bool-checkable.hpp" + + +namespace engine { + + namespace error = lumiera::error; + using error::LUMIERA_ERROR_LIFECYCLE; + + typedef size_t HashVal; ////////////TICKET #722 + + class BuffHandle; + class BufferProvider; + + + + /** + * An opaque descriptor to identify the type and further properties of a data buffer. + * For each kind of buffer, there is somewhere a BufferProvider responsible for the + * actual storage management. This provider may "lock" a buffer for actual use, + * returning a BuffHandle. + * @note this descriptor and especially the #subClassification_ is really owned + * by the BufferProvider, which may use (and even change) the opaque contents + * to organise the internal buffer management. + * + * @todo try to move that definition into buffer-provider.hpp ////////////////////////////////////TICKET #249 + */ + class BufferDescriptor + { + BufferProvider* provider_; + HashVal subClassification_; + + BufferDescriptor(BufferProvider& manager, HashVal detail) + : provider_(&manager) + , subClassification_(detail) + { } + + friend class BufferProvider; + + public: + // using standard copy operations + + bool verifyValidity() const; + size_t determineBufferSize() const; + + void emit (BuffHandle const&) const; + void release (BuffHandle const&) const; + + operator HashVal() const { return subClassification_; } + }; + + + + class ProcNode; + typedef ProcNode* PNode; + + + struct ChannelDescriptor ///////TODO really need to define that here? it is needed for node wiring only + { + const lumiera::StreamType * bufferType; /////////////////////////////////////////TICKET #828 + }; + + struct InChanDescriptor : ChannelDescriptor + { + PNode dataSrc; ///< the ProcNode to pull this input from + uint srcChannel; ///< output channel to use on the predecessor node + }; + + + + + + /** + * Handle for a buffer for processing data, abstracting away the actual implementation. + * The real buffer pointer can be retrieved by dereferencing this smart-handle class. + * + * @todo as of 6/2011 it isn't clear how buffer handles are actually created + * and how the lifecycle (and memory) management works //////////////////////TICKET #249 rework BuffHandle creation and usage + */ + class BuffHandle + : public lib::BoolCheckable<BuffHandle> + { + typedef lumiera::StreamType::ImplFacade::DataBuffer Buff; + + BufferDescriptor descriptor_; + Buff* pBuffer_; + + + public: + typedef Buff* PBuff; + + /** @internal a buffer handle may be obtained by "locking" + * a buffer from the corresponding BufferProvider */ + BuffHandle(BufferDescriptor const& typeInfo, void* storage = 0) + : descriptor_(typeInfo) + , pBuffer_(static_cast<PBuff>(storage)) + { } + + // using standard copy operations + + + + void + emit() + { + REQUIRE (pBuffer_); + descriptor_.emit (*this); + } + + void release() + { + if (pBuffer_) + { + REQUIRE (isValid()); + descriptor_.release (*this); + pBuffer_ = 0; + } + ENSURE (!isValid()); + } + + + template<typename BU> + BU& create(); + + template<typename BU> + BU& accessAs(); + + + //////////////////////////////////////////TICKET #249 this operator looks obsolete. The Buff type is a placeholder type, + //////////////////////////////////////////TODO it should never be accessed directly from within Lumiera engine code + Buff& + operator* () const + { + ENSURE (pBuffer_); + return *pBuffer_; + } + + bool + isValid() const + { + return bool(pBuffer_) + && descriptor_.verifyValidity(); + } + + HashVal + entryID() const + { + return descriptor_; + } + + size_t + size() const + { + return descriptor_.determineBufferSize(); + } + + }; + + + /* === Implementation details === */ + + /** convenience shortcut: place and maintain an object within the buffer. + * This operation performs the necessary steps to attach an object; + * if the buffer isn't locked yet, it will do so. Moreover, the created + * object will be owned by the buffer management facilities, i.e. the + * destructor is registered as cleanup function. + */ + template<typename BU> + BU& + BuffHandle::create() + { + UNIMPLEMENTED ("convenience shortcut to attach/place an object in one sway"); + } + + + /** convenience shortcut: access the buffer contents casted to a specific type. + * @warning this is a \em blind cast, there is no type safety. + * @note clients can utilise the metadata::LocalKey to keep track of some + * specific property of the buffer, like e.g. the type of object. + */ + template<typename BU> + BU& + BuffHandle::accessAs() + { + if (!pBuffer_) + throw error::Logic ("buffer not (yet) locked for access by clients" + , LUMIERA_ERROR_LIFECYCLE); + return *reinterpret_cast<BU*> (pBuffer_); + } + + + +} // namespace engine +#endif diff --git a/src/proc/engine/nodeinvocation.hpp b/src/proc/engine/nodeinvocation.hpp index a67696850..7bb92f1e2 100644 --- a/src/proc/engine/nodeinvocation.hpp +++ b/src/proc/engine/nodeinvocation.hpp @@ -55,7 +55,7 @@ #include "proc/state.hpp" #include "proc/engine/procnode.hpp" -#include "proc/engine/buffhandle.hpp" +#include "proc/engine/channel-descriptor.hpp" #include "proc/engine/bufftable-obsolete.hpp" diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp index 947fa645f..34e8b684f 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -59,7 +59,7 @@ #include "proc/state.hpp" #include "proc/engine/procnode.hpp" -#include "proc/engine/buffhandle.hpp" +#include "proc/engine/channel-descriptor.hpp" #include "proc/engine/bufftable-obsolete.hpp" #include "proc/engine/nodeinvocation.hpp" diff --git a/tests/components/proc/engine/node-basic-test.cpp b/tests/components/proc/engine/node-basic-test.cpp index 8e973cc1a..810edf8a7 100644 --- a/tests/components/proc/engine/node-basic-test.cpp +++ b/tests/components/proc/engine/node-basic-test.cpp @@ -26,7 +26,7 @@ #include "proc/engine/nodefactory.hpp" #include "proc/engine/nodewiring.hpp" #include "proc/engine/stateproxy.hpp" -#include "proc/engine/buffhandle.hpp" +#include "proc/engine/channel-descriptor.hpp" #include "proc/mobject/session/effect.hpp" #include "lib/allocation-cluster.hpp" From d6b069fbbd6e225edcd4961f8f14b62945c0ead4 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 25 Nov 2011 17:29:06 +0100 Subject: [PATCH 274/496] clean-up after refactoring --- src/proc/engine/buffhandle.hpp | 38 ++---- src/proc/engine/channel-descriptor.hpp | 181 +------------------------ src/proc/engine/procnode.hpp | 1 + 3 files changed, 21 insertions(+), 199 deletions(-) diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index eb87ffec2..185914fa5 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -21,9 +21,9 @@ */ /** @file buffhandle.hpp - ** Various bits needed to support the buffer management within the render nodes. + ** A front-end 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 + ** needs several input and output buffers. These may be allocated and provided by various ** 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 @@ -31,12 +31,18 @@ ** 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 + ** These buffer handles are based on a buffer descriptor record, which is opaque as far + ** as the client is concerned. BufferDescriptor acts as a representation of the type or + ** kind of buffer. The only way to obtain such a BufferDescriptor is from a concrete + ** BufferProvider implementation. A back-link to this owning and managing provider is + ** embedded into the BufferDescriptor, allowing to retrieve an buffer handle, corresponding + ** to an actual buffer provided and managed behind the scenes. There is no automatic + ** resource management; clients are responsible to invoke BuffHandle#release when done. + ** + ** @see BufferProvider + ** @see BufferProviderProtocol_test usage demonstration + ** @see OutputSlot ** @see bufftable.hpp storage for the buffer table ** @see engine::RenderInvocation */ @@ -99,24 +105,6 @@ namespace engine { - class ProcNode; - typedef ProcNode* PNode; - - - struct ChannelDescriptor ///////TODO really need to define that here? it is needed for node wiring only - { - const lumiera::StreamType * bufferType; /////////////////////////////////////////TICKET #828 - }; - - struct InChanDescriptor : ChannelDescriptor - { - PNode dataSrc; ///< the ProcNode to pull this input from - uint srcChannel; ///< output channel to use on the predecessor node - }; - - - - /** * Handle for a buffer for processing data, abstracting away the actual implementation. diff --git a/src/proc/engine/channel-descriptor.hpp b/src/proc/engine/channel-descriptor.hpp index 1631690dc..0a8e9887a 100644 --- a/src/proc/engine/channel-descriptor.hpp +++ b/src/proc/engine/channel-descriptor.hpp @@ -20,24 +20,15 @@ */ -/** @file buffhandle.hpp - ** 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). 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. +/** @file channel-descriptor.hpp + ** Representation of the Media type of a data channel used within the engine. + ** + ** @todo as it stands (11/2011) this file is obsoleted and needs to be refactored, + ** alongside with adapting the node invocation to the new BufferProvider interface. ** ** @see nodewiring-def.hpp ** @see nodeoperation.hpp - ** @see bufftable.hpp storage for the buffer table + ** @see bufftable-obsolete.hpp storage for the buffer table ** @see engine::RenderInvocation */ @@ -59,43 +50,7 @@ namespace engine { class BuffHandle; class BufferProvider; - - - - /** - * An opaque descriptor to identify the type and further properties of a data buffer. - * For each kind of buffer, there is somewhere a BufferProvider responsible for the - * actual storage management. This provider may "lock" a buffer for actual use, - * returning a BuffHandle. - * @note this descriptor and especially the #subClassification_ is really owned - * by the BufferProvider, which may use (and even change) the opaque contents - * to organise the internal buffer management. - * - * @todo try to move that definition into buffer-provider.hpp ////////////////////////////////////TICKET #249 - */ - class BufferDescriptor - { - BufferProvider* provider_; - HashVal subClassification_; - - BufferDescriptor(BufferProvider& manager, HashVal detail) - : provider_(&manager) - , subClassification_(detail) - { } - - friend class BufferProvider; - - public: - // using standard copy operations - - bool verifyValidity() const; - size_t determineBufferSize() const; - - void emit (BuffHandle const&) const; - void release (BuffHandle const&) const; - - operator HashVal() const { return subClassification_; } - }; + class BufferDescriptor; @@ -116,127 +71,5 @@ namespace engine { - - - /** - * Handle for a buffer for processing data, abstracting away the actual implementation. - * The real buffer pointer can be retrieved by dereferencing this smart-handle class. - * - * @todo as of 6/2011 it isn't clear how buffer handles are actually created - * and how the lifecycle (and memory) management works //////////////////////TICKET #249 rework BuffHandle creation and usage - */ - class BuffHandle - : public lib::BoolCheckable<BuffHandle> - { - typedef lumiera::StreamType::ImplFacade::DataBuffer Buff; - - BufferDescriptor descriptor_; - Buff* pBuffer_; - - - public: - typedef Buff* PBuff; - - /** @internal a buffer handle may be obtained by "locking" - * a buffer from the corresponding BufferProvider */ - BuffHandle(BufferDescriptor const& typeInfo, void* storage = 0) - : descriptor_(typeInfo) - , pBuffer_(static_cast<PBuff>(storage)) - { } - - // using standard copy operations - - - - void - emit() - { - REQUIRE (pBuffer_); - descriptor_.emit (*this); - } - - void release() - { - if (pBuffer_) - { - REQUIRE (isValid()); - descriptor_.release (*this); - pBuffer_ = 0; - } - ENSURE (!isValid()); - } - - - template<typename BU> - BU& create(); - - template<typename BU> - BU& accessAs(); - - - //////////////////////////////////////////TICKET #249 this operator looks obsolete. The Buff type is a placeholder type, - //////////////////////////////////////////TODO it should never be accessed directly from within Lumiera engine code - Buff& - operator* () const - { - ENSURE (pBuffer_); - return *pBuffer_; - } - - bool - isValid() const - { - return bool(pBuffer_) - && descriptor_.verifyValidity(); - } - - HashVal - entryID() const - { - return descriptor_; - } - - size_t - size() const - { - return descriptor_.determineBufferSize(); - } - - }; - - - /* === Implementation details === */ - - /** convenience shortcut: place and maintain an object within the buffer. - * This operation performs the necessary steps to attach an object; - * if the buffer isn't locked yet, it will do so. Moreover, the created - * object will be owned by the buffer management facilities, i.e. the - * destructor is registered as cleanup function. - */ - template<typename BU> - BU& - BuffHandle::create() - { - UNIMPLEMENTED ("convenience shortcut to attach/place an object in one sway"); - } - - - /** convenience shortcut: access the buffer contents casted to a specific type. - * @warning this is a \em blind cast, there is no type safety. - * @note clients can utilise the metadata::LocalKey to keep track of some - * specific property of the buffer, like e.g. the type of object. - */ - template<typename BU> - BU& - BuffHandle::accessAs() - { - if (!pBuffer_) - throw error::Logic ("buffer not (yet) locked for access by clients" - , LUMIERA_ERROR_LIFECYCLE); - return *reinterpret_cast<BU*> (pBuffer_); - } - - - } // namespace engine #endif diff --git a/src/proc/engine/procnode.hpp b/src/proc/engine/procnode.hpp index 757524914..4017140c6 100644 --- a/src/proc/engine/procnode.hpp +++ b/src/proc/engine/procnode.hpp @@ -46,6 +46,7 @@ #include "proc/state.hpp" #include "proc/asset/proc.hpp" #include "proc/mobject/parameter.hpp" +#include "proc/engine/channel-descriptor.hpp" #include "lib/frameid.hpp" #include "lib/ref-array.hpp" From ce25be6fa3b7b099eaa46a119fb21e5cf87294c6 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 25 Nov 2011 18:20:01 +0100 Subject: [PATCH 275/496] refactor relation of BuffHandle and BufferDescriptor they are now tightly coupled and assumed to work together; there is no need to relay half of BuffHandle's interface through BufferDescriptor --- src/proc/engine/buffer-provider.cpp | 17 +++++++++++------ src/proc/engine/buffhandle.hpp | 26 +++++--------------------- src/proc/engine/bufftable-obsolete.hpp | 1 + 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp index e7f7f9cb2..6c33104b0 100644 --- a/src/proc/engine/buffer-provider.cpp +++ b/src/proc/engine/buffer-provider.cpp @@ -211,18 +211,23 @@ namespace engine { void - BufferDescriptor::emit (BuffHandle const& handle) const + BuffHandle::emit() { - REQUIRE (verifyValidity()); - provider_->emitBuffer(handle); + REQUIRE (isValid()); + descriptor_.provider_->emitBuffer(*this); } void - BufferDescriptor::release (BuffHandle const& handle) const + BuffHandle::release() { - REQUIRE (verifyValidity()); - provider_->releaseBuffer(handle); + if (pBuffer_) + { + REQUIRE (isValid()); + descriptor_.provider_->releaseBuffer(*this); + pBuffer_ = 0; + } + ENSURE (!isValid()); } diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index 185914fa5..32a47fc2c 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -81,6 +81,7 @@ namespace engine { */ class BufferDescriptor { + protected: BufferProvider* provider_; HashVal subClassification_; @@ -90,6 +91,7 @@ namespace engine { { } friend class BufferProvider; + friend class BuffHandle; public: // using standard copy operations @@ -97,9 +99,6 @@ namespace engine { bool verifyValidity() const; size_t determineBufferSize() const; - void emit (BuffHandle const&) const; - void release (BuffHandle const&) const; - operator HashVal() const { return subClassification_; } }; @@ -136,23 +135,8 @@ namespace engine { - void - emit() - { - REQUIRE (pBuffer_); - descriptor_.emit (*this); - } - - void release() - { - if (pBuffer_) - { - REQUIRE (isValid()); - descriptor_.release (*this); - pBuffer_ = 0; - } - ENSURE (!isValid()); - } + void emit(); + void release(); template<typename BU> @@ -181,7 +165,7 @@ namespace engine { HashVal entryID() const { - return descriptor_; + return HashVal(descriptor_); } size_t diff --git a/src/proc/engine/bufftable-obsolete.hpp b/src/proc/engine/bufftable-obsolete.hpp index 2b43ee6d1..391808f65 100644 --- a/src/proc/engine/bufftable-obsolete.hpp +++ b/src/proc/engine/bufftable-obsolete.hpp @@ -70,6 +70,7 @@ namespace engine { PBu inBuff; }; + class BufferDescriptor; /** Obsolete, to be rewritten /////TICKET #826 */ class BuffTableStorage From 55a77bdd73e304d79efdcfdcf55127e50ed8c728 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 25 Nov 2011 20:23:31 +0100 Subject: [PATCH 276/496] factor out and treat the attaching of objects separately this is an advanced feature not required in the standard buffer usage cycle --- src/proc/engine/buffer-provider.cpp | 54 +++++++- src/proc/engine/buffer-provider.hpp | 9 +- src/proc/engine/buffhandle-attach.hpp | 128 ++++++++++++++++++ src/proc/engine/buffhandle.hpp | 37 +---- .../engine/buffer-provider-protocol-test.cpp | 2 +- .../tracking-heap-block-provider-test.cpp | 2 +- 6 files changed, 196 insertions(+), 36 deletions(-) create mode 100644 src/proc/engine/buffhandle-attach.hpp diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp index 6c33104b0..449b41370 100644 --- a/src/proc/engine/buffer-provider.cpp +++ b/src/proc/engine/buffer-provider.cpp @@ -57,7 +57,7 @@ namespace engine { * currently locked and usable by client code */ bool - BufferProvider::verifyValidity (BufferDescriptor const& bufferID) + BufferProvider::verifyValidity (BufferDescriptor const& bufferID) const { return meta_->isLocked (bufferID); } @@ -184,6 +184,39 @@ namespace engine { ERROR_LOG_AND_IGNORE (engine, "releasing a buffer from BufferProvider") + + /** @warning this operation locally modifies the metadata entry of a single buffer + * to attach a TypeHandler taking ownership of an object embedded within the buffer. + * The client is responsible for actually placement-constructing the object; moreover + * the client is responsible for any damage done to already existing buffer content. + * @note the buffer must be in locked state and the underlying buffer type must not define + * an non-trivial TypeDescriptor, because there is no clean way of superseding an + * existing TypeDescriptor, which basically is just a functor and possibly + * could perform any operation on buffer clean-up. + * @note EX_STRONG + */ + void + BufferProvider::attachTypeHandler (BuffHandle const& target, BufferDescriptor const& reference) + { + UNIMPLEMENTED ("convenience shortcut to attach/place an object in one sway"); + } + + + /** @internal abort normal lifecycle, reset the underlying buffer and detach from it. + * This allows to break out of normal usage and reset the handle to \em invalid state + * @param invokeDtor if possibly the clean-up function of an TypeHandler registered with + * the buffer metadata should be invoked prior to resetting the metadata state. + * Default is \em not to invoke anything + * @note EX_FREE + */ + void + BufferProvider::emergencyCleanup (BuffHandle const& target, bool invokeDtor) + { + UNIMPLEMENTED ("emergency cleanup"); + } + + + bool BufferProvider::was_created_by_this_provider (BufferDescriptor const& descr) const { @@ -231,5 +264,24 @@ namespace engine { } + void + BuffHandle::emergencyCleanup() + { + descriptor_.provider_->emergencyCleanup(*this); // EX_FREE + pBuffer_ = 0; + } + + + void + BuffHandle::takeOwnershipFor(BufferDescriptor const& type) + { + if (this->size() < type.determineBufferSize()) + throw error::Logic ("insufficient buffer size to hold an instance of that type"); + + descriptor_.provider_->attachTypeHandler(*this, type); // EX_STRONG + } + + + } // namespace engine diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index b52948911..4fff36efe 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -106,6 +106,11 @@ namespace engine { template<typename BU> BuffHandle lockBufferFor (); + /** allow for attaching and owing an object within an already created buffer */ + void attachTypeHandler (BuffHandle const& target, BufferDescriptor const& reference); + + void emergencyCleanup (BuffHandle const& target, bool invokeDtor =false); + /** describe the kind of buffer managed by this provider */ BufferDescriptor getDescriptorFor(size_t storageSize=0); @@ -118,8 +123,8 @@ namespace engine { /* === API for BuffHandle internal access === */ - bool verifyValidity (BufferDescriptor const&); - size_t getBufferSize (HashVal typeID) const; + bool verifyValidity (BufferDescriptor const&) const; + size_t getBufferSize (HashVal typeID) const; protected: BuffHandle buildHandle (HashVal typeID, void* storage, LocalKey const&); diff --git a/src/proc/engine/buffhandle-attach.hpp b/src/proc/engine/buffhandle-attach.hpp new file mode 100644 index 000000000..4f5b94b55 --- /dev/null +++ b/src/proc/engine/buffhandle-attach.hpp @@ -0,0 +1,128 @@ +/* + BUFFHANDLE-ATTACH.hpp - Buffer handle extension to attach objects into the buffer + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file buffhandle-attach.hpp + ** Extension to allow placing objects right into the buffers, taking ownership. + ** This extension is mostly helpful for writing unit-tests, and beyond that for the + ** rather unusual case where we need to place an full-blown object into the buffer, + ** instead of just plain data. A possible use case for this mechanism is to allow for + ** state pre calculation stream, feeding this local state to the individual render node + ** embedded into a "state frame". Some effect processors indeed need to maintain state + ** beyond the single frame (e.g. averaging, integrating, sound compression), which usually + ** is handled by applying an "instance" of that processor to the frames to be calculated + ** in a straight sequence. + ** + ** BuffHandle and the underlying BufferProvider standard implementation support that case + ** by attaching an object managing functor to the metadata. This way, the state can live + ** directly embedded into the frame and still be accessed like an object. To keep the + ** header and compilation footprint low, the implementation of the functions supporting + ** this special case was split out of the basic buffhandle.hpp + ** + ** @see BuffHandle + ** @see BufferProviderProtocol_test usage demonstration + */ + +#ifndef ENGINE_BUFFHANDLE_ATTACH_H +#define ENGINE_BUFFHANDLE_ATTACH_H + + +#include "lib/error.hpp" +#include "proc/engine/buffer-provider.hpp" +#include "proc/engine/buffhandle.hpp" + + +namespace engine { + + + + + /* === BuffHandle Implementation === */ + + +#define _EXCEPTION_SAFE_INVOKE(_CTOR_) \ + try \ + { \ + return *new(pBuffer_) _CTOR_; \ + } \ + catch(...) \ + { \ + emergencyCleanup(); /* EX_FREE */ \ + pBuffer_ = 0; \ + } + + /** 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. + * @throw error::Logic in case there is already another TypeHandler registered + * in charge of managing the buffer contents, or when the object to create + * would not fit into this buffer. + */ + template<typename BU> + inline BU& + BuffHandle::create() + { + takeOwnershipFor<BU>(); + _EXCEPTION_SAFE_INVOKE (BU()); + } + +#undef _EXCEPTION_SAFE_INVOKE + + + + /** @internal helper to attach an TypeHandler after-the fact. + * @note this prepares the buffer for placement-creating an embedded object. + * It doesn't actually create an object + * @throw error::Logic in case there is already another TypeHandler registered + * in charge of managing the buffer contents, or when the object to create + * would not fit into this buffer. + */ + template<typename BU> + inline void + BuffHandle::takeOwnershipFor() + { + BufferDescriptor howto_attach_object_automatically + = descriptor_.provider_->getDescriptor<BU>(); + takeOwnershipFor (howto_attach_object_automatically); // EX_STRONG + } + + + /** convenience shortcut: access the buffer contents casted to a specific type. + * @warning this is a \em blind cast, there is no type safety. + * @note clients can utilise the metadata::LocalKey to keep track of some + * specific property of the buffer, like e.g. the type of object. + */ + template<typename BU> + inline BU& + BuffHandle::accessAs() + { + if (!pBuffer_) + throw error::Logic ("buffer not (yet) locked for access by clients" + , LUMIERA_ERROR_LIFECYCLE); + return *reinterpret_cast<BU*> (pBuffer_); + } + + + +} // namespace engine +#endif diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index 32a47fc2c..a035f3a69 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -174,40 +174,15 @@ namespace engine { return descriptor_.determineBufferSize(); } + private: + template<typename BU> + void takeOwnershipFor(); + void takeOwnershipFor(BufferDescriptor const& type); + + void emergencyCleanup(); }; - /* === Implementation details === */ - - /** convenience shortcut: place and maintain an object within the buffer. - * This operation performs the necessary steps to attach an object; - * if the buffer isn't locked yet, it will do so. Moreover, the created - * object will be owned by the buffer management facilities, i.e. the - * destructor is registered as cleanup function. - */ - template<typename BU> - BU& - BuffHandle::create() - { - UNIMPLEMENTED ("convenience shortcut to attach/place an object in one sway"); - } - - - /** convenience shortcut: access the buffer contents casted to a specific type. - * @warning this is a \em blind cast, there is no type safety. - * @note clients can utilise the metadata::LocalKey to keep track of some - * specific property of the buffer, like e.g. the type of object. - */ - template<typename BU> - BU& - BuffHandle::accessAs() - { - if (!pBuffer_) - throw error::Logic ("buffer not (yet) locked for access by clients" - , LUMIERA_ERROR_LIFECYCLE); - return *reinterpret_cast<BU*> (pBuffer_); - } - } // 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 6732668b6..b7cfd338b 100644 --- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp +++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp @@ -28,7 +28,7 @@ //#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/buffhandle-attach.hpp" #include "proc/engine/bufftable.hpp" //#include <boost/format.hpp> diff --git a/tests/components/proc/engine/tracking-heap-block-provider-test.cpp b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp index 2d48be480..62ff32412 100644 --- a/tests/components/proc/engine/tracking-heap-block-provider-test.cpp +++ b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp @@ -24,8 +24,8 @@ #include "lib/error.hpp" #include "lib/test/run.hpp" #include "proc/engine/tracking-heap-block-provider.hpp" +#include "proc/engine/buffhandle-attach.hpp" #include "proc/engine/testframe.hpp" -#include "proc/engine/buffhandle.hpp" #include <cstdlib> #include <vector> From 2fce2b1c8d4488fbb4403955ae36475c09bffec3 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 25 Nov 2011 22:05:12 +0100 Subject: [PATCH 277/496] make ctor-tracking test::Dummy more widely usable --- src/lib/test/test-helper.cpp | 6 ++ src/lib/test/testdummy.hpp | 99 +++++++++++++++++++++++ tests/lib/scoped-holder-test.cpp | 58 ++++++------- tests/lib/scoped-holder-transfer-test.cpp | 48 +++++------ tests/lib/scoped-ptrvect-test.cpp | 32 ++++---- tests/lib/testdummy.hpp | 87 -------------------- tests/lib/vector-transfer-test.cpp | 16 ++-- 7 files changed, 182 insertions(+), 164 deletions(-) create mode 100644 src/lib/test/testdummy.hpp delete mode 100644 tests/lib/testdummy.hpp diff --git a/src/lib/test/test-helper.cpp b/src/lib/test/test-helper.cpp index 8c8a7dda0..1d031b3b0 100644 --- a/src/lib/test/test-helper.cpp +++ b/src/lib/test/test-helper.cpp @@ -22,6 +22,7 @@ #include "lib/test/test-helper.hpp" +#include "lib/test/testdummy.hpp" #include <boost/format.hpp> @@ -55,7 +56,12 @@ namespace test{ garbage[--p] = alpha[rand() % MAXAL]; return garbage; } + + /** storage for testdummy flags */ + + long Dummy::_local_checksum = 0; + bool Dummy::_throw_in_ctor = false; }} // namespace lib::test diff --git a/src/lib/test/testdummy.hpp b/src/lib/test/testdummy.hpp new file mode 100644 index 000000000..24d467c1f --- /dev/null +++ b/src/lib/test/testdummy.hpp @@ -0,0 +1,99 @@ +/* + TESTDUMMY.hpp - yet another test dummy for tracking ctor/dtor calls + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + + +#include <boost/noncopyable.hpp> +#include <algorithm> + + +namespace lib { +namespace test{ + + + class Dummy + : boost::noncopyable + { + int val_; + + /** to verify ctor/dtor calls */ + static long _local_checksum; + static bool _throw_in_ctor; + + public: + Dummy () + : val_(1 + (rand() % 100000000)) + { init(); } + + Dummy (int v) + : val_(v) + { init(); } + + ~Dummy() + { + checksum() -= val_; + } + + long add (int i) { return val_+i; } + + int getVal() const { return val_; } + + void + setVal (int newVal) + { + checksum() += newVal - val_; + val_ = newVal; + } + + friend void + swap (Dummy& dum1, Dummy& dum2) ///< checksum neutral + { + std::swap(dum1.val_, dum2.val_); + } + + static long& + checksum() + { + return _local_checksum; + } + + static void + activateCtorFailure(bool indeed =true) + { + _throw_in_ctor = indeed; + } + + + private: + void + init() + { + checksum() += val_; + if (_throw_in_ctor) + throw val_; + } + + }; + + +}} // namespace lib::test + diff --git a/tests/lib/scoped-holder-test.cpp b/tests/lib/scoped-holder-test.cpp index b424ec685..dd0734bb7 100644 --- a/tests/lib/scoped-holder-test.cpp +++ b/tests/lib/scoped-holder-test.cpp @@ -28,7 +28,7 @@ #include "lib/error.hpp" #include "lib/scoped-holder.hpp" -#include "testdummy.hpp" +#include "lib/test/testdummy.hpp" #include <boost/noncopyable.hpp> #include <iostream> @@ -85,20 +85,20 @@ namespace test{ void checkAllocation() { - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); { HO holder; CHECK (!holder); - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); create_contained_object (holder); CHECK (holder); CHECK (false!=holder); CHECK (holder!=false); - CHECK (0!=checksum); + CHECK (0 != Dummy::checksum()); CHECK ( &(*holder)); - CHECK (holder->add(2) == checksum+2); + CHECK (holder->add(2) == 2 + Dummy::checksum()); Dummy *rawP = holder.get(); CHECK (rawP); @@ -111,7 +111,7 @@ namespace test{ TRACE (test, "size(object) = %lu", sizeof(*holder)); TRACE (test, "size(holder) = %lu", sizeof(holder)); } - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); } @@ -119,11 +119,11 @@ namespace test{ void checkErrorHandling() { - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); { HO holder; - throw_in_ctor = true; + Dummy::activateCtorFailure(); try { create_contained_object (holder); @@ -131,15 +131,15 @@ namespace test{ } catch (int val) { - CHECK (0!=checksum); - checksum -= val; - CHECK (0==checksum); + CHECK (0 != Dummy::checksum()); + Dummy::checksum() -= val; + CHECK (0 == Dummy::checksum()); } CHECK (!holder); /* because the exception happens in ctor object doesn't count as "created" */ - throw_in_ctor = false; + Dummy::activateCtorFailure(false); } - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); } @@ -147,7 +147,7 @@ namespace test{ void checkCopyProtocol() { - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); { HO holder; HO holder2 (holder); @@ -158,38 +158,38 @@ namespace test{ CHECK (!holder); create_contained_object (holder); CHECK (holder); - long currSum = checksum; + long currSum = Dummy::checksum(); void* adr = holder.get(); VERIFY_ERROR(LOGIC, holder2 = holder ); CHECK (holder); CHECK (!holder2); CHECK (holder.get()==adr); - CHECK (checksum==currSum); + CHECK (Dummy::checksum()==currSum); VERIFY_ERROR(LOGIC, holder = holder2 ); CHECK (holder); CHECK (!holder2); CHECK (holder.get()==adr); - CHECK (checksum==currSum); + CHECK (Dummy::checksum()==currSum); create_contained_object (holder2); CHECK (holder2); - CHECK (checksum != currSum); - currSum = checksum; + CHECK (Dummy::checksum() != currSum); + currSum = Dummy::checksum(); VERIFY_ERROR(LOGIC, holder = holder2 ); CHECK (holder); CHECK (holder2); CHECK (holder.get()==adr); - CHECK (checksum==currSum); + CHECK (Dummy::checksum()==currSum); VERIFY_ERROR(LOGIC, HO holder3 (holder2) ); CHECK (holder); CHECK (holder2); - CHECK (checksum==currSum); + CHECK (Dummy::checksum()==currSum); } - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); } @@ -202,7 +202,7 @@ namespace test{ { typedef std::map<int,HO> MapHO; - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); { MapHO maph; CHECK (isnil (maph)); @@ -212,8 +212,8 @@ namespace test{ HO & contained = maph[i]; CHECK (!contained); } // 100 holder objects created by sideeffect - - CHECK (0==checksum); // ..... without creating any contained object! + // ..... without creating any contained object! + CHECK (0 == Dummy::checksum()); CHECK (!isnil (maph)); CHECK (100==maph.size()); @@ -224,14 +224,14 @@ namespace test{ CHECK (0 < maph[i]->add(12)); } CHECK (100==maph.size()); - CHECK (0!=checksum); + CHECK (0 != Dummy::checksum()); long value55 = maph[55]->add(0); - long currSum = checksum; + long currSum = Dummy::checksum(); CHECK (1 == maph.erase(55)); - CHECK (checksum == currSum - value55); // proves object#55's dtor has been invoked + CHECK (Dummy::checksum() == currSum - value55); // proves object#55's dtor has been invoked CHECK (maph.size() == 99); maph[55]; // create new empty holder by sideeffect... @@ -239,7 +239,7 @@ namespace test{ CHECK (!maph[55]); CHECK (maph.size() == 100); } - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); } diff --git a/tests/lib/scoped-holder-transfer-test.cpp b/tests/lib/scoped-holder-transfer-test.cpp index 0c3773095..36b1bdd2c 100644 --- a/tests/lib/scoped-holder-transfer-test.cpp +++ b/tests/lib/scoped-holder-transfer-test.cpp @@ -27,7 +27,7 @@ #include "lib/scoped-holder.hpp" #include "lib/scoped-holder-transfer.hpp" -#include "testdummy.hpp" +#include "lib/test/testdummy.hpp" #include <iostream> #include <vector> @@ -125,17 +125,17 @@ namespace test { void buildVector() { - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); { typedef typename Table<HO>::Type Vect; Vect table(50); - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); for (uint i=0; i<10; ++i) create_contained_object (table[i]); - CHECK (0 < checksum); + CHECK (0 < Dummy::checksum()); CHECK ( table[9]); CHECK (!table[10]); @@ -145,7 +145,7 @@ namespace test { CHECK (rawP == &(*table[5])); CHECK (rawP->add(-555) == table[5]->add(-555)); } - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); } @@ -153,29 +153,29 @@ namespace test { void growVector() { - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); { typedef typename Table<HO>::Type Vect; Vect table; table.reserve(2); - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); cout << ".\n..install one element at index[0]\n"; table.push_back(HO()); - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); create_contained_object (table[0]); // switches into "managed" state - CHECK (0 < checksum); - int theSum = checksum; + CHECK (0 < Dummy::checksum()); + int theSum = Dummy::checksum(); cout << ".\n..*** resize table to 16 elements\n"; for (uint i=0; i<15; ++i) table.push_back(HO()); - CHECK (theSum==checksum); + CHECK (theSum == Dummy::checksum()); } - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); } @@ -183,37 +183,37 @@ namespace test { void checkErrorHandling() { - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); { typedef typename Table<HO>::Type Vect; Vect table(5); table.reserve(5); - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); create_contained_object (table[2]); create_contained_object (table[4]); - CHECK (0 < checksum); - int theSum = checksum; + CHECK (0 < Dummy::checksum()); + int theSum = Dummy::checksum(); cout << ".\n.throw some exceptions...\n"; - throw_in_ctor = true; + Dummy::activateCtorFailure(); try { create_contained_object (table[3]); - NOTREACHED (); + NOTREACHED ("ctor should throw"); } catch (int val) { - CHECK (theSum < checksum); - checksum -= val; - CHECK (theSum==checksum); + CHECK (theSum < Dummy::checksum()); + Dummy::checksum() -= val; + CHECK (theSum == Dummy::checksum()); } CHECK ( table[2]); CHECK (!table[3]); // not created because of exception CHECK ( table[4]); - throw_in_ctor = false; + Dummy::activateCtorFailure(false); throw_in_transfer=true; // can do this only when using ScopedHolder try { @@ -223,10 +223,10 @@ namespace test { { CHECK ( table.size() < 10); } - CHECK (theSum == checksum); + CHECK (theSum == Dummy::checksum()); throw_in_transfer=false; } - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); } }; diff --git a/tests/lib/scoped-ptrvect-test.cpp b/tests/lib/scoped-ptrvect-test.cpp index 63772334b..b1d7fe62f 100644 --- a/tests/lib/scoped-ptrvect-test.cpp +++ b/tests/lib/scoped-ptrvect-test.cpp @@ -27,7 +27,7 @@ #include "lib/util.hpp" #include "lib/scoped-ptrvect.hpp" -#include "testdummy.hpp" +#include "lib/test/testdummy.hpp" namespace lib { @@ -63,16 +63,16 @@ namespace test{ void simpleUsage() { - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); { VectD holder; CHECK (isnil (holder)); - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); Dummy* ptr = new Dummy(); Dummy& ref = holder.manage (ptr); CHECK (!isnil (holder)); - CHECK (0!=checksum); + CHECK (0 != Dummy::checksum()); CHECK (&ref==ptr); holder.manage (new Dummy); @@ -80,7 +80,7 @@ namespace test{ CHECK (3 == holder.size()); holder.clear(); - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); CHECK (isnil (holder)); holder.manage (new Dummy); @@ -93,16 +93,16 @@ namespace test{ holder.manage (new Dummy); holder.manage (new Dummy); CHECK (9 == holder.size()); - CHECK (0 < checksum); + CHECK (0 < Dummy::checksum()); } - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); } void iterating() { - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); { VectD holder; for (int i=0; i<16; ++i) @@ -142,7 +142,7 @@ namespace test{ VERIFY_ERROR (ITER_EXHAUST, ++cii ); } - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); } @@ -151,10 +151,10 @@ namespace test{ { int id2, id3; Dummy* extracted(0); - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); { VectD holder; - CHECK (0 == checksum); + CHECK (0 == Dummy::checksum()); CHECK (isnil (holder)); holder.manage (new Dummy); @@ -163,7 +163,7 @@ namespace test{ holder.manage (new Dummy); holder.manage (new Dummy); CHECK (5 == holder.size()); - CHECK (0 < checksum); + CHECK (0 < Dummy::checksum()); id2 = holder[2].getVal(); id3 = holder[3].getVal(); @@ -173,14 +173,14 @@ namespace test{ CHECK (id3 == holder[2].getVal()); CHECK (4 == holder.size()); } - CHECK (0 < checksum); // not all dummies are dead - CHECK (id2 == checksum); // #2 is alive! + CHECK (0 < Dummy::checksum()); // not all dummies are dead + CHECK (id2 == Dummy::checksum()); // #2 is alive! extracted->setVal(id2+id3); - CHECK (id2+id3 == checksum); + CHECK (id2+id3 == Dummy::checksum()); delete extracted; - CHECK (0 == checksum); + CHECK (0 == Dummy::checksum()); } }; diff --git a/tests/lib/testdummy.hpp b/tests/lib/testdummy.hpp deleted file mode 100644 index 76dcac652..000000000 --- a/tests/lib/testdummy.hpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - TESTDUMMY.hpp - yet another test dummy for tracking ctor/dtor calls - - Copyright (C) Lumiera.org - 2008, Hermann Vosseler <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -* *****************************************************/ - - - -#include <boost/noncopyable.hpp> -#include <algorithm> - - -namespace lib { -namespace test{ - - namespace { // yet another test dummy - - long checksum = 0; - bool throw_in_ctor = false; - - class Dummy - : boost::noncopyable - { - int val_; - - public: - Dummy () - : val_(1 + (rand() % 100000000)) - { init(); } - - Dummy (int v) - : val_(v) - { init(); } - - ~Dummy() - { - checksum -= val_; - } - - long add (int i) { return val_+i; } - - int getVal() const { return val_; } - - void - setVal (int newVal) - { - checksum += newVal - val_; - val_ = newVal; - } - - friend void - swap (Dummy& dum1, Dummy& dum2) ///< checksum neutral - { - std::swap(dum1.val_, dum2.val_); - } - - private: - void - init() - { - checksum += val_; - if (throw_in_ctor) - throw val_; - } - - }; - - } // anonymous test dummy - -}} // namespace lib::test - diff --git a/tests/lib/vector-transfer-test.cpp b/tests/lib/vector-transfer-test.cpp index 98f8f4021..6a4e8974d 100644 --- a/tests/lib/vector-transfer-test.cpp +++ b/tests/lib/vector-transfer-test.cpp @@ -25,7 +25,7 @@ #include "lib/test/run.hpp" #include "lib/scoped-holder-transfer.hpp" -#include "testdummy.hpp" +#include "lib/test/testdummy.hpp" #include <iostream> #include <vector> @@ -130,27 +130,27 @@ namespace test { cout << "\n..setup table space for 2 elements\n"; TransDummyVector table; table.reserve(2); - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); cout << "\n..install one element at index[0]\n"; table.push_back(TransDummy()); - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); table[0].setup(); // switches into "managed" state - CHECK (0 < checksum); - int theSum = checksum; + CHECK (0 < Dummy::checksum()); + int theSum = Dummy::checksum(); cout << "\n..*** resize table to 5 elements\n"; table.resize(5); - CHECK (theSum==checksum); + CHECK (theSum==Dummy::checksum()); cout << "\n..install another element\n"; table[3].setup(375); - CHECK (theSum+375==checksum); + CHECK (theSum+375==Dummy::checksum()); cout << "\n..kill all elements....\n"; table.clear(); - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); } }; From 7ba8ff432f5557295d0527ae71fc781b4ecc52b0 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 26 Nov 2011 00:09:59 +0100 Subject: [PATCH 278/496] BufferProvider interface finished thus far. simulated lifecycle passes unit test --- src/proc/engine/buffer-metadata.hpp | 31 ++++- src/proc/engine/buffer-provider.cpp | 48 ++++++-- src/proc/engine/buffer-provider.hpp | 8 +- src/proc/engine/buffhandle-attach.hpp | 1 + src/proc/engine/buffhandle.hpp | 7 +- .../engine/diagnostic-buffer-provider.cpp | 29 +---- .../engine/diagnostic-buffer-provider.hpp | 17 +-- tests/46engine.tests | 2 +- .../proc/control/command-argument-test.cpp | 2 +- .../engine/buffer-provider-protocol-test.cpp | 114 +++++++++++++++--- 10 files changed, 178 insertions(+), 81 deletions(-) diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index 484450a10..c33c59ea7 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -235,6 +235,15 @@ namespace engine { return newKey; } + void + useTypeHandlerFrom (Key const& ref) + { + if (nontrivial(this->instanceFunc_)) + throw error::Logic ("unable to supersede an already attached TypeHandler" + , LUMIERA_ERROR_LIFECYCLE); + instanceFunc_ = ref.instanceFunc_; + } + LocalKey const& localKey() const { return specifics_;} size_t storageSize() const { return storageSize_; } @@ -346,6 +355,16 @@ namespace engine { return mark (LOCKED); } + Entry& + invalidate (bool invokeDtor =true) + { + if (buffer_ && invokeDtor) + invokeEmbeddedDtor_and_clear(); + buffer_ = 0; + state_ = FREE; + return *this; + } + protected: /** @internal maybe invoke a registered TypeHandler's @@ -712,11 +731,19 @@ namespace engine { { Entry* entry = table_.fetch (key); if (!entry) return; - if (entry && (FREE != entry->state())) + + ASSERT (entry && (key == HashVal(*entry))); + release (*entry); + } + + void + release (Entry const& entry) + { + if (FREE != entry.state()) throw error::Logic ("Attempt to release a buffer still in use" , error::LUMIERA_ERROR_LIFECYCLE); - table_.remove (key); + table_.remove (HashVal(entry)); } diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp index 449b41370..1303d1458 100644 --- a/src/proc/engine/buffer-provider.cpp +++ b/src/proc/engine/buffer-provider.cpp @@ -89,7 +89,7 @@ namespace engine { * to be returned to the client as result of the #lockBuffer call. * Performs the necessary metadata state transition leading from an * abstract buffer type to a metadata::Entry corresponding to an - * actual buffer, which is locked for exclusive use by one client. + * actual buffer, which is locked for exclusive use by one client. */ BuffHandle BufferProvider::buildHandle (HashVal typeID, void* storage, LocalKey const& implID) @@ -178,8 +178,9 @@ namespace engine { BufferProvider::releaseBuffer (BuffHandle const& handle) try { metadata::Entry& metaEntry = meta_->get (handle.entryID()); + metaEntry.mark(FREE); // might invoke embedded dtor function detachBuffer (metaEntry.parentKey(), metaEntry.localKey()); - metaEntry.mark(FREE); + meta_->release (metaEntry); } ERROR_LOG_AND_IGNORE (engine, "releasing a buffer from BufferProvider") @@ -198,7 +199,15 @@ namespace engine { void BufferProvider::attachTypeHandler (BuffHandle const& target, BufferDescriptor const& reference) { - UNIMPLEMENTED ("convenience shortcut to attach/place an object in one sway"); + metadata::Entry& metaEntry = meta_->get (target.entryID()); + metadata::Entry& refEntry = meta_->get (reference); + REQUIRE (refEntry.isTypeKey()); + REQUIRE (!metaEntry.isTypeKey()); + if (!metaEntry.isLocked()) + throw error::Logic ("unable to attach an object because buffer isn't locked for use" + , LUMIERA_ERROR_LIFECYCLE); + + metaEntry.useTypeHandlerFrom (refEntry); // EX_STRONG } @@ -211,10 +220,14 @@ namespace engine { */ void BufferProvider::emergencyCleanup (BuffHandle const& target, bool invokeDtor) - { - UNIMPLEMENTED ("emergency cleanup"); + try { + metadata::Entry& metaEntry = meta_->get (target.entryID()); + metaEntry.invalidate (invokeDtor); + detachBuffer (metaEntry.parentKey(), metaEntry.localKey()); + meta_->release (metaEntry); } - + ERROR_LOG_AND_IGNORE (engine, "cleanup of buffer metadata while handling an error") + bool @@ -222,7 +235,7 @@ namespace engine { { return isSameObject (*this, *descr.provider_); } - + @@ -267,20 +280,35 @@ namespace engine { void BuffHandle::emergencyCleanup() { - descriptor_.provider_->emergencyCleanup(*this); // EX_FREE - pBuffer_ = 0; + descriptor_.provider_->emergencyCleanup(*this); // EX_FREE + pBuffer_ = 0; } + /** Install a standard TypeHandler for an already locked buffer. + * This causes the dtor function to be invoked when releasing this buffer. + * The assumption is that client code will placement-construct an object + * into this buffer right away, and thus we're taking ownership on that object. + * @param type a reference BufferDescriptor defining an embedded TypeHandler to use + * A copy of this TypeHandler will be stored into the local metadata for + * this buffer only, not altering the basic buffer type in any way + * @throw lifecycle error when attempting to treat an buffer not in locked state + * @throw error::Logic in case of insufficient buffer space to hold the + * intended target object + * @note EX_STRONG + */ void BuffHandle::takeOwnershipFor(BufferDescriptor const& type) { + if (!this->isValid()) + throw error::Logic ("attaching an object requires an buffer in locked state" + , LUMIERA_ERROR_LIFECYCLE); if (this->size() < type.determineBufferSize()) throw error::Logic ("insufficient buffer size to hold an instance of that type"); descriptor_.provider_->attachTypeHandler(*this, type); // EX_STRONG } - + diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 4fff36efe..37add12f1 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -131,10 +131,10 @@ namespace engine { bool was_created_by_this_provider (BufferDescriptor const&) const; }; - - - - + + + + /* === Implementation === */ /** convenience shortcut: diff --git a/src/proc/engine/buffhandle-attach.hpp b/src/proc/engine/buffhandle-attach.hpp index 4f5b94b55..0b2d3a0ae 100644 --- a/src/proc/engine/buffhandle-attach.hpp +++ b/src/proc/engine/buffhandle-attach.hpp @@ -67,6 +67,7 @@ namespace engine { { \ emergencyCleanup(); /* EX_FREE */ \ pBuffer_ = 0; \ + throw; \ } /** convenience shortcut: place and maintain an object within the buffer. diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index a035f3a69..9ed842cb4 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -43,7 +43,7 @@ ** @see BufferProvider ** @see BufferProviderProtocol_test usage demonstration ** @see OutputSlot - ** @see bufftable.hpp storage for the buffer table + ** @see bufftable.hpp storage for the buffer table ** @see engine::RenderInvocation */ @@ -76,8 +76,6 @@ namespace engine { * @note this descriptor and especially the #subClassification_ is really owned * by the BufferProvider, which may use (and even change) the opaque contents * to organise the internal buffer management. - * - * @todo try to move that definition into buffer-provider.hpp ////////////////////////////////////TICKET #249 */ class BufferDescriptor { @@ -108,9 +106,6 @@ namespace engine { /** * Handle for a buffer for processing data, abstracting away the actual implementation. * The real buffer pointer can be retrieved by dereferencing this smart-handle class. - * - * @todo as of 6/2011 it isn't clear how buffer handles are actually created - * and how the lifecycle (and memory) management works //////////////////////TICKET #249 rework BuffHandle creation and usage */ class BuffHandle : public lib::BoolCheckable<BuffHandle> diff --git a/src/proc/engine/diagnostic-buffer-provider.cpp b/src/proc/engine/diagnostic-buffer-provider.cpp index f00dcb8a3..98f65cf94 100644 --- a/src/proc/engine/diagnostic-buffer-provider.cpp +++ b/src/proc/engine/diagnostic-buffer-provider.cpp @@ -27,13 +27,6 @@ #include "proc/engine/diagnostic-buffer-provider.hpp" #include "proc/engine/tracking-heap-block-provider.hpp" -//#include <boost/scoped_array.hpp> -//#include <vector> - -//using lib::ScopedPtrVect; -//using boost::scoped_array; - - namespace engine { @@ -42,20 +35,10 @@ namespace engine { lib::Singleton<DiagnosticBufferProvider> DiagnosticBufferProvider::diagnostics; - - - namespace { // Details of allocation and accounting - - - } // (END) Details of allocation and accounting - - - - DiagnosticBufferProvider::DiagnosticBufferProvider() - : pImpl_() //////////TODO create PImpl here + : pImpl_() { } DiagnosticBufferProvider::~DiagnosticBufferProvider() { } @@ -78,9 +61,9 @@ namespace engine { return diagnostics(); } - - - + + + TrackingHeapBlockProvider& DiagnosticBufferProvider::reset() { @@ -93,9 +76,9 @@ namespace engine { { return &implInstance == pImpl_.get(); } - - + + /* === diagnostic API === */ diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp index 83bb589ca..cef2c1e0d 100644 --- a/src/proc/engine/diagnostic-buffer-provider.hpp +++ b/src/proc/engine/diagnostic-buffer-provider.hpp @@ -33,6 +33,7 @@ #include "lib/error.hpp" #include "lib/singleton.hpp" #include "lib/util.hpp" +#include "proc/engine/type-handler.hpp" #include "proc/engine/buffer-provider.hpp" #include <boost/scoped_ptr.hpp> @@ -98,22 +99,6 @@ namespace engine { bool all_buffers_released() const; - template<typename BU> - bool - object_was_attached (uint bufferID) const - { - UNIMPLEMENTED ("verify object attachment status of a specific buffer"); - } - - - template<typename BU> - bool - object_was_destroyed (uint bufferID) const - { - UNIMPLEMENTED ("verify object attachment status of a specific buffer"); - } - - private: diff --git a/tests/46engine.tests b/tests/46engine.tests index 07fac0119..b0de4f200 100644 --- a/tests/46engine.tests +++ b/tests/46engine.tests @@ -12,7 +12,7 @@ return: 0 END -PLANNED "Buffer provider diagnostics" BufferProviderProtocol_test <<END +TEST "Buffer provider diagnostics" BufferProviderProtocol_test <<END return: 0 END diff --git a/tests/components/proc/control/command-argument-test.cpp b/tests/components/proc/control/command-argument-test.cpp index edd36dc1c..655686acc 100644 --- a/tests/components/proc/control/command-argument-test.cpp +++ b/tests/components/proc/control/command-argument-test.cpp @@ -333,7 +333,7 @@ namespace test { // store a set of parameter values, later to be used on invocation args.storeTuple ( - tuple::make (TTime(randTime()), Tstr("Lumiera rocks"), rand() % 100)); + tuple::make (TTime(randTime()), Tstr("Lumiera rocks"), 10 + rand() % 90)); CHECK (!isnil (args)); cout << args << endl; diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp index b7cfd338b..1b128823f 100644 --- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp +++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp @@ -24,29 +24,25 @@ #include "lib/error.hpp" #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" +#include "lib/test/testdummy.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-attach.hpp" #include "proc/engine/bufftable.hpp" -//#include <boost/format.hpp> -//#include <iostream> - -//using boost::format; -//using std::string; -//using std::cout; +using util::isSameObject; using util::for_each; namespace engine{ namespace test { -// using lib::AllocationCluster; -// using mobject::session::PEffect; + using lib::test::Dummy; + using ::engine::BuffHandle; - using lumiera::error::LUMIERA_ERROR_LIFECYCLE; + using error::LUMIERA_ERROR_LOGIC; + using error::LUMIERA_ERROR_LIFECYCLE; namespace { // Test fixture @@ -64,12 +60,16 @@ namespace test { } - /******************************************************************* - * @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. + /****************************************************************************** + * @test verify and demonstrate the usage cycle of data buffers for the engine + * based on the BufferProvider interface. This is kind of a "dry run" + * for documentation purposes, because the BufferProvider implementation + * used here is just a diagnostics facility, allowing to investigate + * the state of individual buffers even after "releasing" them. + * + * This test should help understanding the sequence of buffer management + * operations performed at various stages while passing an calculation job + * through the render engine. */ class BufferProviderProtocol_test : public Test { @@ -78,6 +78,8 @@ namespace test { { verifySimpleUsage(); verifyStandardCase(); + verifyObjectAttachment(); + verifyObjectAttachmentFailure(); } @@ -97,6 +99,7 @@ namespace test { TestFrame& content = buff.accessAs<TestFrame>(); CHECK (testData(0) == content); + buff.emit(); buff.release(); CHECK (!buff.isValid()); VERIFY_ERROR (LIFECYCLE, buff.accessAs<TestFrame>() ); @@ -104,8 +107,6 @@ namespace test { DiagnosticBufferProvider& checker = DiagnosticBufferProvider::access(provider); CHECK (checker.buffer_was_used (0)); CHECK (checker.buffer_was_closed (0)); - CHECK (checker.object_was_attached<TestFrame> (0)); - CHECK (checker.object_was_destroyed<TestFrame> (0)); CHECK (testData(0) == checker.accessMemory (0)); } @@ -146,6 +147,83 @@ namespace test { CHECK (checker.all_buffers_released()); #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829 } + + + void + verifyObjectAttachment() + { + BufferProvider& provider = DiagnosticBufferProvider::build(); + BufferDescriptor type_A = provider.getDescriptorFor(sizeof(TestFrame)); + BufferDescriptor type_B = provider.getDescriptorFor(sizeof(int)); + BufferDescriptor type_C = provider.getDescriptor<int>(); + + BuffHandle handle_A = provider.lockBuffer(type_A); + BuffHandle handle_B = provider.lockBuffer(type_B); + BuffHandle handle_C = provider.lockBuffer(type_C); + + CHECK (handle_A); + CHECK (handle_B); + CHECK (handle_C); + + CHECK (sizeof(TestFrame) == handle_A.size()); + CHECK (sizeof( int ) == handle_B.size()); + CHECK (sizeof( int ) == handle_C.size()); + + TestFrame& embeddedFrame = handle_A.create<TestFrame>(); + CHECK (isSameObject (*handle_A, embeddedFrame)); + CHECK (embeddedFrame.isAlive()); + CHECK (embeddedFrame.isSane()); + + VERIFY_ERROR (LOGIC, handle_B.create<TestFrame>()); // too small to hold a TestFrame + VERIFY_ERROR (LIFECYCLE, handle_C.create<int>()); // has already an attached TypeHandler (creating an int) + + handle_A.release(); + handle_B.release(); + handle_C.release(); + + CHECK (embeddedFrame.isDead()); + CHECK (embeddedFrame.isSane()); + } + + + void + verifyObjectAttachmentFailure() + { + BufferProvider& provider = DiagnosticBufferProvider::build(); + BufferDescriptor type_D = provider.getDescriptorFor(sizeof(Dummy)); + + Dummy::checksum() = 0; + BuffHandle handle_D = provider.lockBuffer(type_D); + CHECK (0 == Dummy::checksum()); // nothing created thus far + + handle_D.create<Dummy>(); + CHECK (0 < Dummy::checksum()); + + handle_D.release(); + CHECK (0 == Dummy::checksum()); + + BuffHandle handle_DD = provider.lockBuffer(type_D); + + CHECK (0 == Dummy::checksum()); + Dummy::activateCtorFailure(); + + CHECK (handle_DD.isValid()); + try + { + handle_DD.create<Dummy>(); + NOTREACHED ("Dummy ctor should fail"); + } + catch (int val) + { + CHECK (!handle_DD.isValid()); + + CHECK (0 < Dummy::checksum()); + CHECK (val == Dummy::checksum()); + } + + VERIFY_ERROR (LIFECYCLE, handle_DD.accessAs<Dummy>() ); + VERIFY_ERROR (LIFECYCLE, handle_DD.create<Dummy>() ); + } }; From d58fa9129c33b259db111976b7350f58e6224b75 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 26 Nov 2011 23:32:56 +0100 Subject: [PATCH 279/496] clean-up: remove direct access to the selection value --- src/gui/widgets/timeline/timeline-body.cpp | 6 ------ src/gui/widgets/timeline/timeline-body.hpp | 3 --- src/gui/widgets/timeline/timeline-ibeam-tool.hpp | 4 ++-- src/gui/widgets/timeline/timeline-state.hpp | 2 -- 4 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index e64110997..650feeb6b 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -85,12 +85,6 @@ TimelineBody::getTimelineWidget () const return timelineWidget; } -TimeSpan -TimelineBody::get_selection() -{ - return timelineWidget.get_state()->get_selection(); -} - ToolType TimelineBody::get_tool() const { diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index 8abf01a3f..c248ca061 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -74,9 +74,6 @@ public: TimelineWidget& getTimelineWidget () const; - TimeSpan - get_selection(); - /** * Returns the type of the currently selected timeline tool. */ diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.hpp b/src/gui/widgets/timeline/timeline-ibeam-tool.hpp index 22cf6862f..ecf5b517d 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.hpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.hpp @@ -138,12 +138,12 @@ private: enum DragType { /** - * No drag is occuring + * No drag is occurring */ None, /** - * A selection drag is occuring. + * A selection drag is occurring. * @remarks The position of one end of the selection was set at * mouse-down of the drag, and the other end is set by * drag-release. diff --git a/src/gui/widgets/timeline/timeline-state.hpp b/src/gui/widgets/timeline/timeline-state.hpp index 36ba996f3..7b5221aa1 100644 --- a/src/gui/widgets/timeline/timeline-state.hpp +++ b/src/gui/widgets/timeline/timeline-state.hpp @@ -106,8 +106,6 @@ public: */ timeline::TimelineViewWindow& get_view_window(); - TimeSpan& get_selection() { return selection_; } - SelectionListener<TimeSpan>& get_selection_listener() { return selectionListener; } From a6392f5d14adca9bdcedf6217195cf6d4824b6d9 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 27 Nov 2011 00:52:10 +0100 Subject: [PATCH 280/496] review and annotate design aspects regarding zoom handling --- src/gui/panels/timeline-panel.cpp | 1 - src/gui/widgets/timeline-widget.cpp | 3 ++- src/gui/widgets/timeline/timeline-body.cpp | 3 ++- src/gui/widgets/timeline/timeline-ruler.cpp | 2 ++ src/gui/widgets/timeline/timeline-view-window.cpp | 12 ++++-------- src/gui/widgets/timeline/timeline-zoom-scale.cpp | 1 + 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index 0366504c6..827a2bee9 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -144,7 +144,6 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager, update_tool_buttons(); update_zoom_buttons(); show_time (Time::ZERO); - std::cout << timelineWidget->get_state()->get_view_window().get_time_scale() << "\n"; } const char* diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index b971477c6..81d93e5ba 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -415,12 +415,13 @@ TimelineWidget::update_scroll() if(state) { + ///////////////////////////////////////////////TICKET #861 shoudln't that be performed by TimelineViewWindow, instead of manipulating values from the outside? timeline::TimelineViewWindow &window = state->get_view_window(); //----- Horizontal Scroll ------// // TEST CODE - horizontalAdjustment.set_upper(1000 * GAVL_TIME_SCALE / 200); + horizontalAdjustment.set_upper( 1000 * GAVL_TIME_SCALE / 200); horizontalAdjustment.set_lower(-1000 * GAVL_TIME_SCALE / 200); // Set the page size diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index 650feeb6b..1b580c262 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -267,7 +267,8 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event) if (timelineState) { - // Handle a middle-mouse drag if one is occuring + // Handle a middle-mouse drag if one is occurring + /////////////////////////////TICKET #861 : shoudln't all of that be performed by TimelineViewWindow, instead of manipulating values from the outside? switch(dragType) { case Shift: diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp index b8725aedf..532e461f7 100644 --- a/src/gui/widgets/timeline/timeline-ruler.cpp +++ b/src/gui/widgets/timeline/timeline-ruler.cpp @@ -274,6 +274,7 @@ TimelineRuler::draw_ruler(Cairo::RefPtr<Cairo::Context> cr, REQUIRE(ruler_rect.get_width() > 0); REQUIRE(ruler_rect.get_height() > 0); + /////////////////////////////TICKET #861 : what part of these calculation could be centralised within TimelineViewWindow? const TimelineViewWindow &window = viewWindow(); const gavl_time_t left_offset = _raw(window.get_time_offset()); const int64_t time_scale = window.get_time_scale(); @@ -504,6 +505,7 @@ TimelineRuler::calculate_major_spacing() const { unsigned int i; + /////////////////////////////TICKET #861 : couldn't that be a library function in TimelineViewWindow? const gavl_time_t major_spacings[] = { GAVL_TIME_SCALE / 1000, GAVL_TIME_SCALE / 400, diff --git a/src/gui/widgets/timeline/timeline-view-window.cpp b/src/gui/widgets/timeline/timeline-view-window.cpp index 8b6528f11..c53722e89 100644 --- a/src/gui/widgets/timeline/timeline-view-window.cpp +++ b/src/gui/widgets/timeline/timeline-view-window.cpp @@ -88,9 +88,8 @@ void TimelineViewWindow::zoom_view(int point, double time_scale_ratio) { // Apply the smoothing factor - int64_t new_time_scale = - (int64_t)(pow(time_scale_ratio, TimelineWidget::ZoomSmoothing) * - (double)TimelineWidget::MaxScale); + int64_t new_time_scale(pow (time_scale_ratio, TimelineWidget::ZoomSmoothing) + * double(TimelineWidget::MaxScale)); /* Prevent Zooming in To Close and Far */ if(new_time_scale < 1) @@ -100,8 +99,7 @@ TimelineViewWindow::zoom_view(int point, double time_scale_ratio) new_time_scale = TimelineWidget::MaxScale; // The view must be shifted so that the zoom is centred on the cursor - TimeVar newStartPoint = get_time_offset(); - newStartPoint += TimeValue (point * (timeScale - new_time_scale)); + TimeVar newStartPoint = timeOffset + TimeValue(point * (timeScale - new_time_scale)); set_time_offset (newStartPoint); // Apply the new scale @@ -133,7 +131,5 @@ TimelineViewWindow::changed_signal() const return changedSignal; } -} // namespace timeline -} // namespace widgets -} // namespace gui +}}} // namespace gui::widgets::timeline diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp index dfa71f858..02169dfdd 100644 --- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp +++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp @@ -102,6 +102,7 @@ TimelineZoomScale::wireTimelineState (shared_ptr<TimelineState> currentState, void TimelineZoomScale::on_timeline_state_changed (shared_ptr<TimelineState> newState) { + /////////////////////////////TICKET #861 : part of that could be a library routine! REQUIRE (newState); timelineState = newState; From d6fa67f2b8f9073737cd3c305ace3664395eb1d0 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 27 Nov 2011 01:28:48 +0100 Subject: [PATCH 281/496] refactor zoom smothing into TimelineViewWindow --- src/gui/widgets/timeline-widget.cpp | 4 +-- src/gui/widgets/timeline-widget.hpp | 10 ------- .../widgets/timeline/timeline-view-window.cpp | 27 +++++++++++++++---- .../widgets/timeline/timeline-view-window.hpp | 14 ++++++++++ .../widgets/timeline/timeline-zoom-scale.cpp | 13 +-------- 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index 81d93e5ba..fac09a6f5 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -41,9 +41,7 @@ namespace widgets { const int TimelineWidget::TrackPadding = 1; const int TimelineWidget::HeaderWidth = 150; const int TimelineWidget::HeaderIndentWidth = 10; -const double TimelineWidget::ZoomIncrement = 1.25; // Not currently used -const double TimelineWidget::ZoomSmoothing = 9.0; -const int64_t TimelineWidget::MaxScale = 30000000; // 30 Million + TimelineWidget::TimelineWidget(shared_ptr<timeline::TimelineState> source_state) : Table(2, 2) diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index b5d57c667..ffff15dda 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -278,20 +278,10 @@ protected: bool update_tracks_frozen; /* ===== Constants ===== */ -public: - /** - * The maximum scale for timeline display. - * @remarks At MaxScale, every pixel on the timeline is equivalent - * to 30000000 lumiera::Time increments. - */ - static const int64_t MaxScale; - static const double ZoomSmoothing; - protected: static const int TrackPadding; static const int HeaderWidth; static const int HeaderIndentWidth; - static const double ZoomIncrement; friend class timeline::TimelineBody; friend class timeline::TimelineHeaderContainer; diff --git a/src/gui/widgets/timeline/timeline-view-window.cpp b/src/gui/widgets/timeline/timeline-view-window.cpp index c53722e89..357e4db97 100644 --- a/src/gui/widgets/timeline/timeline-view-window.cpp +++ b/src/gui/widgets/timeline/timeline-view-window.cpp @@ -31,6 +31,14 @@ namespace gui { namespace widgets { namespace timeline { +/* == public constants == */ + +const int64_t TimelineViewWindow::MaxScale = 30000000; // 30 Million +const double TimelineViewWindow::ZoomIncrement = 1.25; // Not currently used +const double TimelineViewWindow::ZoomSmoothing = 9.0; + + + TimelineViewWindow::TimelineViewWindow (Offset offset, int64_t scale) : timeOffset(offset) , timeScale(scale) @@ -67,7 +75,7 @@ TimelineViewWindow::set_time_scale(int64_t scale) void TimelineViewWindow::set_time_scale(double ratio) { - int64_t max = TimelineWidget::MaxScale; + int64_t max = MaxScale; int64_t min = 1; if(ratio <= 0.0) @@ -84,19 +92,28 @@ TimelineViewWindow::set_time_scale(double ratio) set_time_scale((int64_t)(ratio * max)); } +double +TimelineViewWindow::get_smoothed_time_scale() const +{ + double linear_scale ( 1.0 / MaxScale * timeScale); + + // reverse the effect of zoom scale smoothing + return pow (linear_scale, (1.0 / ZoomSmoothing)); +} + void TimelineViewWindow::zoom_view(int point, double time_scale_ratio) { // Apply the smoothing factor - int64_t new_time_scale(pow (time_scale_ratio, TimelineWidget::ZoomSmoothing) - * double(TimelineWidget::MaxScale)); + int64_t new_time_scale(pow (time_scale_ratio, ZoomSmoothing) + * double(MaxScale)); /* Prevent Zooming in To Close and Far */ if(new_time_scale < 1) new_time_scale = 1; - if(new_time_scale > TimelineWidget::MaxScale) - new_time_scale = TimelineWidget::MaxScale; + if(new_time_scale > MaxScale) + new_time_scale = MaxScale; // The view must be shifted so that the zoom is centred on the cursor TimeVar newStartPoint = timeOffset + 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 d52f84406..f4c6fa558 100644 --- a/src/gui/widgets/timeline/timeline-view-window.hpp +++ b/src/gui/widgets/timeline/timeline-view-window.hpp @@ -54,6 +54,16 @@ using lib::time::Offset; class TimelineViewWindow { public: + + /** + * The maximum scale for timeline display. + * @remarks At MaxScale, every pixel on the timeline is equivalent + * to 30000000 lumiera::Time increments. + */ + static const int64_t MaxScale; + static const double ZoomSmoothing; + static const double ZoomIncrement; + /** * @param offset The initial view offset. @@ -95,6 +105,10 @@ public: */ void set_time_scale(int64_t time_scale); void set_time_scale(double ratio); + + /** get the current time scale with zoom smoothing applied */ + double get_smoothed_time_scale() const; + /** * Zooms the view in or out as by a number of steps while keeping a * given point on the timeline still. diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp index 02169dfdd..99299608a 100644 --- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp +++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp @@ -102,21 +102,10 @@ TimelineZoomScale::wireTimelineState (shared_ptr<TimelineState> currentState, void TimelineZoomScale::on_timeline_state_changed (shared_ptr<TimelineState> newState) { - /////////////////////////////TICKET #861 : part of that could be a library routine! REQUIRE (newState); timelineState = newState; - int64_t current_scale = - getViewWindow().get_time_scale(); - - double linear_scale = - (double) current_scale / (double) TimelineWidget::MaxScale; - - /* We have to Revese the Smoothing */ - double new_relative_scale = - pow(linear_scale,(1.0 / TimelineWidget::ZoomSmoothing)); - - adjustment.set_value(new_relative_scale); + adjustment.set_value (getViewWindow().get_smoothed_time_scale()); } void From 00813a0169dce4622e70c78aa0d42404e9c337c2 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 27 Nov 2011 06:15:35 +0100 Subject: [PATCH 282/496] long standing issue: a style guide page --- doc/devel/rfc/CCodingStyleGuide.txt | 15 +-- doc/index.txt | 1 + doc/technical/code/codingGuidelines.txt | 117 ++++++++++++++++++++++++ doc/technical/index.txt | 1 + doc/technical/overview.txt | 30 +++--- doc/user/intro/intro.txt | 3 +- 6 files changed, 144 insertions(+), 23 deletions(-) create mode 100644 doc/technical/code/codingGuidelines.txt diff --git a/doc/devel/rfc/CCodingStyleGuide.txt b/doc/devel/rfc/CCodingStyleGuide.txt index 72914077d..7325b3961 100644 --- a/doc/devel/rfc/CCodingStyleGuide.txt +++ b/doc/devel/rfc/CCodingStyleGuide.txt @@ -65,16 +65,11 @@ For each `struct namespace_foo_struct` we have following typedefs: [source,C] ---- -typedef struct namespace_foo_struct namespace_foo; // canonical -typename -typedef const namespace_foo * const_NamespaceFoo; // pointer to const -object -typedef namespace_foo* link:NamespaceFoo[]; // canonical -pointer/handle -typedef namespace_foo ** link:NamespaceFoo[]_ref; // when intend to -mutate the handle itself -typedef const namespace_foo ** const_NamespaceFoo_ref; // for const object -handle +typedef struct namespace_foo_struct namespace_foo; // basic struct name +typedef namespace_foo* NamespaceFoo; // canonical object pointer/handle +typedef const namespace_foo * const_NamespaceFoo; // pointer to const object +typedef namespace_foo ** NamespaceFoo_ref; // when intend to mutate the handle itself +typedef const namespace_foo ** const_NamespaceFoo_ref; // same for const object handle ---- Examples: diff --git a/doc/index.txt b/doc/index.txt index 5037b84fa..36ab3139a 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -16,6 +16,7 @@ Informations about the structure of Lumiera, they give an overview on the main c Programming-oriented details about the functional layers of Lumiera as well as a Doxygen documentation : * link:technical/index.html[Technical Documents] + * link:technical/code/codingGuidelines.html[Style and Coding Guidelines] === the TiddlyWiki === Currently, Lumiera is still in the design- and evolution phase. diff --git a/doc/technical/code/codingGuidelines.txt b/doc/technical/code/codingGuidelines.txt new file mode 100644 index 000000000..96be8032e --- /dev/null +++ b/doc/technical/code/codingGuidelines.txt @@ -0,0 +1,117 @@ +Coding Guidelines +================= +:Date: Autumn 2011 + + +_this page summarises some style and coding guidelines for the Lumiera code base_ + +Style Guide +----------- +The Lumiera project uses GNU indentation style with slight adaptations. + +- *no tabs* please. The typical ``semi indent'' of GNU style thus becomes 2 spaces. +- maximum line length is rather around *110 characters*. +- originally, GNU style focussed on plain-C code. + + We thus apply some relaxations and clarifications ... + + * the braces for a class scope are indented by 2 spaces + * the access modifiers start at this brace level, while all declarations and definitions + within the class are again indented by 2 spaces + * the line breaking rules are relaxed. Definitions and statements may be written as single line, + provided that the length remains below 110 chars and the result _remains legible_. Otherwise, + we'll fall back to the default and wrap the lines. More specifically + + ** function declarations may be written in one line + ** same for definitions with just a single statement in the body + ** same for simple if-statements without else-branch. + + * the space between function name and opening parenthesis of the argument list is not + enforced when this doesn't make sense, esp. for argument-less functions, chained calls + or constructor syntax. But in all other cases, we really value this additional space, + it improves legibility. + * template argument declarations are _always_ written on a separate line, above the + return type declaration. This rule holds even if the rest of a definition can be + written within a single line. + * the opening brace of namespaces is placed on the same line. Optionally, the namespace + body may be indented, as long as this helps underpinning the nesting structure. But + there is no need to use 3 indents on a 3 level nested namespace. One level is enough + to underpin the presence of a nesting. + +Naming conventions +~~~~~~~~~~~~~~~~~~ +Naming conventions are used to underpin the kind of element at hand and give a visual +clue to the reader. We use our own conventions -- there is no point in arguing that this +and that library or language uses other conventions. + +- type names start with an uppercase letter +- variable and function names start with lowercase. +- fields within a class, especially the private ones are decorated with a trailing underscore +- a leading underscore may be used to emphasize the strictly internal or technical nature of a type, + variable or function +- namespaces are all-lowercase +- macros and constants are preferably all-caps (at least where this makes sense) + +There is a preference for *CamelCase* -- yet underscores are acceptable, especially when the +name is more like a sentence than just a compound term. + +plain-C names +^^^^^^^^^^^^^ +Since C has no namespaces, we strictly require a +lumiera_+ prefix on all non-local names and definitions. +Generally, names should be formed according to the following pattern: + + namespace[_object][_verb[_subjects]][_version] + +In case a definition actually denotes an object, there should be + +- a basic struct name: `typedef struct namespace_foo_struct namespace_foo;` +- plus an object pointer/handle: `typedef namespace_foo* NamespaceFoo;` + +The object pointer/handle should be passed as 1^st^ argument with the name +self+ + + +General Code Arrangement and Layout +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- Headers and translation units are named `*.hpp` and `*.cpp` rsp. `*.h` and `*.c` + + Multilingual headers are called `*.h` +- Each header should be named according to the primary facility it exposes. For the filesystem name, + the +CamelCaseWords+ of this type are translated into +camel-case-words.hpp+ +- Each file should start with the GNU licence preamble. The headline should give a one-line summary. + The primary author(s) and the year of the initial copyright claim should be mentioned. +- Each header should be focussed on a specific purpose. Preferably it starts with a file-level + doxygen comment explaining the intention and anything not obvious from reading the code. +- when arranging headers and compilation units, please take care of the compilation times and the + code size. Avoid unnecessary includes. Use forward declarations where applicable. + Yet still, _all really required includes should be mentioned_ (even if already included by another + dependency) +- The include block starts with our own dependencies, followed by a second block with the library + dependencies. After that, optionally some symbols may be brought into scope (through +using+ clauses). + Avoid cluttering top-level namespaces. Never import full namespaces (no +using namespace boost;+ please!) +- the includes for our own dependencies shall be given relative to source-root (or test root). Don't use + relative includes for headers located in the same directory, or -- worse still -- in the parent directory. +- sometimes, the actual translation units will combine several facilities for technical reasons, e.g. + when sharing an implementation-level class or even singleton instance. + Anonymous namespaces should be used liberally to avoid unnecessary exports. +- template code mostly needs to reside in headers. (same for metaprogramming code). + We employ the simple inclusion model (``Borland model'') for template instantiation. +- But in some specific situations it is preferable to drive explicit instantiations from within + a +*.cpp+ file. Most notably this is the case when defining some core class hierarchies. + Such explicit instantiations should be limited to just a view obvious places. They should be + written into a block at the end of some central implementation file. See +assetmanager.cpp+ + for an example. +- deliberately there is _no single top-level namespace._ The +namespace lumiera+ is the root of + our _exported interfaces_ -- intended for use by external scripts and libraries. Everything + implementation related is arranged in per-subsystem trees of namespaces. The APIs of the + subsystems are exported explicitly into the +lumiera+ namespace. + + +Design Guidelines and Values +---------------------------- +Code is written for *being read by humans*; code shall convey its meaning even to the casual reader. +On the long run, this language nature of code is more important than any performance tweaks. Recall, +every idiot can figure out how to make a computer perform something. Yet the real challenge is to +write legible code. Code that operates exactly the way you'd guess just from reading it. +Black magic and all kinds of surprise trickery and cleverness are nothing to be proud off. + +-> please have a look at the link:/project/background/CleanCodeDevelopment.html[Clean Code] page +for a coherent system of design principles + diff --git a/doc/technical/index.txt b/doc/technical/index.txt index b56ad484d..ca4cffc43 100644 --- a/doc/technical/index.txt +++ b/doc/technical/index.txt @@ -31,6 +31,7 @@ components. .Development * link:http://www.lumiera.org/doxy/[*Doxygen generated documentation*] : API documentation of the Lumiera code. * organisation of the link:code/index.html[Code Base] in general +* link:code/codingGuidelines.html[Coding Style] and further guidelines .Building * link:build/index.html[*Buildsystem*] : Installation & compilation tools, dependencies and packaging. diff --git a/doc/technical/overview.txt b/doc/technical/overview.txt index 5688d9dad..d8dbb8904 100644 --- a/doc/technical/overview.txt +++ b/doc/technical/overview.txt @@ -48,20 +48,28 @@ The extra components are: Coding Style ~~~~~~~~~~~~ -The Lumiera team agreed on using GNU coding style with some exceptions (no -tabs, line wrap must not always be enforced). Otherwise we are a bit pedantic -to be consistent to make the codebase uniform. Function naming conventions -and other details are described in several RFCs. +The Lumiera team agreed on using GNU coding style with slight adaptations. +Generally speaking, we strive to keep the code base consistent and stick to +widely accepted guidelines and best practices. See our separate +link:{ldoc}/technical/code/codingGuidelines.html[Coding Guidelines] page. +Function naming conventions and other details are also described in several RFCs. + Documentation ~~~~~~~~~~~~~ +The central location for all design and technical documentation is the Lumiera +website you're reading right now. Besides that, a summary and introduction +for various components can be found in the file-level doxygen comments, while +details are usualy explained in the class and function level comments. -Every public function should be documented with doxygen comments. The overall -design is outlined in this text and documented in the various detail pages and -accompanying RFCs. Bootstrapping the Lumiera design used several other places, -in several TiddlyWikis, an UML model and cehteh's private wiki, most of this -information is asciidoced meanwhile and in progress to be integrated in the -central documentation hierarchy. +==== the TiddlyWiki +Currently, Lumiera is still in the design- and evolution phase. Documentation +is written as an ongoing effort. +There is an embedded JavaScript wiki (TiddlyWiki) within the source tree, mostly +used as design notebook, featuring day-to-day design sketches and notes, but also +quite some more persistent planning. Finished documentation text is constantly +moved over to the documentation section(s) of the Lumiera website. + +-> access the Proy-Layer link:http://lumiera.org/wiki/renderengine.html[TiddlyWiki online here] Test Driven Development @@ -73,7 +81,7 @@ implemented until they pass their tests. While increasing the initial development effort significantly, this approach is known to lead to clearly defined components and overall increases code quality. In practice, this approach might not be suitable at times, -nevertheless we try to sick to it as far as possible +nevertheless we try to stick to it as far as possible and maintain fairly complete test coverage. diff --git a/doc/user/intro/intro.txt b/doc/user/intro/intro.txt index 5617dd44a..dcbf377ab 100644 --- a/doc/user/intro/intro.txt +++ b/doc/user/intro/intro.txt @@ -1,7 +1,6 @@ Lumiera (as seen) from Outer Space ================================== -:Author: Christian Thäter ct@pipapo.org -:Author: Hermann Voßeler Ichthyostega@web.de +:Author: Lumiera_Core_Developers :Date: Summer 2010 From 0399ea1313a1eba3b786964055227a4a15c93b29 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 1 Dec 2011 16:38:23 +0100 Subject: [PATCH 283/496] we're missing an important meeting from 2008. this is the raw transcript, needs to be shortened --- doc/devel/meeting_summary/2008-11-12.txt | 1250 ++++++++++++++++++++++ 1 file changed, 1250 insertions(+) create mode 100644 doc/devel/meeting_summary/2008-11-12.txt diff --git a/doc/devel/meeting_summary/2008-11-12.txt b/doc/devel/meeting_summary/2008-11-12.txt new file mode 100644 index 000000000..ebc6ad06e --- /dev/null +++ b/doc/devel/meeting_summary/2008-11-12.txt @@ -0,0 +1,1250 @@ +2008-11-12 Lumiera Developers Meeting +===================================== +:Author: Ichthyostega +:Date: 2008-11-12 + +Dec 11, 2012 on #lumiera 20:00 - 01:15 UTC + + +__Participants__ + + * cehteh + * ichthyo + * joelholdsworth + * raffa + +_Summary and transcript supplemented by Ichthyo 11/2011_ + + + + * Platform and lib dependency status + * Discussion about foreseeable problems with Gui Plugins + * Include dir and namespaces + * build system, plugin tree + + + +Platform and lib dependency status +---------------------------------- +Right away, there is agreement between all 3 core devs that the Lumiera project +should try to stay close to a *stable reference linux distribution*. Usually, +developers tend to stick to ``latest greatest'', causing lots of difficult to resolve +problems to packagers and ursers of their software. The Lumiera team wants to go into +the opposite direction: staying close to a mature and stable distro and rather try to +backport any newer libraries, when they are _really required_ for getting ahead with +the coding. Since _cehteh_ and _ichthyo_ are Debian users and _joelholdsworth_ tends +to use a moderately recent Ubuntu, the intention is to use Debian/stable as reference. + +Conclusion +~~~~~~~~~~ + + * use Debian/stable (currently Lenny) as reference distribution + * care for backports of any newer libraries required + * use VMs to verify the build on a moderate selection of more recent distros + + + +Topic 1 +------- +Summary what is discussed + +_cehteh_ points out that... + +_joelholdsworth_ adds.... + + +Conclusion +~~~~~~~~~~ + + * do this + * do that + + + + +'''' + + +.-- Discussion of details -- +[caption="☉Transcript☉ "] +---------------------------- +12:51 cehteh key/value pairs +12:51 gmerlin not sure about that +12:51 cehteh i am pretty sure that i want that in lumiera + +( shortened and tidied transcript of some interesting detailed discussions ) +---------------------------- + +[2008-11-12 21:49:18] <cehteh> Gui for Plugins, Architecture/Design .. the tough one :P +[2008-11-12 21:49:54] <cehteh> ichthyo: i think you rated my pragmatic approach bit high past days :) .. i just wanted to get somethnig going +[2008-11-12 21:50:19] <cehteh> as in "we have git we can refactor anytime, but make it work first" +[2008-11-12 21:50:20] <ichthyo> cehteh: no, I understand that +[2008-11-12 21:50:38] <joelholdsworth> right... I can focus a bit more +[2008-11-12 21:50:40] <ichthyo> and btw, nothing personal +[2008-11-12 21:50:44] <cehteh> hehe +[2008-11-12 21:51:19] <ichthyo> joelholdsworth: you told me your work job goes off the scale at the moment... +[2008-11-12 21:51:28] <cehteh> note: we had some mail coversations past days in german with made some things more clear to us :) +[2008-11-12 21:51:32] <joelholdsworth> yeah a little bit +[2008-11-12 21:51:58] <joelholdsworth> I had to prepare a talk with very short notice, on top of everything else I'm doing +[2008-11-12 21:52:28] <joelholdsworth> so I've been pretty solid for last week +[2008-11-12 21:52:28] <ichthyo> it's sort of the same for me. At job we had a hell of a high transaction load +[2008-11-12 21:52:34] <joelholdsworth> things should calm down aftern tomorrow +[2008-11-12 21:52:52] <joelholdsworth> :) +[2008-11-12 21:52:54] <joelholdsworth> such is life +[2008-11-12 21:52:56] <joelholdsworth> anyway... what next? +[2008-11-12 21:53:03] <ichthyo> On top of everything, I got a CD deadline next Tuedsay +[2008-11-12 21:53:11] <cehteh> Gui for Plugins, Architecture/Design .... still +[2008-11-12 21:53:17] <ichthyo> but after that, I should have more time +[2008-11-12 21:53:25] <ichthyo> yes +[2008-11-12 21:53:50] <ichthyo> I thought we could just discuss the problem informally and share some thoughts what we think is most critical +[2008-11-12 21:54:01] <cehteh> ichthyo: so i am more about how to deploy the internal components .. means who of us does the 'main()' +[2008-11-12 21:54:20] <joelholdsworth> not me - my main is only a temporary hack +[2008-11-12 21:54:24] <cehteh> not about architecture, about get it rolling +[2008-11-12 21:54:30] <joelholdsworth> that's why my front end has a stupid name +[2008-11-12 21:54:56] <cehteh> yes i made one too in my devel branch .. but ichthyo has some other valid plans +[2008-11-12 21:55:30] <ichthyo> probably I could just collect together my parts-of-a-main in another devel branch +[2008-11-12 21:55:38] <ichthyo> then we can compare them +[2008-11-12 21:55:44] -->| pedro (n=pedro@32.Red-83-61-132.dynamicIP.rima-tde.net) has joined #lumiera +[2008-11-12 21:55:46] <cehteh> i opt for plain C .. but :) +[2008-11-12 21:55:56] <ichthyo> :) +[2008-11-12 21:56:26] <cehteh> its now clear that you dont want to use the 'interfaces' between backend and proc +[2008-11-12 21:56:31] <cehteh> not yet at least +[2008-11-12 21:56:39] <ichthyo> ah, btw. The textbook answer is: the main needs to be C++ if you want to have a mixed linkage +[2008-11-12 21:56:49] <ichthyo> the C++ faq tells the same +[2008-11-12 21:56:58] <ichthyo> I never questioned this +[2008-11-12 21:57:06] <thorwil> good night! :) +[2008-11-12 21:57:12] <cehteh> ok +[2008-11-12 21:57:13] <ichthyo> good night thorwil +[2008-11-12 21:57:28] |<-- thorwil has left kornbluth.freenode.net (Remote closed the connection) +[2008-11-12 21:57:29] <cehteh> iirc the C++ linker suffices +[2008-11-12 21:57:32] |<-- wimo has left kornbluth.freenode.net (Read error: 104 (Connection reset by peer)) +[2008-11-12 21:57:38] <cehteh> but well that are implementation details +[2008-11-12 21:58:10] <ichthyo> plus the main has be to treated by the C++ compiler, because he builds the startup and shutdown object code there +[2008-11-12 21:58:14] <cehteh> how about constructors for static objects in dynamic modules? will they be called when the lib is dlopend? +[2008-11-12 21:58:23] <cehteh> i have to figure that out +[2008-11-12 21:58:45] <ichthyo> I must confess I haven't tried this. But I thought the official answer was yes +[2008-11-12 21:59:05] <ichthyo> if the build process of the dynamic lib was done by the C++ linker of course +[2008-11-12 21:59:07] <joelholdsworth> I think so yes +[2008-11-12 21:59:14] <cehteh> anyways ... basic bootstrapping should imo not have a very advanced OO design but just some simple sequence of actions which pull it up +[2008-11-12 21:59:21] <cehteh> without becoming spaghetti +[2008-11-12 22:00:03] <ichthyo> as said.. I propose that I just code up my proposal too, than we can compare +[2008-11-12 22:00:18] <cehteh> and in detail .. config system needs to be first because other bootstraping might depend on config data +[2008-11-12 22:00:28] <ichthyo> thats clear. I noted that +[2008-11-12 22:00:51] <cehteh> next interfaces/plugins (initialization) +[2008-11-12 22:01:37] <ichthyo> It could even be that we need to push it down into the static initialisation phase. I am no fan of static initialisation and try to avoid doing anything of importance there, but we might get into a situation something needs a config value there +[2008-11-12 22:01:42] <cehteh> and then the rest .. in whatever order is needed +[2008-11-12 22:02:10] <cehteh> interfaces/plugin needs config +[2008-11-12 22:02:38] <ichthyo> yes, but we certainly don't need them in static initialisation. If we do, we do something wrong +[2008-11-12 22:02:41] <cehteh> what about my idea to put commandline args into the config? +[2008-11-12 22:02:47] <cehteh> yes +[2008-11-12 22:03:24] <ichthyo> cehteh: maybe we should discuss it later, and talk now just about the plugin GUI problem? +[2008-11-12 22:04:10] <ichthyo> I mean, after the official part of the meeting? +[2008-11-12 22:04:11] <cehteh> heh ... guess the next main() wont be there soon :) ... did you looked at my devel? +[2008-11-12 22:04:20] <cehteh> ok +[2008-11-12 22:04:50] <ichthyo> maybe I start with this topic +[2008-11-12 22:04:50] <cehteh> whats the plugin gui problem? +[2008-11-12 22:05:04] <cehteh> the whole gui .. or plugins in the gui? +[2008-11-12 22:05:07] <ichthyo> I see some problems we should thik about +[2008-11-12 22:05:18] <ichthyo> rather the latter, plugins +[2008-11-12 22:05:30] <joelholdsworth> ok +[2008-11-12 22:05:46] <joelholdsworth> so bundling GUI together with plugins is easier now, right? +[2008-11-12 22:05:59] <joelholdsworth> we implement some kind of parameters system +[2008-11-12 22:06:01] <ichthyo> there are plugins which don't provide a GUI, and some plugin systems allow for a standard method for creating plugin guis +[2008-11-12 22:06:03] <ichthyo> yes +[2008-11-12 22:06:14] <cehteh> joelholdsworth: thats up to you .. but the plugin system is ready +[2008-11-12 22:06:27] <joelholdsworth> yeah that's fine +[2008-11-12 22:06:38] <joelholdsworth> if you want to extend the GUI you just expose and interface for it +[2008-11-12 22:06:50] <ichthyo> so, basically loading a processing routine from a plugin and using it to process video/audio is no problem +[2008-11-12 22:06:58] <cehteh> basically you define some interface (first in C++) then translate it to the lumiera interface macros +[2008-11-12 22:07:09] <cehteh> yes +[2008-11-12 22:07:31] <ichthyo> but with the GUI there are several conceptual problems +[2008-11-12 22:07:35] <ichthyo> first. +[2008-11-12 22:07:44] <ichthyo> There are plugins which don't have a GUI. +[2008-11-12 22:07:45] <ichthyo> fine +[2008-11-12 22:07:51] * cehteh thinks about some canvas widget which the plugin can use to draw stuff there +[2008-11-12 22:08:05] <ichthyo> we need to define a parameter system, as joel pointed out +[2008-11-12 22:08:26] <joelholdsworth> cehteh: as in overlays on the video canvas? +[2008-11-12 22:08:38] <ichthyo> and then we need to fit the existing plugin systems, each one, to use this parameter system +[2008-11-12 22:08:45] <cehteh> thats type:key:value at simplest +[2008-11-12 22:08:51] <cehteh> maybe with some representation hints +[2008-11-12 22:08:54] <ichthyo> yes +[2008-11-12 22:08:58] <ichthyo> there is more about it +[2008-11-12 22:09:03] <ichthyo> representation hints +[2008-11-12 22:09:07] <ichthyo> type of gui control +[2008-11-12 22:09:15] <ichthyo> logarithmic/linear scale +[2008-11-12 22:09:17] <ichthyo> zero point +[2008-11-12 22:09:19] <ichthyo> range +[2008-11-12 22:09:19] <cehteh> joelholdsworth: up to you, not necessary overlays +[2008-11-12 22:09:27] <ichthyo> but it is manageable +[2008-11-12 22:09:38] <ichthyo> LADSPA is a prominent example +[2008-11-12 22:09:45] <cehteh> well .. i has my generic fader ... +[2008-11-12 22:09:55] <ichthyo> then, the problem in the GUI is how to arrange those controls +[2008-11-12 22:10:16] <cehteh> logarithmic/linear scale <<< how about exposing callbacks rather +[2008-11-12 22:10:20] <ichthyo> but, basically, I think this alternative is the most simple one and the one which will create the least problems +[2008-11-12 22:10:34] <joelholdsworth> icthyo: I figured the UI will just expose parameters +[2008-11-12 22:10:39] -->| daitheflu (n=francois@ill67-3-88-164-129-128.fbx.proxad.net) has joined #lumiera +[2008-11-12 22:10:42] <joelholdsworth> so you expose them semantically - maybe in a tree +[2008-11-12 22:10:51] <joelholdsworth> and add rich presentation hints +[2008-11-12 22:10:59] <cehteh> i think layout should be optional by some config/stylesheet +[2008-11-12 22:11:00] <joelholdsworth> that hit the UI how to lay out out +[2008-11-12 22:11:05] <ichthyo> cehteh: possible. But those things are already defined by the plugin systems we want to wrap up. +[2008-11-12 22:11:14] <cehteh> hi daitheflu +[2008-11-12 22:11:43] <ichthyo> ok. this was the first problem +[2008-11-12 22:11:50] <daitheflu> hi cehteh :) +[2008-11-12 22:11:53] <ichthyo> next problem is: +[2008-11-12 22:12:02] <cehteh> there are 2 things .. presentation and application +[2008-11-12 22:12:03] <daitheflu> good evening ! +[2008-11-12 22:12:16] <ichthyo> the plugins need to allocate some space in the GUI and should not clutter the workspace +[2008-11-12 22:12:33] <ichthyo> this is a tough really tough interface design problem +[2008-11-12 22:12:54] <joelholdsworth> well ichthyo: I'm not planning to allow plugins to "take over" +[2008-11-12 22:12:57] <ichthyo> because some plugins aren't just some "optional stuff", but right in the middle of the handling workflow +[2008-11-12 22:13:01] <cehteh> you have a list (or maybe tree) of parameters .. presentation aside ... tree just because some parameters depend on the presence of some else +[2008-11-12 22:13:05] <joelholdsworth> they get to extend very specific parts of the UI +[2008-11-12 22:13:09] <joelholdsworth> e.g. params +[2008-11-12 22:13:11] <joelholdsworth> or the canvas +[2008-11-12 22:13:15] <joelholdsworth> or timeline +[2008-11-12 22:13:20] <cehteh> and you have a presentation .. +[2008-11-12 22:13:22] <joelholdsworth> and only in very specific ways +[2008-11-12 22:13:27] <ichthyo> thats fine +[2008-11-12 22:14:15] <joelholdsworth> ichthyo: and most of the time plugins wont extend anything - their whole UI will be exposed semantically (with UI presentation hints) +[2008-11-12 22:14:32] <joelholdsworth> (in addition to the plain semantics) +[2008-11-12 22:14:58] <ichthyo> but do you see the interface design problem? basically you want to design a smooth handling. but now you need to rely on some module which is plugged in and you don't know much about this module +[2008-11-12 22:15:23] <joelholdsworth> yes I agree +[2008-11-12 22:15:37] <ichthyo> how is it possible to tweak effects easily while you just don't know much about those effects +[2008-11-12 22:15:48] <ichthyo> then ther is another problem: +[2008-11-12 22:15:56] <ichthyo> key bindings and MIDI bindings +[2008-11-12 22:16:10] <ichthyo> you want to bind key shortcuts and external controllers to GUI elements +[2008-11-12 22:16:20] <ichthyo> and of course plugins need to be included into this system +[2008-11-12 22:16:35] <joelholdsworth> ok that's quite easy - you just right click on the control (or click a tiny button) that opens up a menu that lets you set this stuff +[2008-11-12 22:16:59] <joelholdsworth> UI design? well that's what presentation hints help with +[2008-11-12 22:17:05] <ichthyo> yes +[2008-11-12 22:17:26] <joelholdsworth> they set size, layout, the appriate type of slider for example to expose this value +[2008-11-12 22:17:29] <ichthyo> and what happens when the respective plugin options window isn't opened at all? +[2008-11-12 22:17:47] <ichthyo> or when you have multiple instances of the same plugin? +[2008-11-12 22:17:48] <joelholdsworth> what do you mean? +[2008-11-12 22:17:58] <cehteh> no tiny buttons to configure the ui please ... preferences are once place ... some key modifier +mouseclick another +[2008-11-12 22:18:11] <ichthyo> for example: you use a sound panner plugin +[2008-11-12 22:18:47] <ichthyo> and you want to adjust the sound pan in 3 dimensions by key shortcuts, or to bind them to some knobs on an external control surface +[2008-11-12 22:18:49] <cehteh> ichthyo: this slaps back on you, proc ... +[2008-11-12 22:18:59] <ichthyo> I know I know +[2008-11-12 22:19:03] <cehteh> how i thought about this: +[2008-11-12 22:19:24] <ichthyo> and now you have multiple instances of the same panner plugin +[2008-11-12 22:19:39] <ichthyo> so we need a naming scheme +[2008-11-12 22:19:45] <cehteh> 1) plugin provides the list of its parameters (maybe as tree) +[2008-11-12 22:20:05] <ichthyo> as tree for sure, because many +[2008-11-12 22:20:10] <cehteh> 2) there is a config file (config system) which gets installed with the plugins which defines the default layout +[2008-11-12 22:20:10] <ichthyo> plugins have nested windows +[2008-11-12 22:20:40] <cehteh> 3) user might override that in his own configs .. but still only defaults for new instances of the things +[2008-11-12 22:20:58] <cehteh> 4) the actual configured state is saved with the session +[2008-11-12 22:21:07] <cehteh> for each instance +[2008-11-12 22:21:11] <ichthyo> yes +[2008-11-12 22:21:19] <ichthyo> it should work this way +[2008-11-12 22:21:26] <cehteh> no doubt +[2008-11-12 22:21:49] <ichthyo> which means we need a naming/addressing scheme +[2008-11-12 22:21:52] <cehteh> while all this config just is a definition which gets applied to a kindof stylesheet +[2008-11-12 22:22:20] <ichthyo> ...which creates sort of a cascading scheme +[2008-11-12 22:22:22] <cehteh> yes the whole config system needs a namespace .. +[2008-11-12 22:22:37] <cehteh> seen the document i started? +[2008-11-12 22:22:42] <ichthyo> I mean, a naming/addressing scheme +[2008-11-12 22:23:04] <ichthyo> for distinguishing identical parameters in different instances of yet-unknown plugins +[2008-11-12 22:23:19] <ichthyo> and then: where to should the key binding stick? +[2008-11-12 22:23:39] <ichthyo> to a specific instance, or to the suitable instance which is just in focus? +[2008-11-12 22:23:46] <ichthyo> or both (probably) +[2008-11-12 22:24:03] <ichthyo> you may want to bind a different knob to each different panner plugin +[2008-11-12 22:24:25] <ichthyo> or alternatively, you may want to bind "the pan knob" to the "current pannner plugin inscance" +[2008-11-12 22:24:42] <ichthyo> this would require that we introduce a focus concept +[2008-11-12 22:25:00] <ichthyo> I think thorwil proposed something similar in the last discussion +[2008-11-12 22:25:16] <ichthyo> so we have a notion of the "current track", "current clip" +[2008-11-12 22:25:49] <ichthyo> and then there is another problem with plugin GUIs +[2008-11-12 22:26:10] <ichthyo> many plugin systems, especially VST have custom GUIs +[2008-11-12 22:26:27] <ichthyo> and may not even work properly without using those +[2008-11-12 22:26:48] <ichthyo> which means, some GUI code in the plugin takes over. That is something I utterly dislike +[2008-11-12 22:27:09] |<-- markekeller has left kornbluth.freenode.net (kornbluth.freenode.net irc.freenode.net) +[2008-11-12 22:27:09] |<-- cehteh has left kornbluth.freenode.net (kornbluth.freenode.net irc.freenode.net) +[2008-11-12 22:27:09] |<-- rgareus has left kornbluth.freenode.net (kornbluth.freenode.net irc.freenode.net) +[2008-11-12 22:27:09] |<-- joelholdsworth has left kornbluth.freenode.net (kornbluth.freenode.net irc.freenode.net) +[2008-11-12 22:27:10] |<-- lkjasa has left kornbluth.freenode.net (kornbluth.freenode.net irc.freenode.net) +[2008-11-12 22:27:10] |<-- rexbron has left kornbluth.freenode.net (kornbluth.freenode.net irc.freenode.net) +[2008-11-12 22:29:47] <ichthyo> hallooo.... +[2008-11-12 22:29:54] <ichthyo> anybody there....? +[2008-11-12 22:31:12] [INFO] The view ``#lumiera'' has been successfully saved to ``/home/hiv/Desktop/aua.html''. +[2008-11-12 22:31:20] [QUIT] Disconnected from irc://kornbluth.freenode.net/ (irc://kornbluth.freenode.net/). [[Reconnect][Reconnect to kornbluth.freenode.net][reconnect]] +[2008-11-12 22:31:30] [INFO] Now logging to <file:///Lager/heim/profi/fox/chatzilla/logs/kornbluth.freenode.net/channels/%23lumiera.2008-11-12.log>. +[2008-11-12 22:31:30] [INFO] Channel view for ``#lumiera'' opened. +[2008-11-12 22:31:32] =-= User mode for ichthyo is now +i +[2008-11-12 22:31:34] [INFO] No such nick/channel +[2008-11-12 22:31:38] -->| YOU (ichthyo) have joined #lumiera +[2008-11-12 22:31:38] =-= Topic for #lumiera is ``Lumiera, the new emerging NLE for linux http://www.lumiera.org | Enter the logo contest at http://www.pipapo.org/pipawiki/Lumiera/Logos'' +[2008-11-12 22:31:38] =-= Topic for #lumiera was set by raffa on 27.10.2008 21:57:46 +[2008-11-12 22:31:45] <ichthyo> haloooooo.... +[2008-11-12 22:31:51] <ichthyo> anybody there???? +[2008-11-12 22:32:09] <daitheflu> sure :) +[2008-11-12 22:32:10] -->| cehteh__ (n=ct@pipapo.org) has joined #lumiera +[2008-11-12 22:32:14] <cehteh__> ah found +[2008-11-12 22:32:20] <ichthyo> all killed by a virus? +[2008-11-12 22:32:21] <cehteh__> i am on 3 server +[2008-11-12 22:32:31] <ichthyo> seems there was a mass disconnect +[2008-11-12 22:32:39] <cehteh__> no netsplit +[2008-11-12 22:33:14] <cehteh__> well i found you and joel on another server +[2008-11-12 22:33:28] <cehteh__> [22:22] <ichthyo> I mean, a naming/addressing scheme +[2008-11-12 22:33:29] <cehteh__> [22:23] <ichthyo> for distinguishing identical parameters in different instances of yet-unknown plugins +[2008-11-12 22:33:29] <cehteh__> [22:23] <cehteh> no thats not needed +[2008-11-12 22:33:29] <cehteh__> [22:23] <cehteh> in the session each object has a uuid thats its address +[2008-11-12 22:33:29] <cehteh__> [22:23] <cehteh> each parameter has a uuid too +[2008-11-12 22:33:30] <cehteh__> [22:24] <cehteh> (interface system) +[2008-11-12 22:33:31] <cehteh__> [22:24] <cehteh> so you can say parameter 0x2344545645654534 of object 0x4543765732525t643 .... +[2008-11-12 22:33:34] <cehteh__> [22:26] <cehteh> yet unknown doesnt exist .. it is either unknown or known :) +[2008-11-12 22:33:35] *tomaw* [Global Notice] Hi all. That split has taken services and 10k with it. We're currently investigating the cause and hope to have it resolved soon. Further updates via wallops (/mode <nick> +w). Thanks! +[2008-11-12 22:35:37] [QUIT] Disconnected from irc://kornbluth.freenode.net/ (irc://kornbluth.freenode.net/). [[Reconnect][Reconnect to kornbluth.freenode.net][reconnect]] +[2008-11-12 22:37:59] [INFO] No such nick/channel +[2008-11-12 22:38:03] -->| YOU (ichthyo) have joined #lumiera +[2008-11-12 22:38:03] =-= Topic for #lumiera is ``Lumiera, the new emerging NLE for linux http://www.lumiera.org | Enter the logo contest at http://www.pipapo.org/pipawiki/Lumiera/Logos'' +[2008-11-12 22:38:03] =-= Topic for #lumiera was set by raffa on 27.10.2008 21:57:46 +[2008-11-12 22:38:15] <cehteh__> ichthyo: stay here +[2008-11-12 22:38:27] [QUIT] Disconnected from irc://kornbluth.freenode.net/ (irc://kornbluth.freenode.net/). [[Reconnect][Reconnect to kornbluth.freenode.net][reconnect]] +[2008-11-12 22:48:08] -->| YOU (ichthyo) have joined #lumiera +[2008-11-12 22:48:08] =-= Topic for #lumiera is ``Lumiera, the new emerging NLE for linux http://www.lumiera.org | Enter the logo contest at http://www.pipapo.org/pipawiki/Lumiera/Logos'' +[2008-11-12 22:48:08] =-= Topic for #lumiera was set by raffa on 27.10.2008 21:57:46 +[2008-11-12 22:48:13] <ichthyo> haha +[2008-11-12 22:49:05] <joelholdsworth> yay! +[2008-11-12 22:49:06] <ichthyo> I see three clones of cehteh +[2008-11-12 22:49:08] <cehteh_> heh +[2008-11-12 22:49:29] <joelholdsworth> cehteh, cehteh_ and cehteh__ ! +[2008-11-12 22:49:36] <cehteh__> [22:38] --> ichthyo (n=hiv@p5B0C1C57.dip0.t-ipconnect.de) has joined #lumiera +[2008-11-12 22:49:36] <cehteh__> [22:38] <cehteh__> ichthyo: stay here +[2008-11-12 22:49:36] <cehteh__> [22:38] <-- ichthyo has quit (Client Quit) +[2008-11-12 22:49:44] <ichthyo> has each cehteh_ clone a separate conscience? +[2008-11-12 22:49:51] <cehteh__> .. i tried to bring you back together +[2008-11-12 22:50:02] <cehteh> but well +[2008-11-12 22:50:09] <joelholdsworth> ha! +[2008-11-12 22:50:10] <ichthyo> connection was instable +[2008-11-12 22:50:18] <cehteh_> no one listens to +[2008-11-12 22:50:19] <cehteh__> me +[2008-11-12 22:50:24] <joelholdsworth> :D +[2008-11-12 22:50:28] <ichthyo> :-o +[2008-11-12 22:50:36] <joelholdsworth> anyway +[2008-11-12 22:50:51] <cehteh__> joel did you got my repaste? +[2008-11-12 22:50:54] |<-- cehteh__ has left kornbluth.freenode.net ("Client exiting") +[2008-11-12 22:50:57] <joelholdsworth> yes +[2008-11-12 22:50:58] |<-- cehteh_ has left kornbluth.freenode.net ("Client exiting") +[2008-11-12 22:51:00] <joelholdsworth> I was just making a comment about customization: +[2008-11-12 22:51:14] <cehteh> ichthyo: did you got my answer? +[2008-11-12 22:51:19] <joelholdsworth> Thorwil helpfully sent me a copy of the Blender's UI review +[2008-11-12 22:51:25] <ichthyo> about the UUIDs yes +[2008-11-12 22:51:28] <cehteh> uuids +[2008-11-12 22:51:30] <cehteh> ok +[2008-11-12 22:51:45] <joelholdsworth> the Belnder UI reviews says this: ""Lastly, I’d like to address another misconception, this time about customizability. There has been a notion that the solution to most of the UI problems can be solved with added customizability. Thenotion goes that if the UI is inefficient, the user can simply customize it herself to suit her needs. This claim is wrong for several reasons: +[2008-11-12 22:51:49] <joelholdsworth> • It is impossible to customize something you have not fully comprehended yet, so it in no way helps the learning process. +[2008-11-12 22:51:53] <joelholdsworth> • It makes an application less predictable, because the user cannot rely on the application to always act the same. +[2008-11-12 22:51:56] <joelholdsworth> • It takes focus away from the users content, and over to managing the application itself, which is what we wanted to avoid in the first place. +[2008-11-12 22:52:02] <joelholdsworth> • It undermines the portability of Blender, because you cannot rely on Blender functioning the same way across different systems. +[2008-11-12 22:52:05] <joelholdsworth> • Customizability does not equate to flexibility. +[2008-11-12 22:52:08] *christel* [Global Notice] Hi all! Terribly sorry for that (fairly substantial) split! Unfortunately one of our sponsors experienced routing issues affecting our secondary US hub, Services and one of our largest MRS servers.. The issues are hopefully fixed for now, but if you have some spare connectivity and hardware, perhaps you should consider helping us not keep all our eggs in one basket! Again, sorry and thanks for using freenode! +[2008-11-12 22:52:10] <joelholdsworth> • 99% of Blenders user will use the default setup anyway. +[2008-11-12 22:52:17] <joelholdsworth> I think those are all very good points! +[2008-11-12 22:52:30] <joelholdsworth> "This is not to say that customizability is always bad - having the ability to change the hotkeys +[2008-11-12 22:52:31] <joelholdsworth> from the defaults to match another 3D application such as Maya or Softimage XSI can make it easier for users of those applications to adapt to Blender." +[2008-11-12 22:53:03] <cehteh> yes we agree on that pretty much i thnik +[2008-11-12 22:53:08] <ichthyo> yes +[2008-11-12 22:53:16] <cehteh> customization should be hardcore +[2008-11-12 22:53:22] <cehteh> for special purposes +[2008-11-12 22:53:42] <joelholdsworth> ok - so you understand if I want to try and just do the UI "right", then add a little bit of customization back in here and there +[2008-11-12 22:53:50] <cehteh> but we may provide some 'theme' switcher .. not themes as in artwork but for workflow +[2008-11-12 22:54:17] <ichthyo> joelholdsworth: I fully support you with this goal +[2008-11-12 22:54:34] <joelholdsworth> well if we're thinking of having a perspective switcher like in eclipse, then saving different panel layouts is quite easy +[2008-11-12 22:54:44] <cehteh> yes +[2008-11-12 22:54:47] <cehteh> thats what i mean +[2008-11-12 22:54:53] <cehteh> thats absolutely necessary +[2008-11-12 22:54:58] <joelholdsworth> very good +[2008-11-12 22:55:08] <joelholdsworth> ok +[2008-11-12 22:55:17] <joelholdsworth> I just need to go wash the dishes +[2008-11-12 22:55:21] <joelholdsworth> back in 10 minutes +[2008-11-12 22:55:22] <cehteh> this panel layout shall be somewhat configureable .. (config system) +[2008-11-12 22:55:26] <ichthyo> the problem is just: you can't avoid things like a user binding some external control surface to a certain gui control +[2008-11-12 22:56:02] <cehteh> ichthyo: about the uuid .. does that solve the problem you spotted with addressing for you? +[2008-11-12 22:56:24] <ichthyo> yes... I intended to use them for this purpose +[2008-11-12 22:56:37] <ichthyo> all I wanted is to bring the problem to everyones attention +[2008-11-12 22:56:48] <ichthyo> also, that we need a parameter system +[2008-11-12 22:57:02] <ichthyo> and that this parameter system is probably tree-like, not just flat +[2008-11-12 22:57:06] <cehteh> i want anythnig be addressable with uuid(manipulating_function)+uuid(object) +[2008-11-12 22:58:41] <ichthyo> for me, this is just a opaque reference which is implemented in some way. So, given, I have the object representing the connectionn to the plugin, I can provide the GUI with some means for re-addressing a certain parameter, e.g. for a key binding +[2008-11-12 22:58:56] <cehteh> yes +[2008-11-12 22:59:09] <ichthyo> and, of course for re-establishing the same binding when re-creating the session from storage +[2008-11-12 22:59:22] <cehteh> internally you prolly dont even need the re-addressing .. the serializer will do that for you +[2008-11-12 22:59:33] <cehteh> but that needs to be worked out +[2008-11-12 22:59:50] <cehteh> means translating between memory addresses and uuids +[2008-11-12 23:00:18] <ichthyo> for now I am just fine with the notion that there is such a "persistent reference". And actually implementint it is just one very special problem +[2008-11-12 23:00:38] <cehteh> yes +[2008-11-12 23:00:50] <ichthyo> but with plugin guis instances is still the further problem is that you probably have more option windows of some plugins as you can show in any reasonable GUI +[2008-11-12 23:00:57] <cehteh> i dont want to care yet either .. i have some more concrete plans .. but that has some tmie +[2008-11-12 23:01:07] * ichthyo nods +[2008-11-12 23:02:31] <cehteh> ok +[2008-11-12 23:03:06] -->| daitheflu_ (n=francois@ill67-3-88-164-129-128.fbx.proxad.net) has joined #lumiera +[2008-11-12 23:03:14] |<-- daitheflu has left kornbluth.freenode.net (Read error: 104 (Connection reset by peer)) +[2008-11-12 23:03:34] <ichthyo> this issues were one of the reasons I made this "FeatureBundle" proposal some time ago. Meaning there is a mechanism allowing you to bundle +[2008-11-12 23:03:34] <ichthyo> - an existing external plugin +[2008-11-12 23:03:34] <ichthyo> - an adaptation we do for connecting it to the gui +[2008-11-12 23:03:34] <ichthyo> - maybe a script or gui +[2008-11-12 23:03:34] <ichthyo> - customisation info we need for the gui layout +[2008-11-12 23:03:34] <ichthyo> ...and provide all together as a single bundle the user can install +[2008-11-12 23:04:40] <ichthyo> joel is washing dishes. Maybe a short break to get some coffee for me? +[2008-11-12 23:05:10] <cehteh> the feature bundle has the deployment problem over different archs +[2008-11-12 23:05:47] <cehteh> but something like that is needed anyways +[2008-11-12 23:05:49] <joelholdsworth> back again +[2008-11-12 23:06:41] <cehteh> ... btw the pluginloader will be able to handle C source plugins which are compiled on the fly someday and things like that +[2008-11-12 23:07:02] <cehteh> lua scripts too .. but thats all far future +[2008-11-12 23:07:23] <cehteh> ah .. and i decided for .lum as plugin extension .. LumieraModule :) +[2008-11-12 23:07:39] <cehteh> (.so works too) +[2008-11-12 23:07:47] <joelholdsworth> that's about as corny as "java beans" +[2008-11-12 23:08:12] <cehteh> .lup lumiera plugin? +[2008-11-12 23:08:30] <cehteh> .lef luimera effect? +[2008-11-12 23:08:32] <cehteh> :) +[2008-11-12 23:09:18] <cehteh> well names dont matter much ... except the basic type of the plugin is decided on that +[2008-11-12 23:09:31] <cehteh> its easy to change/extend +[2008-11-12 23:09:54] * ichthyo back again with a cup of coffee +[2008-11-12 23:10:15] <ichthyo> .lum sounds good +[2008-11-12 23:11:15] <ichthyo> joelholdsworth: maybe you have already some ideas how to deal with all those plugin option windows? +[2008-11-12 23:11:27] <cehteh> really the least concern .. i just wanted to tell something while you get a coffee +[2008-11-12 23:11:36] <ichthyo> they are a real problem for getting a smooth handling and workflow +[2008-11-12 23:12:09] <cehteh> please do not pop up windows for each plugin :) +[2008-11-12 23:12:14] <ichthyo> because: if you work on tweaking, which can be a considerable amount of the whole project time +[2008-11-12 23:12:28] <ichthyo> (yes, that's what I am about to point out....) +[2008-11-12 23:12:34] <cehteh> i'd like to see that integrated in the whole screen concept +[2008-11-12 23:12:55] <cehteh> how about making 'resources' tabbed and have one 'plugin guis' tab there +[2008-11-12 23:13:05] <ichthyo> ...if you work on tweaking, you typically have a small amount of plugin windows which need to stay open +[2008-11-12 23:13:09] <cehteh> with maybe subtabs for all active plugins +[2008-11-12 23:13:14] <joelholdsworth> I guess we need some modeless way of putting plugin controls in a panel +[2008-11-12 23:13:48] <cehteh> then you can tile the resources pane ... or open 2 or more showing different plugin guis +[2008-11-12 23:13:48] <ichthyo> say some color correction on 2 tracks, and then you open and close variouos plugins for several different clips +[2008-11-12 23:14:23] <cehteh> joelholdsworth: see my screen concept .. :/ +[2008-11-12 23:14:39] <joelholdsworth> where? +[2008-11-12 23:14:49] <cehteh> anything should be tileable and detachable and open in multiple instances +[2008-11-12 23:14:54] <cehteh> gui brainstorm +[2008-11-12 23:14:57] <joelholdsworth> yes that's fine +[2008-11-12 23:14:58] <ichthyo> ...plus, maybe the possibility to "pin" some of the plugin windows, while others just allocate the next free slot +[2008-11-12 23:15:32] <joelholdsworth> so maybe having panels open up - or do it like inkscape with a scrollable panel container that gets longer as more panels are opened +[2008-11-12 23:15:48] <cehteh> joelholdsworth: well currently the gtk docks by default are still little special to the window manager +[2008-11-12 23:16:01] <cehteh> would be nice to make them all first class windows +[2008-11-12 23:16:32] <cehteh> .. think about tiled window managers :) +[2008-11-12 23:16:37] <joelholdsworth> I do +[2008-11-12 23:16:52] <joelholdsworth> and I plan to make it compatible with them +[2008-11-12 23:17:15] <cehteh> and provide tiling for the users which dont have a tiled window manager :P +[2008-11-12 23:17:23] <joelholdsworth> but I do want to get it working well for non-tiled WMs who are the vast majority of the user base +[2008-11-12 23:17:48] <cehteh> yes +[2008-11-12 23:18:09] <cehteh> but i expect that most people will run the lumiera window fullscreen anyways +[2008-11-12 23:18:34] <joelholdsworth> maybe +[2008-11-12 23:18:35] <cehteh> (at least resized to full screen dimensions, not fulscreen mdoe) +[2008-11-12 23:18:42] <joelholdsworth> yeah +[2008-11-12 23:19:08] <cehteh> video editing is nothing you do in a 400x300 window +[2008-11-12 23:19:15] <joelholdsworth> :) +[2008-11-12 23:19:25] <raffa> (Hi everyone!) +[2008-11-12 23:19:28] <ichthyo> joelholdsworth: have you already any ideas about focus handling? +[2008-11-12 23:19:31] <ichthyo> hi raffa +[2008-11-12 23:19:32] <cehteh> hi raffa +[2008-11-12 23:19:36] <joelholdsworth> hey +[2008-11-12 23:19:36] <cehteh> .. how was it? +[2008-11-12 23:19:45] <daitheflu_> hi raffa +[2008-11-12 23:20:10] <cehteh> cinelerras plugin guis who constantly get lost behind the main window are really a mess +[2008-11-12 23:20:15] <ichthyo> joelholdsworth: do we get a "current object", a "current track" or the like which recieves keybord input +[2008-11-12 23:20:28] <raffa> (cehteh: Great! :-) ) +[2008-11-12 23:20:37] <joelholdsworth> keybaord focus, yes - that was the plan +[2008-11-12 23:20:48] <cehteh> working on gnome/metacity at karins computer +[2008-11-12 23:20:54] <ichthyo> thorwil recently pointed at the concept of having an "active element" within a larger selected group of elements +[2008-11-12 23:21:12] <ichthyo> e.g. you have a "current clip" and within it a currently active effect attachment +[2008-11-12 23:21:38] <ichthyo> and if you press the shortcut, you switch over to the automation curve of this plugin if there is one +[2008-11-12 23:21:43] <ichthyo> etc. etc... +[2008-11-12 23:21:44] <cehteh> think about future ... multiple elements have focus from different devices +[2008-11-12 23:22:06] <cehteh> multitouch ... and other controlers +[2008-11-12 23:22:06] <joelholdsworth> I think we have to treat keyboard focus as special +[2008-11-12 23:22:23] <cehteh> next X version will utilitze multiple mouse pointers +[2008-11-12 23:22:35] <ichthyo> e.g. what's with the mouse wheel? +[2008-11-12 23:22:42] <ichthyo> whats with a jog dial +[2008-11-12 23:22:47] <ichthyo> on a control surface +[2008-11-12 23:22:48] -->| kaarna (n=joonaz@hoas-fe3cdd00-230.dhcp.inet.fi) has joined #lumiera +[2008-11-12 23:22:54] <cehteh> i have an external numeric keypad which i sometimes connect even when working with karins billing software +[2008-11-12 23:23:03] <ichthyo> or with the play/stop buttons on a control surface? +[2008-11-12 23:23:03] <joelholdsworth> my plan is to treat the jog dial as special +[2008-11-12 23:23:23] <joelholdsworth> yes again - special button assignment +[2008-11-12 23:23:23] <ichthyo> sort of like the mouse wheel? +[2008-11-12 23:23:34] <cehteh> imagine user wants a 2nd normal pc keyboard as video controler +[2008-11-12 23:23:42] <ichthyo> because all those rather follow sort of a focus +[2008-11-12 23:23:56] <ichthyo> maybe together with modifier keys +[2008-11-12 23:24:00] <cehteh> .o(adaptive mouse wheel) +[2008-11-12 23:24:04] <joelholdsworth> yes again that would work via an auxialiry controller setup config +[2008-11-12 23:24:16] <joelholdsworth> the primary keybaord and mouse work in the normal way +[2008-11-12 23:24:24] <joelholdsworth> we can't have two keyboard focusses +[2008-11-12 23:24:28] <joelholdsworth> - it's just won't work +[2008-11-12 23:24:43] <joelholdsworth> we can have a second keyboard - a board of hotkeys +[2008-11-12 23:24:47] <cehteh> works with numeric pad at least +[2008-11-12 23:24:56] <cehteh> yes thats what i mean +[2008-11-12 23:24:59] <joelholdsworth> yes that's ok - because that's an aux controller +[2008-11-12 23:25:04] <cehteh> that would be cool +[2008-11-12 23:25:21] <joelholdsworth> there's mouse, keyboard and auxiliary controllers +[2008-11-12 23:25:27] <cehteh> keyboards are really cheap and have plenty of inputs +[2008-11-12 23:25:34] <joelholdsworth> and those controllers would be mapped to the UI using a cunning dialog +[2008-11-12 23:25:58] <ichthyo> and what I said some time ago today with the binding for the "current" pan is a similar idea. Because also such a binding should rather follow the focus +[2008-11-12 23:26:21] <ichthyo> it doesn't help if you bind it to a specific instance of the panner plugin +[2008-11-12 23:26:34] <ichthyo> the moment you have more then 8 panners, it won't scale +[2008-11-12 23:26:43] <cehteh> see config system ... +[2008-11-12 23:27:07] <ichthyo> rather, it would be helpful, if you had a "current" fade and pan and sound level, which would follow the focus +[2008-11-12 23:27:15] <cehteh> this.panner < focus.panner +[2008-11-12 23:27:23] <cehteh> this.panner < track1.panner +[2008-11-12 23:27:43] <cehteh> meaning: just introduce another level of indirection +[2008-11-12 23:27:44] <joelholdsworth> yes ok +[2008-11-12 23:27:47] <ichthyo> so you could e.g. navigate the focus with the right hand at the cursor keys, and leave the left hand on your control surface's knobs +[2008-11-12 23:28:28] <ichthyo> please understand me right: I don't want to push features, and I will stay out of GUI issues. All I want is that everyone in the project is aware of this problems +[2008-11-12 23:28:41] <joelholdsworth> yes +[2008-11-12 23:28:45] <joelholdsworth> no I see the problem +[2008-11-12 23:28:50] <cehteh> instead having aux controlers you bind directly to some functionality +[2008-11-12 23:28:58] <ichthyo> ok thanks +[2008-11-12 23:29:02] |<-- kettuz has left kornbluth.freenode.net (Read error: 110 (Connection timed out)) +[2008-11-12 23:29:28] <cehteh> bind the aux controlers to some virtual controlers .. why get mapped to some virtual control points which then mapping to the real control +[2008-11-12 23:29:40] <cehteh> then you can configure it from both directions +[2008-11-12 23:29:55] -->| kettuz (n=kettuz@cs160222.pp.htv.fi) has joined #lumiera +[2008-11-12 23:30:45] <ichthyo> Wii controller for Lumiera :-) +[2008-11-12 23:30:53] <joelholdsworth> :) +[2008-11-12 23:30:56] <cehteh> having a trackball which always controls the main volume .. but with some modifier for example it controls the volume of the focused object +[2008-11-12 23:31:11] <ichthyo> yeah, this sort of things. +[2008-11-12 23:31:24] <ichthyo> Well, I think that's enough for this point on the agenda +[2008-11-12 23:31:35] <ichthyo> nothing of it is immediately urgend +[2008-11-12 23:31:45] <cehteh> adding one or 2 layers of indirection there wont be much complicated and makes it much more flexible +[2008-11-12 23:31:54] <ichthyo> but it's fine we are all aware of the problem and seemingly share the same aproach +[2008-11-12 23:32:20] <ichthyo> config system + some sort of automatic persisitent references within proc + some indirection + maybe a focus system +[2008-11-12 23:32:40] <cehteh> multi focus system +[2008-11-12 23:33:10] <ichthyo> and another point I want to note is: all of us are rather reluctant to allow plugins to create GUIs on their own +[2008-11-12 23:33:27] <joelholdsworth> depends what you mean by that +[2008-11-12 23:33:29] <cehteh> i guess we cant avoid that +[2008-11-12 23:33:38] <ichthyo> but note: there is a great urge from some some users for such things +[2008-11-12 23:33:39] <cehteh> for really extrn plugins +[2008-11-12 23:33:47] <joelholdsworth> yes +[2008-11-12 23:34:01] <cehteh> i am more cared about them fireing threads up :P +[2008-11-12 23:34:13] <ichthyo> thats part of the problem +[2008-11-12 23:34:33] <cehteh> well we cant completely avoid uncooperative plugins +[2008-11-12 23:34:37] <ichthyo> some start registering their own key shortcuts and all sorts of horrible things +[2008-11-12 23:34:42] <joelholdsworth> if you let each plugin create every bit of it's ui, you get a lot of code copy/paste and a lot of diverging GUI +[2008-11-12 23:34:45] <cehteh> neither from the core side, nor from the gui side +[2008-11-12 23:35:02] <joelholdsworth> so you need to "help" the plugins by putting them in a strict framework +[2008-11-12 23:35:09] <cehteh> yes +[2008-11-12 23:35:20] <ichthyo> Steinberg failed to address this problem properly and VST bears on this heritage until today +[2008-11-12 23:35:35] <cehteh> make it in a way where the 'correct' way is also the most convinient for any programmer is all and the best we can do +[2008-11-12 23:35:49] * ichthyo nods +[2008-11-12 23:36:04] <joelholdsworth> yes +[2008-11-12 23:36:07] <cehteh> note the 'any' ... +[2008-11-12 23:36:20] <ichthyo> :-P +[2008-11-12 23:36:25] <joelholdsworth> then we allow extensions which allow a little more flexibility +[2008-11-12 23:36:26] <cehteh> means mad programmers, beginner programmers, experienced programmers, html programmers ... +[2008-11-12 23:36:52] <joelholdsworth> but I'm thinking we do need to remember that the majority of our plugin developers will be part of this project team +[2008-11-12 23:37:21] <ichthyo> and at some point I am much in favor of the idea of a official "plugin list" or site which describes good and compatible plugins, and maybe offers customisation bindings for them +[2008-11-12 23:38:09] <ichthyo> joelholdsworth: no, it won't be this way. Rather, there are some mainstream plugin systems. People will foremost want to use plugins they know already +[2008-11-12 23:38:21] <ichthyo> besides a small set of core plugins, yes +[2008-11-12 23:38:34] <ichthyo> those are the ones we provide as native lumiera plugins +[2008-11-12 23:38:42] <joelholdsworth> hmm ok +[2008-11-12 23:38:44] <ichthyo> a fader, a mask etc. +[2008-11-12 23:38:50] <ichthyo> but anything besides that +[2008-11-12 23:39:06] <ichthyo> every sound engenieer has his favorite eq and compressor +[2008-11-12 23:39:26] <ichthyo> and he will rather swich to another application than being forced to use a compressor he isn't fine with +[2008-11-12 23:39:32] <cehteh> btw the plugin system is quite strict currently +[2008-11-12 23:39:36] <ichthyo> same for advanced video stuff +[2008-11-12 23:39:51] <cehteh> a plugin by default sees nothing from the main app +[2008-11-12 23:40:07] <joelholdsworth> good +[2008-11-12 23:40:07] <cehteh> all it gets is a handle which it can use to open interfaces +[2008-11-12 23:40:13] <joelholdsworth> that's the way it's meant to be +[2008-11-12 23:40:20] <ichthyo> yes, fully agreed +[2008-11-12 23:40:29] <cehteh> well its circumventable .. with some efforts +[2008-11-12 23:40:37] <ichthyo> "criminal energy" +[2008-11-12 23:40:46] <cehteh> exactly +[2008-11-12 23:40:47] <ichthyo> allways works like a charm +[2008-11-12 23:41:13] <cehteh> the other way aroud would be a plugin which starts up tcltk for a gui +[2008-11-12 23:41:18] <cehteh> that would be horror +[2008-11-12 23:41:38] <ichthyo> well... I think we are done with the points on the meeting agenda, I mean everything besides "DesignProcess" +[2008-11-12 23:41:50] <daitheflu_> cehteh: I second that +[2008-11-12 23:41:51] <cehteh> and i bet people will do that +[2008-11-12 23:42:25] <cehteh> so i think it might be better to provide a controlled side channel than let the people to ugly hacks +[2008-11-12 23:42:53] <ichthyo> especially for communication with the gui options window of the plugin +[2008-11-12 23:43:18] <cehteh> well lets see then +[2008-11-12 23:43:26] <cehteh> so DesignProcess .. +[2008-11-12 23:44:02] <cehteh> ichthyo: about ApplicationStructure .. do we want to start that one from scratch together +[2008-11-12 23:44:04] <ichthyo> is there anything we want/need to discuss this time +[2008-11-12 23:44:06] <ichthyo> ? +[2008-11-12 23:44:22] <cehteh> not scratch ... but heavily modifying it? +[2008-11-12 23:44:32] <cehteh> then i place it back as idea +[2008-11-12 23:44:40] <ichthyo> probably yes +[2008-11-12 23:45:13] <cehteh> i think our ideas are again not that different .. just the approach was +[2008-11-12 23:45:45] <ichthyo> most of the things we discussed on the private channel aren't really prominently reflected in this proposal. You can see them rather if you read very carefully +[2008-11-12 23:45:51] <cehteh> ok back to 'idea' +[2008-11-12 23:46:27] <cehteh> yes ... ApplicationStructure was actually misnamed +[2008-11-12 23:46:33] <ichthyo> as proposed: I will make an example in a devel branch, so we can compare? is that ok? +[2008-11-12 23:46:53] <cehteh> dunno +[2008-11-12 23:47:02] <ichthyo> not completely. I wouldn't say misnamed +[2008-11-12 23:47:10] <cehteh> you can just look at mine and modify what you want modified +[2008-11-12 23:47:33] <cehteh> i guess that would yield to earlier results than comparing +[2008-11-12 23:47:36] <ichthyo> because the questions of App structure are close and got into focus by this proposal +[2008-11-12 23:47:40] <cehteh> http://www.lumiera.org/gitweb?p=lumiera/ct;a=blob;f=src/lumiera/lumiera.c;h=3499e795b47292d41b8aae27662e08fa470efb13;hb=0140a048c1376dde8622778617c77cee94df5117 +[2008-11-12 23:47:50] <cehteh> as i saied there isnt much yet +[2008-11-12 23:48:14] <ichthyo> yes. I would just start my branch off at this point of course +[2008-11-12 23:48:18] <cehteh> i can make that compileable with C++ too +[2008-11-12 23:48:24] <cehteh> ok +[2008-11-12 23:48:36] <cehteh> well i can merge your branch into my devel +[2008-11-12 23:48:54] <ichthyo> this branch starts off really near to our current master +[2008-11-12 23:49:01] <ichthyo> which is quite clean and builds well +[2008-11-12 23:49:02] <cehteh> most important points for me where that i created the src/lumiera for the main program +[2008-11-12 23:49:10] <cehteh> it doesnt have to be named lumiera +[2008-11-12 23:49:34] <cehteh> but the important thing is that the config system and the interface/plugin system moved from the backend to that +[2008-11-12 23:49:43] <ichthyo> ah yes. There is the namespace issue we left open on the last meeting +[2008-11-12 23:49:57] <cehteh> heh well i dont want to attend that :) +[2008-11-12 23:50:14] <ichthyo> the problem is: I haven't prepared anything for discussing it +[2008-11-12 23:50:34] <cehteh> but its really oblivious that you dont need to reflect the C++ namespaces 1:1 in the directory structure +[2008-11-12 23:50:44] <ichthyo> I wanted to write a short summary, because I see two or three models of organizing the namespaces +[2008-11-12 23:50:56] <ichthyo> and we could best discuss it if we can all look at this +[2008-11-12 23:51:04] <ichthyo> but I didn't find the time to write it down +[2008-11-12 23:51:28] <cehteh> i stil think having all our stuff into lumiera:: would be good .. but :) +[2008-11-12 23:51:33] <ichthyo> in any case, it is closely interconnected to the "interface namespace": which means the following +[2008-11-12 23:51:50] <cehteh> not all .. tool stuff not +[2008-11-12 23:52:12] <ichthyo> there are some interfaces (opaque datatypes plus forward decls for C, abstract base classes for C++) +[2008-11-12 23:52:22] * cehteh has lumiera_ llist_ PPMPL_ cuckoo_ psplay_ in C ... for example +[2008-11-12 23:52:35] <ichthyo> plus the things defined via the interface/plugin system +[2008-11-12 23:52:44] <ichthyo> this stuff is our "public interface" +[2008-11-12 23:53:01] <ichthyo> and I really try hard to keep it separate from the implementation +[2008-11-12 23:53:14] <cehteh> the interface/plugin system has now its own rules which are somewhat special and good by that way +[2008-11-12 23:53:18] <ichthyo> those interface parts will be contained in a "Lumiera sdk" +[2008-11-12 23:53:40] <ichthyo> and probably all those interfaces (for C++) will go into namespace lumiera:: +[2008-11-12 23:53:51] <ichthyo> because it's just the best and most natural pick +[2008-11-12 23:53:53] <cehteh> i am thinking about a interface_doc.h which can be used to generate documentation from the interface language with some special gcc/cpp flags +[2008-11-12 23:54:30] <ichthyo> and, as said, I really want to try hard to get this interface part a real interface part and cleanly separated from the implementation +[2008-11-12 23:54:55] <cehteh> heh .. seen my config interface i made as example? +[2008-11-12 23:55:07] <ichthyo> meaning: I don't want *.c an *.cpp files in this package/directory/namespace, unless they are really needed there +[2008-11-12 23:55:55] <cehteh> well interfaces themself need to be .c they need to be implemented and the implementation might adapt the code +[2008-11-12 23:56:13] <ichthyo> so the question regarding namespaces is connected to the question: how is the implementation organized in relation to the interface part +[2008-11-12 23:56:17] <cehteh> for example the config system somtimes returns internal state as success/failure indicator +[2008-11-12 23:56:35] <cehteh> but its interface just returns integers +[2008-11-12 23:57:08] <cehteh> ah http://www.lumiera.org/gitweb?p=lumiera/ct;a=tree;f=src/lumiera;h=2160d2cbe84977fc5aa865495b3e3adad154d9dd;hb=0140a048c1376dde8622778617c77cee94df5117 +[2008-11-12 23:57:31] <cehteh> i request some nameing rules for interface files +[2008-11-12 23:57:37] <cehteh> config_interface.c .h ... +[2008-11-12 23:57:49] <cehteh> shall that be _if.c|h +[2008-11-12 23:58:27] <cehteh> because when i make the documentation generator i would like to have some easy pattern to filter interface files out +[2008-11-12 23:58:36] <ichthyo> you see: there is for example a "config.c". Does this file actually contain working code? Then it should be moved over into an imiplementation package +[2008-11-12 23:58:59] <cehteh> that is the src/lumiera +[2008-11-12 23:59:08] <ichthyo> yes, I know +[2008-11-12 23:59:12] <cehteh> i just prelimary dropped the _interface there in +[2008-11-12 23:59:38] <ichthyo> so given, src/lumiera contains our interface (I just take it as an example to make my point more clear) +[2008-11-12 23:59:41] <cehteh> config_interface.h needs to be installed someday +[2008-11-12 23:59:48] <cehteh> (with a better name maybe) +[2008-11-13 00:00:07] <ichthyo> so, someone *using* the interface system probably will need to include config_interface.h +[2008-11-13 00:00:19] <ichthyo> but he doesn't need config.c +[2008-11-13 00:00:29] <cehteh> config_interface.c is the mapping/actual implementation ... which gets linked in but the user doesnt need it +[2008-11-13 00:00:33] <cehteh> yes +[2008-11-13 00:00:52] <ichthyo> yes, thats my point. So we should try to separte those cleanly. +[2008-11-13 00:01:01] <ichthyo> Let me explain it on another example +[2008-11-13 00:01:02] <cehteh> what is in config_interface.c could be appended to config.c theoretically .. +[2008-11-13 00:01:08] <cehteh> stop +[2008-11-13 00:01:14] <ichthyo> Let me explain it on another example +[2008-11-13 00:01:28] <cehteh> thats intentionally just placed into one dir to be cleaned up :) +[2008-11-13 00:01:47] <ichthyo> I have a class "Session" which is located in a file session.hpp +[2008-11-13 00:01:52] <cehteh> do we want a src/includes/ ? +[2008-11-13 00:01:58] <ichthyo> but it is just a ABC +[2008-11-13 00:02:04] <cehteh> for includes which get installed +[2008-11-13 00:02:18] <ichthyo> and I have a Class SessionImpl which is contained in sessionimpl.hpp and sessionimpl.cpp +[2008-11-13 00:02:44] <ichthyo> and in the "interface package" there should be *only* session.hpp +[2008-11-13 00:02:46] <cehteh> :] +[2008-11-13 00:03:11] <ichthyo> probably this class Session should be moved into namespace lumiera:: +[2008-11-13 00:03:16] <ichthyo> then the situation would be clean +[2008-11-13 00:03:32] <ichthyo> note: up to now i wrote only implementation code. +[2008-11-13 00:03:49] |<-- kettuz has left kornbluth.freenode.net ("Leaving") +[2008-11-13 00:03:50] <cehteh> yes +[2008-11-13 00:04:07] <ichthyo> but I already made the class hierarchy this way, i.e. everyone *uses* just the class Session +[2008-11-13 00:04:28] <ichthyo> and only the class SessManagerImpl knows of the implementation class SessionImpl +[2008-11-13 00:04:32] <ichthyo> and so on +[2008-11-13 00:04:50] <cehteh> well thats not important yet... i currently have some 'mess' of includes because its just easier for now to mix them all together and relative easy to clean up when done +[2008-11-13 00:05:30] <ichthyo> same her, at least partially. In most cases I've just created the interface classes with the intention to move them into namespace lumiera:: when I am ready +[2008-11-13 00:05:39] <cehteh> and i dont have a differentiation between interface and implementation headers .. because a) its all implementation code yet +[2008-11-13 00:06:06] <cehteh> b) i was under the impression that i only wanted to export interfaces over 'Interfaces' +[2008-11-13 00:06:25] <ichthyo> ...which is fine +[2008-11-13 00:06:37] <cehteh> the config_interfaces.h | c stuff there is just the proof of concept +[2008-11-13 00:06:59] <ichthyo> same for most of the stuff I wrote. Just for gettting the tests running for now +[2008-11-13 00:07:18] <ichthyo> and probably the same for joel, right? +[2008-11-13 00:08:24] <cehteh> so .. public interfaces ... place in: [ ] src/include/*.h [ ] src/*/*_interface.h +[2008-11-13 00:08:29] <ichthyo> but... on the long run I am sure we don't just get one "includes" package, rather I guess we gett a (very small) tree of interfaces +[2008-11-13 00:08:45] <ichthyo> I am for the former: put them into a separate directory +[2008-11-13 00:08:53] <cehteh> ok +[2008-11-13 00:08:55] <ichthyo> put them in a clearly separated namespace +[2008-11-13 00:09:19] <cehteh> i just prelimary duped them into that dir because i wanted to talk about that with you and joel +[2008-11-13 00:09:20] <ichthyo> thats the reason behind my keeping the implementation namespaces completely apart +[2008-11-13 00:09:37] <ichthyo> cehteh: thats fine. I didn't critisize it +[2008-11-13 00:09:43] <cehteh> the _interface.c is the glue code which can stay in the implementation dir imo +[2008-11-13 00:09:48] <cehteh> heh+ +[2008-11-13 00:10:16] <cehteh> actually i wanted a include dir too .. i just didnt start because i wanted to ask you too +[2008-11-13 00:10:41] <cehteh> ah another thing i asked joel before .. i dump the tests dir out of the Doxyfile +[2008-11-13 00:10:58] <ichthyo> really? +[2008-11-13 00:11:09] <cehteh> main documentation should have less noise, the include graphs are messed up with all test dependencies +[2008-11-13 00:11:11] <cehteh> but: +[2008-11-13 00:11:24] <ichthyo> ture +[2008-11-13 00:11:26] <cehteh> we can make special doxyfiles for special purposes +[2008-11-13 00:11:26] <ichthyo> true +[2008-11-13 00:11:37] <ichthyo> yes... just wanted to propose the same +[2008-11-13 00:11:49] <cehteh> Doxyfile.all Doxyfile.small Doxyfile.browser +[2008-11-13 00:12:20] <ichthyo> because, it generates a nice list of test cases, and often I add siginficant notes just to the test cases, because they are documentation too +[2008-11-13 00:12:24] <cehteh> just the main doc shall be sufficent to get a precise overview about the project with no noise +[2008-11-13 00:12:39] |<-- pedro has left kornbluth.freenode.net (Read error: 104 (Connection reset by peer)) +[2008-11-13 00:12:47] <cehteh> yes the test cases are actually a big part of our specification .. true +[2008-11-13 00:12:53] -->| pedro (n=pedro@32.Red-83-61-132.dynamicIP.rima-tde.net) has joined #lumiera +[2008-11-13 00:13:03] <cehteh> but it add also a lot of mess +[2008-11-13 00:13:31] <cehteh> i just noticed that today while browsing the docs .. +[2008-11-13 00:13:34] <ichthyo> yes, thats fine. So we make a "small" doc set with just the core facilities, and a "complete" doc set, which includes: test cases and documentation on private members +[2008-11-13 00:13:59] <cehteh> some include graphs are half as big and much clearer when test is excluded +[2008-11-13 00:14:05] <ichthyo> so someone really needing to find out how something works in detail can use the "complete" set. +[2008-11-13 00:14:28] <cehteh> .small is another .. i have that already here privately +[2008-11-13 00:14:40] <cehteh> that turns most graphs and code inlining off +[2008-11-13 00:14:43] <ichthyo> At the end, I bet we will have almost 60% test code and 30% actual code +[2008-11-13 00:15:07] <cehteh> producing something really compact which might be printed on dead trees eventually +[2008-11-13 00:15:09] <ichthyo> or 40% +[2008-11-13 00:15:51] <cehteh> but defining some doxyfile suites wont be a problem anyways +[2008-11-13 00:16:29] <ichthyo> ok, back to the include: My proposal would be to use src/lumiera/... and namespace lumiera::... (and its children) for the public interfaces +[2008-11-13 00:16:38] * cehteh prepares a few next days +[2008-11-13 00:17:04] <cehteh> mhm thats the usual reveral operation +[2008-11-13 00:17:15] <ichthyo> ? +[2008-11-13 00:17:31] <cehteh> src/lumiera/include/ ... gets installed in /usr/include/lumiera :P +[2008-11-13 00:17:40] <cehteh> ok you dont have the include +[2008-11-13 00:17:52] <cehteh> but i rather use the name include +[2008-11-13 00:17:59] <ichthyo> so src/lumiera/** -> /usr/include/lumiera/** +[2008-11-13 00:18:03] <cehteh> yes +[2008-11-13 00:18:13] <cehteh> i dont like that much +[2008-11-13 00:18:18] <cehteh> better src/include +[2008-11-13 00:18:22] <cehteh> rationale: +[2008-11-13 00:18:22] <ichthyo> this would make the namespaces match 100% to the directories +[2008-11-13 00:18:43] <cehteh> when i look at some project i dont know and see a ./include i know that there are the interfaces +[2008-11-13 00:19:14] <cehteh> if it is ./projectname then i expect there a huge tree of code which makes the project +[2008-11-13 00:20:04] <cehteh> which is actually true for most project .. at least i dont know anyone which names its includes projectname +[2008-11-13 00:20:14] <ichthyo> thats a good argument +[2008-11-13 00:20:23] <cehteh> the projectname/include is somewhat common .. but bit bloated +[2008-11-13 00:20:48] <cehteh> so better include +[2008-11-13 00:20:49] <ichthyo> yeah, thats a good point +[2008-11-13 00:20:59] <ichthyo> so namespace lumiera:: - +[2008-11-13 00:21:04] <ichthyo> so namespace lumiera:: -> /src/include +[2008-11-13 00:21:09] <cehteh> yes +[2008-11-13 00:21:35] <ichthyo> namespace lumiera::edit -> /src/include/edit (just as an example) +[2008-11-13 00:21:45] <cehteh> nah +[2008-11-13 00:22:00] <ichthyo> while namespace proc::builder -> src/proc/builder +[2008-11-13 00:22:01] <cehteh> /src/include/edit.h would suffice +[2008-11-13 00:22:22] <cehteh> well if you want (boost does this) +[2008-11-13 00:22:25] <ichthyo> no, I mean a really sub-namespace within the interfaces +[2008-11-13 00:22:31] <cehteh> but i think keeping it flat would be ok +[2008-11-13 00:22:58] <ichthyo> I didn't say I want it. But it may happen to be necessary or just better, when the root of the interfaces gets too big +[2008-11-13 00:23:00] <cehteh> at least i dont plan to make a tree behind it +[2008-11-13 00:23:08] <ichthyo> neither do I +[2008-11-13 00:23:11] <ichthyo> but... well +[2008-11-13 00:23:14] <cehteh> i doubt that it will get very big +[2008-11-13 00:23:29] <ichthyo> I've seen enough projects to prove the contrary +[2008-11-13 00:23:37] <cehteh> even with 100 files there it will still be manageable +[2008-11-13 00:23:45] <ichthyo> and when an interface dir as 60 entries it starts to get messy +[2008-11-13 00:24:02] <cehteh> and if not .. we may reconsider that before we release +[2008-11-13 00:24:38] <cehteh> a deepber hierachy which is very sparse is much more annoying than a dir with up to 100 files +[2008-11-13 00:24:49] <ichthyo> yes, but anyway, the important point is I really want to try hard to get public interfaces and implementation code cleanly separated +[2008-11-13 00:25:31] <cehteh> for C++ absolutely .. for C i less that a *little* bit +[2008-11-13 00:25:54] <cehteh> for performance reasons and because most of the backend wont end in public interfaces anyways +[2008-11-13 00:26:27] <ichthyo> just out of curiosity: what are the performance reasons? +[2008-11-13 00:26:51] <cehteh> i have some static inline accessor functions . which need to be in the .h and need full public structure definitions which expose private details +[2008-11-13 00:27:06] <ichthyo> ah, I see +[2008-11-13 00:27:08] <cehteh> i think these will stay private +[2008-11-13 00:27:08] <ichthyo> of course +[2008-11-13 00:27:19] <cehteh> but if not i wont really care +[2008-11-13 00:27:59] <ichthyo> probably those just count as "reasonable exceptions from the rule" +[2008-11-13 00:28:06] <cehteh> yes +[2008-11-13 00:28:26] <ichthyo> and besides: also within the implementation of one layer, there are further interfaces +[2008-11-13 00:28:33] <cehteh> well C has no access protection at all .. you have to do whats allowed/documented +[2008-11-13 00:28:47] <cehteh> any improvisation counts as undefined behaviour .. point +[2008-11-13 00:28:57] <ichthyo> e.g the builder has an interface +[2008-11-13 00:29:13] <cehteh> of course +[2008-11-13 00:29:37] <cehteh> i have some deeply hidden things no one needs to know +[2008-11-13 00:29:58] <ichthyo> joelholdsworth: I've seen that you already changed some namespaces in the GUI part, is that correct, or am I mixing something up now? +[2008-11-13 00:30:02] <cehteh> but these have interfaces in the backend +[2008-11-13 00:30:17] <ichthyo> (and same for the proc) +[2008-11-13 00:31:01] <cehteh> btw: +[2008-11-13 00:31:04] <cehteh> LumieraFile file = lumiera_file_new ("filename", mode); +[2008-11-13 00:31:05] <cehteh> LumieraFrameIndex index = lumiera_frameindex_new ("indexfilename", file /*, indexing engine*/); +[2008-11-13 00:31:05] <cehteh> LumieraFrame frame = lumiera_frameindex_frame (index, 123); +[2008-11-13 00:31:25] <joelholdsworth> yes I fixed the namespaces +[2008-11-13 00:31:31] <joelholdsworth> so no more lumiera:: +[2008-11-13 00:31:33] <cehteh> ... thats the brainstorming state how to access frames +[2008-11-13 00:32:03] <ichthyo> actually, I thought we could discuss it further... and I wanted to write something down. +[2008-11-13 00:32:05] <cehteh> mhm not fully complete ... but sufficently +[2008-11-13 00:32:17] <ichthyo> But I may well tell it just informally if that's ok +[2008-11-13 00:32:30] <ichthyo> basically I see two scenarios +[2008-11-13 00:33:02] <ichthyo> I allways assume we have separate interface namespace(s) and implementation is in a different namespace then the exported interface +[2008-11-13 00:33:16] <ichthyo> so, the first scenario I'd call "libarry type" +[2008-11-13 00:33:27] <ichthyo> it means that the implementation namespace is nested +[2008-11-13 00:33:54] <ichthyo> eg namespace mylib <-- the stuff the library users include and use in their code +[2008-11-13 00:34:23] <ichthyo> namespace mylib::impl and mylib::impl::special <- the actual code making the library work +[2008-11-13 00:34:35] <ichthyo> this is the first scenario +[2008-11-13 00:34:50] <ichthyo> second scenario is rathe what I was aiming for +[2008-11-13 00:35:13] <ichthyo> namespace lumiera <-- the interfaces +[2008-11-13 00:35:45] <ichthyo> namespace proc::builder:.... <-- the implementation has a completely separate hierarchy for each subsystem +[2008-11-13 00:36:08] <ichthyo> my rationale for prefering the second aproach is: +[2008-11-13 00:36:32] <ichthyo> in our case, we are "implementation heavy". We will certainly have much more implementation code than interfaces +[2008-11-13 00:37:17] <ichthyo> we often will build more nested namespaces for the implementation, but can live with just one shallow public interface +[2008-11-13 00:38:07] <ichthyo> besides, you can just grab one directory and install it as "include", the way we discussed just +[2008-11-13 00:38:36] <ichthyo> for a real library, the situation is reversed: Often you have much code close to the interface, maybe even inline +[2008-11-13 00:38:41] <ichthyo> e.g. think at boost +[2008-11-13 00:39:14] <ichthyo> half of the code is immediately inline within the interface classes you use (I don't say I like this... but well) +[2008-11-13 00:39:34] <ichthyo> and the more technical parts are in shallow sub-namespaces below +[2008-11-13 00:40:12] <ichthyo> the good side of course is, that the implementation code doesn't need to pull in the interface, because he already sees it because of the nesting. +[2008-11-13 00:41:00] <ichthyo> but for our situation, for me, the need to pull in explicitely any interface you want to use or implement counts rather as a good thing +[2008-11-13 00:41:19] <ichthyo> because it clearly documents what non-local parts you use +[2008-11-13 00:41:43] <ichthyo> that was my rationale for keeping the implementation part in a completely separate hierarchy +[2008-11-13 00:43:17] <ichthyo> joelholdsworth: so you moved your gui code within namespace gui, is that correct? +[2008-11-13 00:43:50] <joelholdsworth> yes that's right! +[2008-11-13 00:43:59] <ichthyo> because, then the issue seems to be settled. Originally I had still more shallow trees. But your argument with the matching directory names and the consistency is an important point +[2008-11-13 00:44:29] <ichthyo> so then, I'll go ahead and move my implementationn code into a new root namespace proc +[2008-11-13 00:44:47] <joelholdsworth> great :) +[2008-11-13 00:44:48] <ichthyo> and then start cleaning up the library part +[2008-11-13 00:45:20] <ichthyo> meaning, you can expect quite some stuff moving between /src/common and /src/lib +[2008-11-13 00:45:40] <ichthyo> on the long run, probably the intention should be for "common" to disappear +[2008-11-13 00:46:08] <ichthyo> either stuff goes into namespace lumiera::, meaning it's an interface and the header should go into "src/include" +[2008-11-13 00:46:30] <ichthyo> or stuff is heavy support lib implementation stuff and thus goes below src/lib +[2008-11-13 00:47:06] <ichthyo> which brings us to another question (for my part it's the last question for today) +[2008-11-13 00:47:08] <ichthyo> :-) +[2008-11-13 00:47:18] <ichthyo> how do we organize building plugins? +[2008-11-13 00:47:27] <ichthyo> I mean, the plugins which are in-tree +[2008-11-13 00:47:55] <cehteh> plugins dir +[2008-11-13 00:48:00] <ichthyo> yes, exactly +[2008-11-13 00:48:11] <cehteh> and a tree there +[2008-11-13 00:48:22] <cehteh> plugins/video/effects +[2008-11-13 00:48:24] <ichthyo> background is: i am aming to get the build process as much rules-directed as possible +[2008-11-13 00:48:27] <ichthyo> yes +[2008-11-13 00:48:28] <cehteh> etc +[2008-11-13 00:48:54] <ichthyo> can we come up with a rule about how plugins will be built? +[2008-11-13 00:49:15] <ichthyo> because: one plugin may contain several source files and headers, but needs to be linked into one module +[2008-11-13 00:49:16] <cehteh> i am curious if plugin building with scons produces the same results as autoconfg +[2008-11-13 00:49:56] <cehteh> plugins/video/effects/foo/ ... with foobar.c foobaz.c links to foo.lum +[2008-11-13 00:50:20] <ichthyo> well ... sorry +[2008-11-13 00:50:33] <ichthyo> again the notorious namespace question +[2008-11-13 00:50:34] <cehteh> (or foo.so) +[2008-11-13 00:50:40] <cehteh> heh +[2008-11-13 00:51:08] <ichthyo> if it's a rather large plugin, e.g. a plugin providing an adapter for an external media type libarary or such stuff +[2008-11-13 00:51:16] <ichthyo> then it probably can have nested namespaces +[2008-11-13 00:51:30] <cehteh> huh? +[2008-11-13 00:51:56] <cehteh> we really dont need a 1:1 relation between dirs and namespaces +[2008-11-13 00:52:08] <ichthyo> so, then, how can I tell at what level of the tree below plugins I have to start with building one shared libarary? +[2008-11-13 00:52:32] <ichthyo> would the following rule be ok for you? +[2008-11-13 00:52:38] <cehteh> plugins/video/effects/foo/foo.c builds foo.lum and installs it in $(pkglibdir)/plugins/video/effects/foo.lum +[2008-11-13 00:52:46] <ichthyo> would the following rule be ok for you? +[2008-11-13 00:52:58] <ichthyo> start with dir src/plugins and descend +[2008-11-13 00:53:12] <ichthyo> depth-first tree search +[2008-11-13 00:53:28] <ichthyo> when you enter a directory which contains a real source file +[2008-11-13 00:53:31] <cehteh> well i just suggesting here .. for the plugin loader ist relative simple +[2008-11-13 00:53:46] <ichthyo> then build everything below it into one shared module +[2008-11-13 00:53:56] <cehteh> it just searches in the paths you give him (and doesnt descend itself) +[2008-11-13 00:54:19] <ichthyo> no, I really want to configure as few pathes as possible +[2008-11-13 00:54:23] <cehteh> if you think thats ok, then do it +[2008-11-13 00:54:29] <ichthyo> would this be ok +[2008-11-13 00:54:57] <ichthyo> the limitation is: you can't just put some isolated code file *.c *cpp in some of the root directories +[2008-11-13 00:55:17] <cehteh> thats prolly a good choice anyways +[2008-11-13 00:55:28] <cehteh> plugins shall be only at the leaves of the tree +[2008-11-13 00:55:29] <ichthyo> but the nice part is: I write a small python function and am finished with the plugins issue altogether +[2008-11-13 00:55:42] <cehteh> be careful when linking +[2008-11-13 00:56:16] <cehteh> the autoconf build included a lot libs from configure which where not needed for plugins +[2008-11-13 00:56:18] <ichthyo> the moment you add a *c or *cpp file in some new sub tree, you'll fine a <subtree-rootdirname>.lum in the corresponding bind dir +[2008-11-13 00:56:57] <ichthyo> besides, I will set up a different "build environment" for plugins +[2008-11-13 00:57:16] <ichthyo> in SCons, each build environment has a fixed set of libraries attached +[2008-11-13 00:57:28] <cehteh> you likely need specialized ones for some plugins +[2008-11-13 00:57:43] <cehteh> but you can inherit them +[2008-11-13 00:57:46] |<-- dmj726 has left kornbluth.freenode.net (Read error: 104 (Connection reset by peer)) +[2008-11-13 00:57:50] <ichthyo> currently I have two environments. The standard, and the enviromenent for the GUI code +[2008-11-13 00:58:02] <ichthyo> yes, I can inherit them +[2008-11-13 00:58:12] <ichthyo> and I match them from the directory name +[2008-11-13 00:58:14] * cehteh thinks +[2008-11-13 00:58:20] <ichthyo> the rest is fully automatic +[2008-11-13 00:58:26] -->| dmj726 (n=david@c-24-1-30-137.hsd1.il.comcast.net) has joined #lumiera +[2008-11-13 00:58:38] <cehteh> a bare plugin doesnt need any lib +[2008-11-13 00:58:42] <ichthyo> compiling, linking, switches, includes +[2008-11-13 00:59:04] <ichthyo> not even the lumiera support lib? +[2008-11-13 00:59:11] <ichthyo> liblumiera.a +[2008-11-13 00:59:12] <ichthyo> ? +[2008-11-13 00:59:29] <cehteh> nope +[2008-11-13 00:59:43] <cehteh> only if it needs somthing from that +[2008-11-13 01:00:00] <ichthyo> yes, true... but probably you can guess my goal? +[2008-11-13 01:00:01] <cehteh> -DLUMIERA_PLUGIN for compiling plugins +[2008-11-13 01:00:06] <ichthyo> thats clear +[2008-11-13 01:00:11] <ichthyo> plus libdl +[2008-11-13 01:00:24] <cehteh> no +[2008-11-13 01:00:34] <cehteh> libdl actually breaks the isolation :P +[2008-11-13 01:00:41] <ichthyo> ah, only for the part which loads the lib +[2008-11-13 01:00:51] <cehteh> the host needs libdl not the plugin +[2008-11-13 01:00:54] <ichthyo> but what is when a plugin wants to open another interface +[2008-11-13 01:01:11] <cehteh> it asks the host to do it +[2008-11-13 01:01:17] <ichthyo> doesn't it need to link against your plugin system impl? +[2008-11-13 01:01:32] <cehteh> nope .. just the interface.h +[2008-11-13 01:01:49] <ichthyo> ok +[2008-11-13 01:02:14] <cehteh> (which pull a lot cruft in currently .. ) +[2008-11-13 01:02:29] <ichthyo> but probably you understand my goal: I want to make the build as automatic and rules based as possible +[2008-11-13 01:02:49] <cehteh> interface.h defines some functions .. but these are not available for the plugin either +[2008-11-13 01:02:58] <cehteh> yes ... +[2008-11-13 01:03:09] <ichthyo> for SCons, this is already mostly the case currently, but the autotools build is a maintainance nightmare right now +[2008-11-13 01:03:24] <ichthyo> i spent already a considerable time to fix it again and again +[2008-11-13 01:03:32] <cehteh> but the nature of plugins is to extend the system .. thus follows that they will need some more specific libs in many cases +[2008-11-13 01:03:39] <ichthyo> because the paths and dependencies are so much hard wired there +[2008-11-13 01:03:52] <cehteh> huh i feel comfortable with autotools +[2008-11-13 01:04:04] <ichthyo> fine, but I fix it all the time +[2008-11-13 01:04:17] <cehteh> but i secretly hacked a bit on the prolog build system idea some time ago +[2008-11-13 01:04:35] <cehteh> maybe i do some more when i am in mood for it +[2008-11-13 01:05:19] <ichthyo> and now, after I will start using interfaces separate, there will be much more refactoreing +[2008-11-13 01:05:28] <cehteh> check_LTLIBRARIES += examplepluginc.la +[2008-11-13 01:05:28] <cehteh> examplepluginc_la_SOURCES = $(tests_srcdir)/lumiera/example_plugin.c +[2008-11-13 01:05:29] <cehteh> examplepluginc_la_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -DLUMIERA_PLUGIN -I$(top_srcdir)/src/ +[2008-11-13 01:05:29] <cehteh> examplepluginc_la_LDFLAGS = -module -avoid-version -no-undefined -rpath /dev/null +[2008-11-13 01:05:51] <cehteh> mhm the LDFLAGS .. how do you do that in scons? +[2008-11-13 01:06:10] <cehteh> you can build a normal versioned .so.1.2 lib +[2008-11-13 01:06:39] <cehteh> or moment no only .so +[2008-11-13 01:06:44] <ichthyo> for now I didn't use any special switches +[2008-11-13 01:06:59] <ichthyo> I just defined exampleplugin to be an dynamic module +[2008-11-13 01:07:10] <cehteh> sounds reasonable +[2008-11-13 01:07:41] <ichthyo> as said, in future I'll tell scons "build everything below this root dir into a dynamic module" +[2008-11-13 01:07:57] <ichthyo> and scons will derive the necessary compile and link commands +[2008-11-13 01:07:58] <cehteh> first autotools stuff linked xlibs and all crap with the plugin that was a bug +[2008-11-13 01:08:29] <ichthyo> as said, I will make a new third build environment for plugins +[2008-11-13 01:08:41] <ichthyo> currently I have one env for the core and one for the GUI part +[2008-11-13 01:08:45] <cehteh> -rwxrwx--- 1 ct ct 23K Nov 7 09:28 examplepluginc.so +[2008-11-13 01:08:53] <ichthyo> yes +[2008-11-13 01:08:59] <cehteh> if its around that size its prolly ok +[2008-11-13 01:09:39] <ichthyo> and anyway, we can adjust the compiler/linker switches in SCons for each build environment +[2008-11-13 01:09:47] <ichthyo> of course with inheritance +[2008-11-13 01:10:22] <ichthyo> well.. fine... +[2008-11-13 01:10:32] <ichthyo> ah, another little issue +[2008-11-13 01:10:42] <ichthyo> we have two tools directories currently +[2008-11-13 01:10:53] <ichthyo> one as "src/tool" +[2008-11-13 01:10:57] <ichthyo> and another as +[2008-11-13 01:11:12] <ichthyo> just "admin" +[2008-11-13 01:11:29] <ichthyo> within admin, there is the icon rendering and the vgsuppression +[2008-11-13 01:12:01] <ichthyo> the idea was, that all this special support tools would be in src/tool +[2008-11-13 01:12:05] <cehteh> LUMIERA_PLUGIN_PATH=.libs ./test-interfaces plugin_examplepluginc_nested +[2008-11-13 01:12:17] <cehteh> .. dont forget to set the PLUGIN_PATH for tests +[2008-11-13 01:12:52] <ichthyo> huh? +[2008-11-13 01:12:52] <cehteh> admin are administrative scripts .. tools shall be moved into src/tool +[2008-11-13 01:13:00] <ichthyo> ok +[2008-11-13 01:13:07] <ichthyo> so joelholdsworth: +[2008-11-13 01:13:29] <ichthyo> would it be ok for you to move the icon building executable into src/tool ? +[2008-11-13 01:13:41] <ichthyo> and probably vgsuppression too? +[2008-11-13 01:13:58] <cehteh> dunno .. leave that there +[2008-11-13 01:14:00] <ichthyo> so the build process doesn't need to build anything in admin +[2008-11-13 01:14:12] <cehteh> because the vgsuppression.sh is a administrative script :P +[2008-11-13 01:14:17] <cehteh> heh +[2008-11-13 01:14:29] <ichthyo> but vgsuppression.c is built like a tool +[2008-11-13 01:14:33] <ichthyo> and the iconrenderer too +[2008-11-13 01:14:37] <cehteh> not even that +[2008-11-13 01:14:47] <cehteh> just dont build it .. its unfinished code +[2008-11-13 01:15:03] <ichthyo> ok.. then I stop building it with scons too +[2008-11-13 01:15:05] <cehteh> vgsuppression needs some maintenance +[2008-11-13 01:15:13] <cehteh> yes thats ok for now +[2008-11-13 01:15:25] <ichthyo> but the rsvg-convert.c +[2008-11-13 01:15:29] <joelholdsworth> yes that's ok! +[2008-11-13 01:15:39] <joelholdsworth> that really is just a tool +[2008-11-13 01:15:43] <ichthyo> that is built lilke a tool, so I'd like to move it into src/tool +[2008-11-13 01:16:14] <cehteh> mhm or move vgsuppression.c over to tools +[2008-11-13 01:16:15] <joelholdsworth> it's the standard rsvg utility with some options added +[2008-11-13 01:16:23] <ichthyo> and then I can ditch the admin/SConscript alltogether +[2008-11-13 01:16:32] <cehteh> i dont care for it anytime soon .. so its oh when you move it +[2008-11-13 01:16:58] <ichthyo> a propos: joelholdsworth: I've built some additional directory checks into the accompaning python script +[2008-11-13 01:17:29] <ichthyo> you have probably noticed that I call directly from the SConstruct into your python script to invoke the icon rendering +[2008-11-13 01:17:30] <joelholdsworth> ok +[2008-11-13 01:17:50] <ichthyo> and I built some additional cheks and move non-empty dirs away as "*.bak" +[2008-11-13 01:17:50] <joelholdsworth> I hadn't actually looked +[2008-11-13 01:17:53] <cehteh> -rwxrwx--- 1 ct ct 9.2K Nov 13 01:17 examplepluginc.so +[2008-11-13 01:17:56] <cehteh> hah stripped +[2008-11-13 01:17:59] <joelholdsworth> but yes that makes perfect snese +[2008-11-13 01:18:14] <ichthyo> the icon rendering works well in the SCons build too +[2008-11-13 01:18:31] <joelholdsworth> actually it is supposed to overwrite any prior renderings +[2008-11-13 01:18:39] <ichthyo> ah? +[2008-11-13 01:19:00] <joelholdsworth> so if you change the SVG the PNGs get created fresh +[2008-11-13 01:19:01] <ichthyo> so I could chane it and just do a recursive remove if the target dir exists? +[2008-11-13 01:19:10] <joelholdsworth> yes +[2008-11-13 01:19:31] <ichthyo> fine. But anyway, I'll leave in the move-away in case the target name exists as file +[2008-11-13 01:19:32] <joelholdsworth> like obj files - it's supposed to just overwrite any previous bits +[2008-11-13 01:19:50] <joelholdsworth> well the targets are just build termoraries +[2008-11-13 01:20:01] <ichthyo> ok +[2008-11-13 01:20:52] <ichthyo> yes, SCons uses a md5 or sha hash on the source files by default. So if you change the contents of a SVG, it figures out all PNGs which depend on it and rebuilds them +[2008-11-13 01:21:21] <joelholdsworth> clever! +[2008-11-13 01:22:25] <ichthyo> ok. then regariding the build system, the only bit of cleanup is the tests +[2008-11-13 01:22:33] <joelholdsworth> anyway... time for bed +[2008-11-13 01:22:33] -->| nick4 (n=fffeop@adsl205-29.kln.forthnet.gr) has joined #lumiera +[2008-11-13 01:22:46] <ichthyo> cehteh: we talked about it when I visited you +[2008-11-13 01:23:02] <ichthyo> it's not urgent right now +[2008-11-13 01:23:14] <ichthyo> but.. as we agreed at that time +[2008-11-13 01:23:39] <ichthyo> we should make a separate tree for the test-support code, maybe in a subdirectory of /tests +[2008-11-13 01:24:01] <ichthyo> and this tree of course reflects the main tree. That was the idea if I recall correct +[2008-11-13 01:24:16] |<-- joelholdsworth has left kornbluth.freenode.net (Remote closed the connection) +[2008-11-13 01:24:38] |<-- kaarna has left kornbluth.freenode.net ("Ex-Chat") +[2008-11-13 01:25:02] <ichthyo> we could move your /tests/test.h in the root of this test-support tree +[2008-11-13 01:25:11] <cehteh> ichthyo: yes .. i know +[2008-11-13 01:25:13] <cehteh> pending +[2008-11-13 01:25:18] <ichthyo> yes, not urgent +[2008-11-13 01:25:26] <ichthyo> works as-is for now +[2008-11-13 01:25:45] <ichthyo> I have some similar cleanup for my part +[2008-11-13 01:25:58] <cehteh> you might noticed that i placed new code in proper order .. just the old cruft need to be cleaned up +[2008-11-13 01:26:09] <ichthyo> yes, thanks, I noted it +[2008-11-13 01:26:34] <ichthyo> resulting in most of the special treatment functions in tests/SConscript to disappear :-) +[2008-11-13 01:27:02] <ichthyo> even the plugin test is much more simple right now :) +[2008-11-13 01:27:14] <cehteh> ah +[2008-11-13 01:27:19] <cehteh> back to before +[2008-11-13 01:27:24] <ichthyo> btw: do we need "test plugins"? +[2008-11-13 01:27:32] <cehteh> we settled with src/include now +[2008-11-13 01:27:46] <cehteh> that means src/lumiera remains for the main +[2008-11-13 01:27:52] <ichthyo> i.e. something similar to the src/plugin subdirs we discussed just a moment ago +[2008-11-13 01:28:04] <ichthyo> yes src/lumiera remains for main and startup +[2008-11-13 01:28:30] <ichthyo> btw: do we need "test plugins"? +[2008-11-13 01:28:34] <cehteh> main and startup and config and interface/plugin +[2008-11-13 01:28:41] <cehteh> so to test-plugins +[2008-11-13 01:28:53] <cehteh> you mean some which get installed? +[2008-11-13 01:29:09] <ichthyo> no, just plugins which are needed only from within tests +[2008-11-13 01:29:18] <cehteh> yes we need them +[2008-11-13 01:29:19] <ichthyo> i.e. /tests/plugin +[2008-11-13 01:29:39] <ichthyo> and everything below will be built exactly similar as everything below /src/plugin +[2008-11-13 01:29:44] <cehteh> i currently build them in place example_plugin.c +[2008-11-13 01:29:46] <cehteh> etc +[2008-11-13 01:29:54] <ichthyo> just it will be only used for test runs +[2008-11-13 01:29:56] <ichthyo> yes +[2008-11-13 01:30:06] <cehteh> if you want we can move them to tests/plugins someday +[2008-11-13 01:30:18] <ichthyo> so example_plugin.c would move over into /tests/plugin/example +[2008-11-13 01:30:27] <cehteh> i also thought about test plugins which get installed +[2008-11-13 01:30:28] <ichthyo> or something similar one day +[2008-11-13 01:30:36] <cehteh> to test the installation +[2008-11-13 01:30:37] <ichthyo> yes +[2008-11-13 01:30:48] <ichthyo> but I think those should go below src/plugin +[2008-11-13 01:31:02] <ichthyo> or do you consider them to be part of the test system? +[2008-11-13 01:31:05] <cehteh> maybe the example_plugin can just move to be installed +[2008-11-13 01:31:24] <cehteh> and in production it will just be blacklisted by the plugindb later +[2008-11-13 01:31:41] <cehteh> then not +[2008-11-13 01:31:48] <ichthyo> we could also use diagnostics plugins +[2008-11-13 01:31:58] <ichthyo> which are similarily blacklisted +[2008-11-13 01:32:18] <cehteh> another think shall plugins override existing interfaces or barf out? +[2008-11-13 01:32:30] <ichthyo> but which we could use to get detailed diagnostics info in case we are investigating problems in a user setup.... +[2008-11-13 01:32:33] <cehteh> both has pros and cons +[2008-11-13 01:32:46] <cehteh> both can easily circumvented with some effort +[2008-11-13 01:33:26] <cehteh> src/plugins/maintenance +[2008-11-13 01:33:27] <ichthyo> ehm, I don't fully understand the situation. Could you explain it? Lets say: we have interface A +[2008-11-13 01:33:41] <ichthyo> now a plugin wants to provide an implementation of interface A +[2008-11-13 01:34:03] <cehteh> in a mail to you i explained that i would like mockup plugins which override core functionality +[2008-11-13 01:34:04] <ichthyo> i.e. the client of this plugin would use interface A to access it +[2008-11-13 01:34:42] <ichthyo> is this correct? +[2008-11-13 01:34:49] <cehteh> yes +[2008-11-13 01:35:07] <ichthyo> so, wouldn't the mockup just be another implementation of interface A ? +[2008-11-13 01:35:09] <cehteh> well you query interface+implementation tuples +[2008-11-13 01:35:16] <cehteh> yes +[2008-11-13 01:35:35] <cehteh> you cant just open a interface .. you open a implementation of an interface +[2008-11-13 01:35:49] <ichthyo> yes, but you use this (interface+implemntation) just for opening the interface, or? +[2008-11-13 01:36:05] <cehteh> so mocplugin can provide blurfoo +[2008-11-13 01:36:15] <cehteh> even if blurfoo.lum provides it too +[2008-11-13 01:36:26] <cehteh> yes +[2008-11-13 01:36:50] <raffa> (good night!) +[2008-11-13 01:37:03] <ichthyo> raffa: good night +[2008-11-13 01:37:06] <cehteh> i tend to barf if anything wants to provide the same implementation a 2nd time +[2008-11-13 01:37:11] <cehteh> n8 raffa +[2008-11-13 01:37:12] <ichthyo> raffa: wasnt really interesting for you today +[2008-11-13 01:37:16] <ichthyo> much tech talk +[2008-11-13 01:37:33] <raffa> Very promising. :-) +[2008-11-13 01:37:39] <ichthyo> originally, we said we wait for raffa to discuss the logo +[2008-11-13 01:37:51] <cehteh> you missed the logo discussion :P +[2008-11-13 01:37:51] <ichthyo> but we were in the middle of tech stuff +[2008-11-13 01:38:03] <cehteh> hehe +[2008-11-13 01:38:24] <ichthyo> raffa: basically we said: the logo contest seems to be running just fine +[2008-11-13 01:38:34] <raffa> I agree. :-) +[2008-11-13 01:39:03] <raffa> I see if I can stimulate more comments +[2008-11-13 01:39:14] <ichthyo> yes, maybe a good idea +[2008-11-13 01:39:19] <cehteh> ichthyo: well . i think we can stop it here .. i just come up with a solution +[2008-11-13 01:39:30] <cehteh> raffa: yes thats a good plan +[2008-11-13 01:39:51] <ichthyo> cehteh: well .. I rather wanted to understand the problem, because it interests me +[2008-11-13 01:40:26] <raffa> Ok. I go to bed and try to dream something good for comments. ;-) +[2008-11-13 01:40:27] <ichthyo> raffa: well then .. its already late. good night, nice dreams... +[2008-11-13 01:40:30] <raffa> Bye! +[2008-11-13 01:40:32] <cehteh> imagine 2 .lum modules implement and export exactly the same thing (or at least announce it as exactly the same thing) +[2008-11-13 01:40:33] <ichthyo> bye +[2008-11-13 01:40:49] |<-- raffa has left kornbluth.freenode.net (Remote closed the connection) +[2008-11-13 01:40:50] |<-- pedro has left kornbluth.freenode.net (Read error: 104 (Connection reset by peer)) +[2008-11-13 01:40:51] <cehteh> ... but i just solved it +[2008-11-13 01:40:57] <ichthyo> I just try to relate it to the similar situation with classes +[2008-11-13 01:41:00] -->| pedro (n=pedro@32.Red-83-61-132.dynamicIP.rima-tde.net) has joined #lumiera +[2008-11-13 01:41:00] <ichthyo> let me explain +[2008-11-13 01:41:12] <cehteh> nah classes are different +[2008-11-13 01:41:16] <ichthyo> you have an interface and now you have two subclasses +[2008-11-13 01:41:27] <ichthyo> no they aren't. really different +[2008-11-13 01:41:43] <cehteh> subtle different +[2008-11-13 01:41:47] <ichthyo> so both subclasses claim to implement the contract which is defined by the interface +[2008-11-13 01:42:04] <ichthyo> now I have a factory which instantiates one of those subclasses +[2008-11-13 01:42:14] <ichthyo> or the other or both +[2008-11-13 01:42:18] <ichthyo> same situation +[2008-11-13 01:42:22] <cehteh> yes +[2008-11-13 01:42:44] <cehteh> err no +[2008-11-13 01:42:48] <cehteh> you have only one class +[2008-11-13 01:42:49] <ichthyo> the client of the whole apparatus just wants an "instance" of the contract defined by the interface +[2008-11-13 01:43:01] <ichthyo> and doesn't care which one it is +[2008-11-13 01:43:14] <cehteh> you didnt understand my prioblem because it doesnt exist in C +[2008-11-13 01:43:15] <cehteh> C++ +[2008-11-13 01:43:33] <cehteh> class foo {...}; +[2008-11-13 01:43:43] <cehteh> foo this_is_it; +[2008-11-13 01:43:44] <cehteh> foo this_is_it; +[2008-11-13 01:43:57] <cehteh> ... redefinition of the same instance is my problem +[2008-11-13 01:44:08] <cehteh> not even exactly +[2008-11-13 01:44:37] <ichthyo> ah, in C++ the two would either be rejected by the compiler, or they would be allowed if in different scopes +[2008-11-13 01:44:43] <cehteh> even worse the 2nd can say the it is a identical instance but it isnt +[2008-11-13 01:45:07] <cehteh> thats a good way to inject moc objects +[2008-11-13 01:45:09] <ichthyo> called "slicing" in C++ +[2008-11-13 01:45:15] <cehteh> so now my solution: +[2008-11-13 01:45:51] <cehteh> eh +[2008-11-13 01:46:01] <cehteh> *thinking* +[2008-11-13 01:46:03] <cehteh> yes +[2008-11-13 01:46:29] <cehteh> minor versions must differ, biggier minor will win +[2008-11-13 01:46:41] <cehteh> (newer one for certain) +[2008-11-13 01:46:53] <cehteh> thats intended +[2008-11-13 01:47:11] <cehteh> now a mock object might claim it is some *exact* same .. +[2008-11-13 01:47:57] <cehteh> that will be rejected .. so a mock object must do some heavy work to throw out the old one from the nest +[2008-11-13 01:48:10] <cehteh> thats ok for testing purposes +[2008-11-13 01:48:12] <ichthyo> well... at some point you will be able to tell the loader: load this module for use as interface A +[2008-11-13 01:48:20] <cehteh> nah +[2008-11-13 01:48:24] <cehteh> well yes +[2008-11-13 01:48:37] <cehteh> but you forget that modules != interfaces +[2008-11-13 01:49:07] <ichthyo> I understand the modules as being like a "subclass" of the interface +[2008-11-13 01:49:18] <cehteh> nope wrong +[2008-11-13 01:49:23] <ichthyo> meaning, each of them can "stand-in" for the interface +[2008-11-13 01:49:41] <cehteh> modules are a collection of interfaces +[2008-11-13 01:49:47] <cehteh> completely unrelated +[2008-11-13 01:49:54] <ichthyo> huh? +[2008-11-13 01:50:19] <cehteh> a module could provide a video effect and a gui plugin and a fancy format exporter at once +[2008-11-13 01:50:36] <cehteh> no one shall do that ... but modules are just collections of interfaces +[2008-11-13 01:50:37] <ichthyo> which would be 3 interfaces +[2008-11-13 01:50:41] <cehteh> yes +[2008-11-13 01:51:00] <ichthyo> but what then is the term for the implementation of an interface? +[2008-11-13 01:51:08] <ichthyo> because, at an extension point +[2008-11-13 01:51:21] <ichthyo> you just want to tell: this is the interface to be used at this extension point +[2008-11-13 01:51:28] <cehteh> but the module is just a bunch of interfaces .. and module itself doesnt give them a purpose +[2008-11-13 01:51:46] <cehteh> yes +[2008-11-13 01:52:09] <ichthyo> thats clear. Something like the feature bundle could give it a spefici purpose, or something similar, which itself is loaded as plugin +[2008-11-13 01:52:23] <cehteh> for example +[2008-11-13 01:52:46] <cehteh> modules are just the bucket .. it is pretty unspecified what you put in +[2008-11-13 01:53:01] <ichthyo> yes thats fine, to start with +[2008-11-13 01:53:10] <cehteh> (except that must be interfaces ... ) +[2008-11-13 01:53:23] <ichthyo> anything more specific can be built on top of such a system +[2008-11-13 01:53:35] <ichthyo> for example take the video effect +[2008-11-13 01:53:56] <ichthyo> at some point, you define an interface "VideoEffect" +[2008-11-13 01:54:27] <ichthyo> probably this interface isn't loaded from a module, rather it's defined from the core, but this doesn't matter for the point in question here +[2008-11-13 01:55:02] <cehteh> huh +[2008-11-13 01:55:04] <ichthyo> now, everything you can ever use as an implementation of this "VideoEffect" must use this interface, +[2008-11-13 01:55:28] <ichthyo> because the client of the interface (which in this case is the core which uses this interface for rendering) +[2008-11-13 01:55:37] <cehteh> yes +[2008-11-13 01:55:45] <ichthyo> doen't care about the implementation and doesn't know any differences +[2008-11-13 01:55:56] <cehteh> probably this interface isn't loaded from a module << interfaced themself are never loaded +[2008-11-13 01:56:08] <ichthyo> I know, they are just opened +[2008-11-13 01:56:14] <cehteh> the just exist as header at compiletime +[2008-11-13 01:56:25] <cehteh> you open implementations thereof +[2008-11-13 01:56:33] <ichthyo> ah, still better, yes +[2008-11-13 01:56:47] <cehteh> hehe i had some struggles to get the names right +[2008-11-13 01:57:04] <ichthyo> and each of these implementations is just like a class instance +[2008-11-13 01:57:24] <cehteh> yes +[2008-11-13 01:57:29] <cehteh> they can map 1:1 +[2008-11-13 01:57:33] <ichthyo> unless you created it yourself, you never can tell its subclass (unless there is some sort of introspection mechanism) +[2008-11-13 01:58:33] <cehteh> well since every function has a uuid for each implementations they have quite strong identity +[2008-11-13 01:58:43] <cehteh> you cant tell if its a subclass .. +[2008-11-13 01:58:49] <cehteh> but you know when it differs +[2008-11-13 01:59:06] <cehteh> thats even stronger than C++ +[2008-11-13 01:59:11] <ichthyo> now all you need is some "backdoor" wich is only used for the mock tests. By use of this backdoor-function, you tell the plugin loader/plugindb: and now replace temporarily the instance XYZ by the mock instance ABC +[2008-11-13 01:59:40] <ichthyo> thats the way I'd propose to handle this problem +[2008-11-13 02:00:05] <ichthyo> so a test can "push aside" what is loaded temporarily and place a mock in its place +[2008-11-13 02:00:18] <cehteh> yes .. so my backdoor is as described +[2008-11-13 02:00:30] <ichthyo> and before finishing, the test should remove the mock, and the original instance will be uncovered +[2008-11-13 02:01:07] <cehteh> heh .. thinking about my solution .. i have this backdoor already very well hidden :) +[2008-11-13 02:01:34] * ichthyo has almost the same in place on class level for some tests +[2008-11-13 02:01:39] <cehteh> -DLUMIERA_MOCKPLUGIN :) +[2008-11-13 02:02:09] <ichthyo> wouldn't it be still better to make it completely dynamic, i.e from the running test? +[2008-11-13 02:02:22] <cehteh> nope +[2008-11-13 02:02:36] <ichthyo> the test just loads a mock impl he knows, and places it temporarily to shaddow the real thing +[2008-11-13 02:03:17] <cehteh> well maybe .. but what happens when the real thing was in use and keeps some state? +[2008-11-13 02:03:40] <cehteh> i think all-or-nothing is the way to go +[2008-11-13 02:03:53] <ichthyo> any user which at this point already has a pointer to the real thing will just continue to use it +[2008-11-13 02:04:14] <cehteh> yes but when its state is global? +[2008-11-13 02:04:34] <cehteh> shared between all instances +[2008-11-13 02:04:49] <cehteh> dont ask me about a concrete example ... but i can imagine this +[2008-11-13 02:04:59] <ichthyo> then the mock is just another one to use this shared data +[2008-11-13 02:05:20] <cehteh> but its hidden in the original plugin +[2008-11-13 02:05:26] <cehteh> inaccessible for the moc +[2008-11-13 02:06:22] <cehteh> at least not trivially .. and you dont want nontrivial mockups (at least not more than the usually are) +[2008-11-13 02:06:55] <cehteh> well the mockup could proxy to a real one +[2008-11-13 02:07:08] <cehteh> thats prolly much better +[2008-11-13 02:07:08] <ichthyo> and you dont have a class hierarchy to reslove the problem. the only solution for this extra problem is for the mock to know the real thing +[2008-11-13 02:07:28] <ichthyo> similar to a subclass which can use protected data from the base +[2008-11-13 02:07:38] <cehteh> anyways .. this is not actual yet +[2008-11-13 02:07:52] <ichthyo> or static protected data to make it analogus to the global data in the example +[2008-11-13 02:07:54] <cehteh> and i go to bed ... :P +[2008-11-13 02:08:02] <ichthyo> I prolly too +[2008-11-13 02:08:06] <ichthyo> as I told you +[2008-11-13 02:08:16] <ichthyo> I have a real heavy CD deadline next week +[2008-11-13 02:08:27] <cehteh> .. taxes .. urgs :P +[2008-11-13 02:08:34] <ichthyo> I am working almost all the time and almost no sleep +[2008-11-13 02:08:36] |<-- nick4 has left kornbluth.freenode.net () +[2008-11-13 02:08:45] <daitheflu_> good night guys, don't forget to sleep a bit :p +[2008-11-13 02:08:52] <ichthyo> well... I'll survive it +[2008-11-13 02:08:55] <cehteh> ok .. n8 +[2008-11-13 02:09:02] |<-- daitheflu_ has left kornbluth.freenode.net ("Ex-Chat") +[2008-11-13 02:09:19] * cehteh works on the taxes next days and then backend +[2008-11-13 02:09:22] <ichthyo> and after that, I'll make my proposal for the main on a dev branch +[2008-11-13 02:09:39] <cehteh> the main() and startup stuff can be fleshed out when we have both more time +[2008-11-13 02:09:43] <cehteh> yes +[2008-11-13 02:09:56] <cehteh> look at mine .. i guess you wont change that much +[2008-11-13 02:09:56] <ichthyo> yeah, I've seen you started on filehandles and similar, right? +[2008-11-13 02:10:23] <cehteh> actually i started there months ago .. that lied uncommited here becasue of the config system +[2008-11-13 02:10:24] <ichthyo> btw: maybe you noticed it +[2008-11-13 02:10:29] <cehteh> plugin loader +[2008-11-13 02:10:35] <cehteh> builddrone +[2008-11-13 02:10:39] <cehteh> etcetc ... +[2008-11-13 02:10:48] <ichthyo> I made a draft implementation for the memory allocation of render nodes +[2008-11-13 02:11:01] <ichthyo> and leave the rest for later or still better for someone else +[2008-11-13 02:11:09] <cehteh> i seen it but didnt looked into the details +[2008-11-13 02:11:20] <ichthyo> I wrote percy tigalo a mail if he is interested to work on it +[2008-11-13 02:11:27] <cehteh> how about persistent rendernodes in the backend? +[2008-11-13 02:11:53] <ichthyo> what doesn this mean? +[2008-11-13 02:11:56] <cehteh> (another time :P) +[2008-11-13 02:12:21] <ichthyo> ok.. well let's have some sleep +[2008-11-13 02:12:36] <cehteh> they store state .. let this state be maped to files by the backend +[2008-11-13 02:12:55] <ichthyo> yes, of course. I think we already discussed this point +[2008-11-13 02:13:04] <cehteh> but makes no much sense this is short lived data which can be recalculated +[2008-11-13 02:13:16] <ichthyo> and said: we use data frames which will be handled similarily by the backend +[2008-11-13 02:13:29] <ichthyo> its not so much of importance for the rendernodes +[2008-11-13 02:13:29] <cehteh> yes +[2008-11-13 02:13:36] <cehteh> yes +[2008-11-13 02:13:38] <ichthyo> but its very important for the automation +[2008-11-13 02:13:54] <cehteh> no i mean your (actually non-state) rendernode allocation :P +[2008-11-13 02:14:13] <cehteh> forget it :) +[2008-11-13 02:14:25] <ichthyo> they have a per-invocation state +[2008-11-13 02:14:32] <cehteh> i see that i deliver some frames next +[2008-11-13 02:14:46] <cehteh> did you seen my first inkscape drawing :P +[2008-11-13 02:14:52] <ichthyo> oh +[2008-11-13 02:14:56] <ichthyo> yes, of course +[2008-11-13 02:15:00] <ichthyo> looks really nice +[2008-11-13 02:15:07] <ichthyo> and the contents are promising... +[2008-11-13 02:15:27] <ichthyo> was it difficult to get aquainted to inkscape? +[2008-11-13 02:15:36] <cehteh> i ditched the writebuffer idea .. in favor of writing directly into the file area +[2008-11-13 02:15:44] <cehteh> not that much +[2008-11-13 02:16:10] <ichthyo> I found the status line of inkscape quite helpful +[2008-11-13 02:16:11] <cehteh> the upper half is working btw .. indexing soon +[2008-11-13 02:16:26] <cehteh> yes my proposal for lumiera too .. +[2008-11-13 02:16:29] <ichthyo> I mean, it tells you what will happen if you hover +[2008-11-13 02:16:35] <ichthyo> yes, I know. I support this +[2008-11-13 02:16:43] <ichthyo> we'll convince Joel, I think +[2008-11-13 02:16:59] <cehteh> xfig and other graphic programs have that too +[2008-11-13 02:17:27] <ichthyo> wir brauchen keine Büroklammern, gell... +[2008-11-13 02:17:31] <ichthyo> :-) +[2008-11-13 02:17:43] <cehteh> about indexing engines .. i guess that needs some turnarounds with the proc +[2008-11-13 02:17:54] <ichthyo> prolly yes +[2008-11-13 02:19:16] <ichthyo> btw: probably I will leave the impl. of the render nodes in the half-way done state they are now and move up to the interface towards GUI, because I get the feeling this will boost up matters at this edge much more if I can deliver somethinc concrete for joel there +[2008-11-13 02:19:26] <cehteh> maybe i need a proc mockup there :P single avdecoder rendernode +[2008-11-13 02:19:40] <ichthyo> prolly yes +[2008-11-13 02:19:45] <cehteh> yes i have plenty to do +[2008-11-13 02:19:47] <ichthyo> good Idea +[2008-11-13 02:19:57] <ichthyo> I'll provide you such a beast +[2008-11-13 02:20:16] <cehteh> even if the file/maping/indexing stuff works .. scheduler, profiler, threads ... +[2008-11-13 02:20:22] <cehteh> shitloads of things +[2008-11-13 02:20:30] <ichthyo> same here +[2008-11-13 02:20:38] <ichthyo> Lumiera is a huge dry sponge +[2008-11-13 02:20:46] <cehteh> hehe +[2008-11-13 02:20:51] <cehteh> ok really off to bed now +[2008-11-13 02:20:57] <ichthyo> ok... +[2008-11-13 02:21:02] <ichthyo> sleep well! +[2008-11-13 02:21:02] <cehteh> n8 +[2008-11-13 02:21:05] <ichthyo> n8 +[2008-11-13 02:22:35] [QUIT] Disconnected from irc://kornbluth.freenode.net/ (irc://kornbluth.freenode.net/). [[Reconnect][Reconnect to kornbluth.freenode.net][reconnect]] + From 8e4541251372a525e38955d3138e350af75e5949 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 1 Dec 2011 22:48:30 +0100 Subject: [PATCH 284/496] supplementing a transcript from the IRC log (11/2008) --- doc/devel/meeting_summary/2008-11-12.txt | 1377 +++++++--------------- doc/devel/meeting_summary/index.txt | 11 +- 2 files changed, 466 insertions(+), 922 deletions(-) diff --git a/doc/devel/meeting_summary/2008-11-12.txt b/doc/devel/meeting_summary/2008-11-12.txt index ebc6ad06e..c33fc3c74 100644 --- a/doc/devel/meeting_summary/2008-11-12.txt +++ b/doc/devel/meeting_summary/2008-11-12.txt @@ -16,13 +16,6 @@ _Summary and transcript supplemented by Ichthyo 11/2011_ - * Platform and lib dependency status - * Discussion about foreseeable problems with Gui Plugins - * Include dir and namespaces - * build system, plugin tree - - - Platform and lib dependency status ---------------------------------- Right away, there is agreement between all 3 core devs that the Lumiera project @@ -37,220 +30,167 @@ to use a moderately recent Ubuntu, the intention is to use Debian/stable as refe Conclusion ~~~~~~~~~~ - * use Debian/stable (currently Lenny) as reference distribution + * use *Debian/stable* (currently Lenny) as reference distribution * care for backports of any newer libraries required * use VMs to verify the build on a moderate selection of more recent distros -Topic 1 -------- -Summary what is discussed +Problems with GUI Plugins +------------------------- +The core devs are aware of some challenging problems regarding plug-ins for the +GUI and effect plug-ins requiring an specific GUI control or option pane. Still the more +since the intention is for Lumiera to adapt _existing plug-in systems_ rather then +rolling yet-another new plug-in standard no one else cares to support. Initially, +specific Lumiera plug-ins will be provided only for some very basic modular functionality, +eg. fader, panner, blur. -_cehteh_ points out that... +_joelholdsworth_ points out that configurability can't replace real GUI and workflow design. +He quotes from a recent Blender GUI discussion -_joelholdsworth_ adds.... +[quote, the Belnder UI reviews] +____________________________________________________________________ +Lastly, I’d like to address another misconception, this time about customizability. +There has been a notion that the solution to most of the UI problems can be solved with added customizability. +The notion goes that if the UI is inefficient, the user can simply customize it herself to suit her needs. +This claim is wrong for several reasons: + +- It is impossible to customize something you have not fully comprehended yet, + so it in no way helps the learning process. +- It makes an application less predictable, because the user cannot rely on the application + to always act the same. +- It takes focus away from the users content, and over to managing the application itself, + which is what we wanted to avoid in the first place. +- It undermines the portability of Blender, because you cannot rely on Blender + functioning the same way across different systems. +- Customizability does not equate to flexibility. +- 99% of Blenders user will use the default setup anyway. + +This is not to say that customizability is always bad - having the ability to change the hotkeys +from the defaults to match another 3D application such as Maya or Softimage XSI can make it easier +for users of those applications to adapt to Blender. +____________________________________________________________________ + +Consequently, Joel wants to try just doing the UI _right_, then add a little bit of customization back in here and there. +Additionally, agreement is to have a kind-of ``perspective switcher'' (like in Eclipse), so saving different +panel layouts would be covered easily -Conclusion -~~~~~~~~~~ - - * do this - * do that - - - - -'''' - - -.-- Discussion of details -- +.-- Discussion about foreseeable problems with GUI Plugins -- [caption="☉Transcript☉ "] ---------------------------- -12:51 cehteh key/value pairs -12:51 gmerlin not sure about that -12:51 cehteh i am pretty sure that i want that in lumiera +[2008-11-12 21:53:50] <ichthyo> I thought we could just discuss the problem informally + and share some thoughts what we think is most critical +[2008-11-12 22:06:01] <ichthyo> there are plugins which don't provide a GUI, and some plugin systems allow + for a standard method for creating plugin guis -( shortened and tidied transcript of some interesting detailed discussions ) ----------------------------- - -[2008-11-12 21:49:18] <cehteh> Gui for Plugins, Architecture/Design .. the tough one :P -[2008-11-12 21:49:54] <cehteh> ichthyo: i think you rated my pragmatic approach bit high past days :) .. i just wanted to get somethnig going -[2008-11-12 21:50:19] <cehteh> as in "we have git we can refactor anytime, but make it work first" -[2008-11-12 21:50:20] <ichthyo> cehteh: no, I understand that -[2008-11-12 21:50:38] <joelholdsworth> right... I can focus a bit more -[2008-11-12 21:50:40] <ichthyo> and btw, nothing personal -[2008-11-12 21:50:44] <cehteh> hehe -[2008-11-12 21:51:19] <ichthyo> joelholdsworth: you told me your work job goes off the scale at the moment... -[2008-11-12 21:51:28] <cehteh> note: we had some mail coversations past days in german with made some things more clear to us :) -[2008-11-12 21:51:32] <joelholdsworth> yeah a little bit -[2008-11-12 21:51:58] <joelholdsworth> I had to prepare a talk with very short notice, on top of everything else I'm doing -[2008-11-12 21:52:28] <joelholdsworth> so I've been pretty solid for last week -[2008-11-12 21:52:28] <ichthyo> it's sort of the same for me. At job we had a hell of a high transaction load -[2008-11-12 21:52:34] <joelholdsworth> things should calm down aftern tomorrow -[2008-11-12 21:52:52] <joelholdsworth> :) -[2008-11-12 21:52:54] <joelholdsworth> such is life -[2008-11-12 21:52:56] <joelholdsworth> anyway... what next? -[2008-11-12 21:53:03] <ichthyo> On top of everything, I got a CD deadline next Tuedsay -[2008-11-12 21:53:11] <cehteh> Gui for Plugins, Architecture/Design .... still -[2008-11-12 21:53:17] <ichthyo> but after that, I should have more time -[2008-11-12 21:53:25] <ichthyo> yes -[2008-11-12 21:53:50] <ichthyo> I thought we could just discuss the problem informally and share some thoughts what we think is most critical -[2008-11-12 21:54:01] <cehteh> ichthyo: so i am more about how to deploy the internal components .. means who of us does the 'main()' -[2008-11-12 21:54:20] <joelholdsworth> not me - my main is only a temporary hack -[2008-11-12 21:54:24] <cehteh> not about architecture, about get it rolling -[2008-11-12 21:54:30] <joelholdsworth> that's why my front end has a stupid name -[2008-11-12 21:54:56] <cehteh> yes i made one too in my devel branch .. but ichthyo has some other valid plans -[2008-11-12 21:55:30] <ichthyo> probably I could just collect together my parts-of-a-main in another devel branch -[2008-11-12 21:55:38] <ichthyo> then we can compare them -[2008-11-12 21:55:44] -->| pedro (n=pedro@32.Red-83-61-132.dynamicIP.rima-tde.net) has joined #lumiera -[2008-11-12 21:55:46] <cehteh> i opt for plain C .. but :) -[2008-11-12 21:55:56] <ichthyo> :) -[2008-11-12 21:56:26] <cehteh> its now clear that you dont want to use the 'interfaces' between backend and proc -[2008-11-12 21:56:31] <cehteh> not yet at least -[2008-11-12 21:56:39] <ichthyo> ah, btw. The textbook answer is: the main needs to be C++ if you want to have a mixed linkage -[2008-11-12 21:56:49] <ichthyo> the C++ faq tells the same -[2008-11-12 21:56:58] <ichthyo> I never questioned this -[2008-11-12 21:57:06] <thorwil> good night! :) -[2008-11-12 21:57:12] <cehteh> ok -[2008-11-12 21:57:13] <ichthyo> good night thorwil -[2008-11-12 21:57:28] |<-- thorwil has left kornbluth.freenode.net (Remote closed the connection) -[2008-11-12 21:57:29] <cehteh> iirc the C++ linker suffices -[2008-11-12 21:57:32] |<-- wimo has left kornbluth.freenode.net (Read error: 104 (Connection reset by peer)) -[2008-11-12 21:57:38] <cehteh> but well that are implementation details -[2008-11-12 21:58:10] <ichthyo> plus the main has be to treated by the C++ compiler, because he builds the startup and shutdown object code there -[2008-11-12 21:58:14] <cehteh> how about constructors for static objects in dynamic modules? will they be called when the lib is dlopend? -[2008-11-12 21:58:23] <cehteh> i have to figure that out -[2008-11-12 21:58:45] <ichthyo> I must confess I haven't tried this. But I thought the official answer was yes -[2008-11-12 21:59:05] <ichthyo> if the build process of the dynamic lib was done by the C++ linker of course -[2008-11-12 21:59:07] <joelholdsworth> I think so yes -[2008-11-12 21:59:14] <cehteh> anyways ... basic bootstrapping should imo not have a very advanced OO design but just some simple sequence of actions which pull it up -[2008-11-12 21:59:21] <cehteh> without becoming spaghetti -[2008-11-12 22:00:03] <ichthyo> as said.. I propose that I just code up my proposal too, than we can compare -[2008-11-12 22:00:18] <cehteh> and in detail .. config system needs to be first because other bootstraping might depend on config data -[2008-11-12 22:00:28] <ichthyo> thats clear. I noted that -[2008-11-12 22:00:51] <cehteh> next interfaces/plugins (initialization) -[2008-11-12 22:01:37] <ichthyo> It could even be that we need to push it down into the static initialisation phase. I am no fan of static initialisation and try to avoid doing anything of importance there, but we might get into a situation something needs a config value there -[2008-11-12 22:01:42] <cehteh> and then the rest .. in whatever order is needed -[2008-11-12 22:02:10] <cehteh> interfaces/plugin needs config -[2008-11-12 22:02:38] <ichthyo> yes, but we certainly don't need them in static initialisation. If we do, we do something wrong -[2008-11-12 22:02:41] <cehteh> what about my idea to put commandline args into the config? -[2008-11-12 22:02:47] <cehteh> yes -[2008-11-12 22:03:24] <ichthyo> cehteh: maybe we should discuss it later, and talk now just about the plugin GUI problem? -[2008-11-12 22:04:10] <ichthyo> I mean, after the official part of the meeting? -[2008-11-12 22:04:11] <cehteh> heh ... guess the next main() wont be there soon :) ... did you looked at my devel? -[2008-11-12 22:04:20] <cehteh> ok -[2008-11-12 22:04:50] <ichthyo> maybe I start with this topic -[2008-11-12 22:04:50] <cehteh> whats the plugin gui problem? -[2008-11-12 22:05:04] <cehteh> the whole gui .. or plugins in the gui? -[2008-11-12 22:05:07] <ichthyo> I see some problems we should thik about -[2008-11-12 22:05:18] <ichthyo> rather the latter, plugins -[2008-11-12 22:05:30] <joelholdsworth> ok [2008-11-12 22:05:46] <joelholdsworth> so bundling GUI together with plugins is easier now, right? [2008-11-12 22:05:59] <joelholdsworth> we implement some kind of parameters system -[2008-11-12 22:06:01] <ichthyo> there are plugins which don't provide a GUI, and some plugin systems allow for a standard method for creating plugin guis [2008-11-12 22:06:03] <ichthyo> yes [2008-11-12 22:06:14] <cehteh> joelholdsworth: thats up to you .. but the plugin system is ready [2008-11-12 22:06:27] <joelholdsworth> yeah that's fine [2008-11-12 22:06:38] <joelholdsworth> if you want to extend the GUI you just expose and interface for it -[2008-11-12 22:06:50] <ichthyo> so, basically loading a processing routine from a plugin and using it to process video/audio is no problem -[2008-11-12 22:06:58] <cehteh> basically you define some interface (first in C++) then translate it to the lumiera interface macros -[2008-11-12 22:07:09] <cehteh> yes -[2008-11-12 22:07:31] <ichthyo> but with the GUI there are several conceptual problems -[2008-11-12 22:07:35] <ichthyo> first. -[2008-11-12 22:07:44] <ichthyo> There are plugins which don't have a GUI. -[2008-11-12 22:07:45] <ichthyo> fine + [2008-11-12 22:07:51] * cehteh thinks about some canvas widget which the plugin can use to draw stuff there -[2008-11-12 22:08:05] <ichthyo> we need to define a parameter system, as joel pointed out [2008-11-12 22:08:26] <joelholdsworth> cehteh: as in overlays on the video canvas? +[2008-11-12 22:09:19] <cehteh> joelholdsworth: up to you, not necessary overlays + +[2008-11-12 22:05:07] <ichthyo> I see some problems we should thik about +[2008-11-12 22:06:50] <ichthyo> so, basically loading a processing routine from + a plugin and using it to process video/audio is no problem. Basically you define + some interface (first in C++) then translate it to the lumiera interface macros + but with the GUI there are several conceptual problems + +[2008-11-12 22:07:35] <ichthyo> first: There are plugins which don't have a GUI. Thats fine +[2008-11-12 22:08:05] <ichthyo> we need to define a parameter system, as joel pointed out [2008-11-12 22:08:38] <ichthyo> and then we need to fit the existing plugin systems, each one, to use this parameter system [2008-11-12 22:08:45] <cehteh> thats type:key:value at simplest [2008-11-12 22:08:51] <cehteh> maybe with some representation hints -[2008-11-12 22:08:54] <ichthyo> yes [2008-11-12 22:08:58] <ichthyo> there is more about it [2008-11-12 22:09:03] <ichthyo> representation hints [2008-11-12 22:09:07] <ichthyo> type of gui control [2008-11-12 22:09:15] <ichthyo> logarithmic/linear scale [2008-11-12 22:09:17] <ichthyo> zero point [2008-11-12 22:09:19] <ichthyo> range -[2008-11-12 22:09:19] <cehteh> joelholdsworth: up to you, not necessary overlays [2008-11-12 22:09:27] <ichthyo> but it is manageable [2008-11-12 22:09:38] <ichthyo> LADSPA is a prominent example -[2008-11-12 22:09:45] <cehteh> well .. i has my generic fader ... [2008-11-12 22:09:55] <ichthyo> then, the problem in the GUI is how to arrange those controls -[2008-11-12 22:10:16] <cehteh> logarithmic/linear scale <<< how about exposing callbacks rather -[2008-11-12 22:10:20] <ichthyo> but, basically, I think this alternative is the most simple one and the one which will create the least problems -[2008-11-12 22:10:34] <joelholdsworth> icthyo: I figured the UI will just expose parameters -[2008-11-12 22:10:39] -->| daitheflu (n=francois@ill67-3-88-164-129-128.fbx.proxad.net) has joined #lumiera +[2008-11-12 22:10:20] <ichthyo> anyway, I think this alternative is the most simple one and the one which will + create the least problems +[2008-11-12 22:10:34] <joelholdsworth> ichthyo: I figured the UI will just expose parameters [2008-11-12 22:10:42] <joelholdsworth> so you expose them semantically - maybe in a tree [2008-11-12 22:10:51] <joelholdsworth> and add rich presentation hints -[2008-11-12 22:10:59] <cehteh> i think layout should be optional by some config/stylesheet [2008-11-12 22:11:00] <joelholdsworth> that hit the UI how to lay out out -[2008-11-12 22:11:05] <ichthyo> cehteh: possible. But those things are already defined by the plugin systems we want to wrap up. -[2008-11-12 22:11:14] <cehteh> hi daitheflu -[2008-11-12 22:11:43] <ichthyo> ok. this was the first problem -[2008-11-12 22:11:50] <daitheflu> hi cehteh :) +[2008-11-12 22:10:59] <cehteh> i think layout should be optional by some config/stylesheet +[2008-11-12 22:11:05] <ichthyo> cehteh: possible. But those things are already defined by the + plugin systems we want to wrap up. +[2008-11-12 22:13:01] <cehteh> you have a list (or maybe tree) of parameters .. presentation aside + ... tree just because some parameters depend on the presence of some else + and you have a presentation .. + [2008-11-12 22:11:53] <ichthyo> next problem is: -[2008-11-12 22:12:02] <cehteh> there are 2 things .. presentation and application -[2008-11-12 22:12:03] <daitheflu> good evening ! -[2008-11-12 22:12:16] <ichthyo> the plugins need to allocate some space in the GUI and should not clutter the workspace -[2008-11-12 22:12:33] <ichthyo> this is a tough really tough interface design problem +[2008-11-12 22:12:16] <ichthyo> the plugins need to allocate some space in the GUI and should not clutter + the workspace this is a tough really tough interface design problem + because some plugins aren't just some "optional stuff", but wired + right into the core operation + [2008-11-12 22:12:54] <joelholdsworth> well ichthyo: I'm not planning to allow plugins to "take over" -[2008-11-12 22:12:57] <ichthyo> because some plugins aren't just some "optional stuff", but right in the middle of the handling workflow -[2008-11-12 22:13:01] <cehteh> you have a list (or maybe tree) of parameters .. presentation aside ... tree just because some parameters depend on the presence of some else -[2008-11-12 22:13:05] <joelholdsworth> they get to extend very specific parts of the UI + handling workflow. they get to extend very specific parts of the UI [2008-11-12 22:13:09] <joelholdsworth> e.g. params [2008-11-12 22:13:11] <joelholdsworth> or the canvas [2008-11-12 22:13:15] <joelholdsworth> or timeline -[2008-11-12 22:13:20] <cehteh> and you have a presentation .. [2008-11-12 22:13:22] <joelholdsworth> and only in very specific ways + +[2008-11-12 22:14:15] <joelholdsworth> ichthyo: and most of the time plugins wont extend anything -- + their whole UI will be exposed semantically (with UI presentation hints) + (in addition to the plain semantics) [2008-11-12 22:13:27] <ichthyo> thats fine -[2008-11-12 22:14:15] <joelholdsworth> ichthyo: and most of the time plugins wont extend anything - their whole UI will be exposed semantically (with UI presentation hints) -[2008-11-12 22:14:32] <joelholdsworth> (in addition to the plain semantics) -[2008-11-12 22:14:58] <ichthyo> but do you see the interface design problem? basically you want to design a smooth handling. but now you need to rely on some module which is plugged in and you don't know much about this module + +[2008-11-12 22:14:58] <ichthyo> but do you see the interface design problem? + basically you want to design a smooth handling. but now you need to rely + on some module which is plugged in and you don't know much about this module + how is it possible to tweak effects easily while you just don't know much + about those effects? [2008-11-12 22:15:23] <joelholdsworth> yes I agree -[2008-11-12 22:15:37] <ichthyo> how is it possible to tweak effects easily while you just don't know much about those effects -[2008-11-12 22:15:48] <ichthyo> then ther is another problem: + +[2008-11-12 22:15:48] <ichthyo> then there is another problem: [2008-11-12 22:15:56] <ichthyo> key bindings and MIDI bindings [2008-11-12 22:16:10] <ichthyo> you want to bind key shortcuts and external controllers to GUI elements [2008-11-12 22:16:20] <ichthyo> and of course plugins need to be included into this system -[2008-11-12 22:16:35] <joelholdsworth> ok that's quite easy - you just right click on the control (or click a tiny button) that opens up a menu that lets you set this stuff +[2008-11-12 22:16:35] <joelholdsworth> ok that's quite easy - you just right click on the control + (or click a tiny button) that opens up a menu that lets you set this stuff + [2008-11-12 22:16:59] <joelholdsworth> UI design? well that's what presentation hints help with -[2008-11-12 22:17:05] <ichthyo> yes -[2008-11-12 22:17:26] <joelholdsworth> they set size, layout, the appriate type of slider for example to expose this value +[2008-11-12 22:17:26] <joelholdsworth> they set size, layout, the appropriate type of slider + for example to expose this value [2008-11-12 22:17:29] <ichthyo> and what happens when the respective plugin options window isn't opened at all? [2008-11-12 22:17:47] <ichthyo> or when you have multiple instances of the same plugin? [2008-11-12 22:17:48] <joelholdsworth> what do you mean? -[2008-11-12 22:17:58] <cehteh> no tiny buttons to configure the ui please ... preferences are once place ... some key modifier +mouseclick another [2008-11-12 22:18:11] <ichthyo> for example: you use a sound panner plugin -[2008-11-12 22:18:47] <ichthyo> and you want to adjust the sound pan in 3 dimensions by key shortcuts, or to bind them to some knobs on an external control surface -[2008-11-12 22:18:49] <cehteh> ichthyo: this slaps back on you, proc ... -[2008-11-12 22:18:59] <ichthyo> I know I know + and you want to adjust the sound pan in 3 dimensions by key shortcuts, + or to bind them to some knobs on an external control surface + and now you have multiple instances of the same panner plugin + so we need a naming scheme + [2008-11-12 22:19:03] <cehteh> how i thought about this: -[2008-11-12 22:19:24] <ichthyo> and now you have multiple instances of the same panner plugin -[2008-11-12 22:19:39] <ichthyo> so we need a naming scheme -[2008-11-12 22:19:45] <cehteh> 1) plugin provides the list of its parameters (maybe as tree) -[2008-11-12 22:20:05] <ichthyo> as tree for sure, because many -[2008-11-12 22:20:10] <cehteh> 2) there is a config file (config system) which gets installed with the plugins which defines the default layout -[2008-11-12 22:20:10] <ichthyo> plugins have nested windows -[2008-11-12 22:20:40] <cehteh> 3) user might override that in his own configs .. but still only defaults for new instances of the things -[2008-11-12 22:20:58] <cehteh> 4) the actual configured state is saved with the session -[2008-11-12 22:21:07] <cehteh> for each instance -[2008-11-12 22:21:11] <ichthyo> yes -[2008-11-12 22:21:19] <ichthyo> it should work this way -[2008-11-12 22:21:26] <cehteh> no doubt + 1) plugin provides the list of its parameters (maybe as tree) + 2) there is a config file (config system) which gets installed + with the plugins which defines the default layout + 3) user might override that in his own configs ... + but still only defaults for new instances of the things + 4) the actual configured state is saved with the session + while all this config just is a definition which gets applied to a kindof stylesheet + +[2008-11-12 22:20:05] <ichthyo> as tree for sure, because many plugins have nested windows for each instance [2008-11-12 22:21:49] <ichthyo> which means we need a naming/addressing scheme -[2008-11-12 22:21:52] <cehteh> while all this config just is a definition which gets applied to a kindof stylesheet [2008-11-12 22:22:20] <ichthyo> ...which creates sort of a cascading scheme -[2008-11-12 22:22:22] <cehteh> yes the whole config system needs a namespace .. -[2008-11-12 22:22:37] <cehteh> seen the document i started? [2008-11-12 22:22:42] <ichthyo> I mean, a naming/addressing scheme [2008-11-12 22:23:04] <ichthyo> for distinguishing identical parameters in different instances of yet-unknown plugins -[2008-11-12 22:23:19] <ichthyo> and then: where to should the key binding stick? +[2008-11-12 22:23:19] <ichthyo> and now consider: where should these key bindings attach to? [2008-11-12 22:23:39] <ichthyo> to a specific instance, or to the suitable instance which is just in focus? [2008-11-12 22:23:46] <ichthyo> or both (probably) [2008-11-12 22:24:03] <ichthyo> you may want to bind a different knob to each different panner plugin -[2008-11-12 22:24:25] <ichthyo> or alternatively, you may want to bind "the pan knob" to the "current pannner plugin inscance" +[2008-11-12 22:24:25] <ichthyo> or alternatively, you may want to bind "the pan knob" to the "current pannner" [2008-11-12 22:24:42] <ichthyo> this would require that we introduce a focus concept [2008-11-12 22:25:00] <ichthyo> I think thorwil proposed something similar in the last discussion [2008-11-12 22:25:16] <ichthyo> so we have a notion of the "current track", "current clip" @@ -258,655 +198,447 @@ Conclusion [2008-11-12 22:26:10] <ichthyo> many plugin systems, especially VST have custom GUIs [2008-11-12 22:26:27] <ichthyo> and may not even work properly without using those [2008-11-12 22:26:48] <ichthyo> which means, some GUI code in the plugin takes over. That is something I utterly dislike -[2008-11-12 22:27:09] |<-- markekeller has left kornbluth.freenode.net (kornbluth.freenode.net irc.freenode.net) -[2008-11-12 22:27:09] |<-- cehteh has left kornbluth.freenode.net (kornbluth.freenode.net irc.freenode.net) -[2008-11-12 22:27:09] |<-- rgareus has left kornbluth.freenode.net (kornbluth.freenode.net irc.freenode.net) -[2008-11-12 22:27:09] |<-- joelholdsworth has left kornbluth.freenode.net (kornbluth.freenode.net irc.freenode.net) -[2008-11-12 22:27:10] |<-- lkjasa has left kornbluth.freenode.net (kornbluth.freenode.net irc.freenode.net) -[2008-11-12 22:27:10] |<-- rexbron has left kornbluth.freenode.net (kornbluth.freenode.net irc.freenode.net) -[2008-11-12 22:29:47] <ichthyo> hallooo.... -[2008-11-12 22:29:54] <ichthyo> anybody there....? -[2008-11-12 22:31:12] [INFO] The view ``#lumiera'' has been successfully saved to ``/home/hiv/Desktop/aua.html''. -[2008-11-12 22:31:20] [QUIT] Disconnected from irc://kornbluth.freenode.net/ (irc://kornbluth.freenode.net/). [[Reconnect][Reconnect to kornbluth.freenode.net][reconnect]] -[2008-11-12 22:31:30] [INFO] Now logging to <file:///Lager/heim/profi/fox/chatzilla/logs/kornbluth.freenode.net/channels/%23lumiera.2008-11-12.log>. -[2008-11-12 22:31:30] [INFO] Channel view for ``#lumiera'' opened. -[2008-11-12 22:31:32] =-= User mode for ichthyo is now +i -[2008-11-12 22:31:34] [INFO] No such nick/channel -[2008-11-12 22:31:38] -->| YOU (ichthyo) have joined #lumiera -[2008-11-12 22:31:38] =-= Topic for #lumiera is ``Lumiera, the new emerging NLE for linux http://www.lumiera.org | Enter the logo contest at http://www.pipapo.org/pipawiki/Lumiera/Logos'' -[2008-11-12 22:31:38] =-= Topic for #lumiera was set by raffa on 27.10.2008 21:57:46 -[2008-11-12 22:31:45] <ichthyo> haloooooo.... -[2008-11-12 22:31:51] <ichthyo> anybody there???? -[2008-11-12 22:32:09] <daitheflu> sure :) -[2008-11-12 22:32:10] -->| cehteh__ (n=ct@pipapo.org) has joined #lumiera -[2008-11-12 22:32:14] <cehteh__> ah found -[2008-11-12 22:32:20] <ichthyo> all killed by a virus? -[2008-11-12 22:32:21] <cehteh__> i am on 3 server -[2008-11-12 22:32:31] <ichthyo> seems there was a mass disconnect -[2008-11-12 22:32:39] <cehteh__> no netsplit -[2008-11-12 22:33:14] <cehteh__> well i found you and joel on another server -[2008-11-12 22:33:28] <cehteh__> [22:22] <ichthyo> I mean, a naming/addressing scheme -[2008-11-12 22:33:29] <cehteh__> [22:23] <ichthyo> for distinguishing identical parameters in different instances of yet-unknown plugins -[2008-11-12 22:33:29] <cehteh__> [22:23] <cehteh> no thats not needed -[2008-11-12 22:33:29] <cehteh__> [22:23] <cehteh> in the session each object has a uuid thats its address -[2008-11-12 22:33:29] <cehteh__> [22:23] <cehteh> each parameter has a uuid too -[2008-11-12 22:33:30] <cehteh__> [22:24] <cehteh> (interface system) -[2008-11-12 22:33:31] <cehteh__> [22:24] <cehteh> so you can say parameter 0x2344545645654534 of object 0x4543765732525t643 .... -[2008-11-12 22:33:34] <cehteh__> [22:26] <cehteh> yet unknown doesnt exist .. it is either unknown or known :) -[2008-11-12 22:33:35] *tomaw* [Global Notice] Hi all. That split has taken services and 10k with it. We're currently investigating the cause and hope to have it resolved soon. Further updates via wallops (/mode <nick> +w). Thanks! -[2008-11-12 22:35:37] [QUIT] Disconnected from irc://kornbluth.freenode.net/ (irc://kornbluth.freenode.net/). [[Reconnect][Reconnect to kornbluth.freenode.net][reconnect]] -[2008-11-12 22:37:59] [INFO] No such nick/channel -[2008-11-12 22:38:03] -->| YOU (ichthyo) have joined #lumiera -[2008-11-12 22:38:03] =-= Topic for #lumiera is ``Lumiera, the new emerging NLE for linux http://www.lumiera.org | Enter the logo contest at http://www.pipapo.org/pipawiki/Lumiera/Logos'' -[2008-11-12 22:38:03] =-= Topic for #lumiera was set by raffa on 27.10.2008 21:57:46 -[2008-11-12 22:38:15] <cehteh__> ichthyo: stay here -[2008-11-12 22:38:27] [QUIT] Disconnected from irc://kornbluth.freenode.net/ (irc://kornbluth.freenode.net/). [[Reconnect][Reconnect to kornbluth.freenode.net][reconnect]] -[2008-11-12 22:48:08] -->| YOU (ichthyo) have joined #lumiera -[2008-11-12 22:48:08] =-= Topic for #lumiera is ``Lumiera, the new emerging NLE for linux http://www.lumiera.org | Enter the logo contest at http://www.pipapo.org/pipawiki/Lumiera/Logos'' -[2008-11-12 22:48:08] =-= Topic for #lumiera was set by raffa on 27.10.2008 21:57:46 -[2008-11-12 22:48:13] <ichthyo> haha -[2008-11-12 22:49:05] <joelholdsworth> yay! -[2008-11-12 22:49:06] <ichthyo> I see three clones of cehteh -[2008-11-12 22:49:08] <cehteh_> heh -[2008-11-12 22:49:29] <joelholdsworth> cehteh, cehteh_ and cehteh__ ! -[2008-11-12 22:49:36] <cehteh__> [22:38] --> ichthyo (n=hiv@p5B0C1C57.dip0.t-ipconnect.de) has joined #lumiera -[2008-11-12 22:49:36] <cehteh__> [22:38] <cehteh__> ichthyo: stay here -[2008-11-12 22:49:36] <cehteh__> [22:38] <-- ichthyo has quit (Client Quit) -[2008-11-12 22:49:44] <ichthyo> has each cehteh_ clone a separate conscience? -[2008-11-12 22:49:51] <cehteh__> .. i tried to bring you back together -[2008-11-12 22:50:02] <cehteh> but well -[2008-11-12 22:50:09] <joelholdsworth> ha! -[2008-11-12 22:50:10] <ichthyo> connection was instable -[2008-11-12 22:50:18] <cehteh_> no one listens to -[2008-11-12 22:50:19] <cehteh__> me -[2008-11-12 22:50:24] <joelholdsworth> :D -[2008-11-12 22:50:28] <ichthyo> :-o -[2008-11-12 22:50:36] <joelholdsworth> anyway -[2008-11-12 22:50:51] <cehteh__> joel did you got my repaste? -[2008-11-12 22:50:54] |<-- cehteh__ has left kornbluth.freenode.net ("Client exiting") -[2008-11-12 22:50:57] <joelholdsworth> yes -[2008-11-12 22:50:58] |<-- cehteh_ has left kornbluth.freenode.net ("Client exiting") -[2008-11-12 22:51:00] <joelholdsworth> I was just making a comment about customization: -[2008-11-12 22:51:14] <cehteh> ichthyo: did you got my answer? -[2008-11-12 22:51:19] <joelholdsworth> Thorwil helpfully sent me a copy of the Blender's UI review -[2008-11-12 22:51:25] <ichthyo> about the UUIDs yes -[2008-11-12 22:51:28] <cehteh> uuids -[2008-11-12 22:51:30] <cehteh> ok -[2008-11-12 22:51:45] <joelholdsworth> the Belnder UI reviews says this: ""Lastly, I’d like to address another misconception, this time about customizability. There has been a notion that the solution to most of the UI problems can be solved with added customizability. Thenotion goes that if the UI is inefficient, the user can simply customize it herself to suit her needs. This claim is wrong for several reasons: -[2008-11-12 22:51:49] <joelholdsworth> • It is impossible to customize something you have not fully comprehended yet, so it in no way helps the learning process. -[2008-11-12 22:51:53] <joelholdsworth> • It makes an application less predictable, because the user cannot rely on the application to always act the same. -[2008-11-12 22:51:56] <joelholdsworth> • It takes focus away from the users content, and over to managing the application itself, which is what we wanted to avoid in the first place. -[2008-11-12 22:52:02] <joelholdsworth> • It undermines the portability of Blender, because you cannot rely on Blender functioning the same way across different systems. -[2008-11-12 22:52:05] <joelholdsworth> • Customizability does not equate to flexibility. -[2008-11-12 22:52:08] *christel* [Global Notice] Hi all! Terribly sorry for that (fairly substantial) split! Unfortunately one of our sponsors experienced routing issues affecting our secondary US hub, Services and one of our largest MRS servers.. The issues are hopefully fixed for now, but if you have some spare connectivity and hardware, perhaps you should consider helping us not keep all our eggs in one basket! Again, sorry and thanks for using freenode! -[2008-11-12 22:52:10] <joelholdsworth> • 99% of Blenders user will use the default setup anyway. -[2008-11-12 22:52:17] <joelholdsworth> I think those are all very good points! -[2008-11-12 22:52:30] <joelholdsworth> "This is not to say that customizability is always bad - having the ability to change the hotkeys -[2008-11-12 22:52:31] <joelholdsworth> from the defaults to match another 3D application such as Maya or Softimage XSI can make it easier for users of those applications to adapt to Blender." -[2008-11-12 22:53:03] <cehteh> yes we agree on that pretty much i thnik -[2008-11-12 22:53:08] <ichthyo> yes -[2008-11-12 22:53:16] <cehteh> customization should be hardcore -[2008-11-12 22:53:22] <cehteh> for special purposes -[2008-11-12 22:53:42] <joelholdsworth> ok - so you understand if I want to try and just do the UI "right", then add a little bit of customization back in here and there -[2008-11-12 22:53:50] <cehteh> but we may provide some 'theme' switcher .. not themes as in artwork but for workflow -[2008-11-12 22:54:17] <ichthyo> joelholdsworth: I fully support you with this goal -[2008-11-12 22:54:34] <joelholdsworth> well if we're thinking of having a perspective switcher like in eclipse, then saving different panel layouts is quite easy -[2008-11-12 22:54:44] <cehteh> yes -[2008-11-12 22:54:47] <cehteh> thats what i mean -[2008-11-12 22:54:53] <cehteh> thats absolutely necessary -[2008-11-12 22:54:58] <joelholdsworth> very good -[2008-11-12 22:55:08] <joelholdsworth> ok -[2008-11-12 22:55:17] <joelholdsworth> I just need to go wash the dishes -[2008-11-12 22:55:21] <joelholdsworth> back in 10 minutes -[2008-11-12 22:55:22] <cehteh> this panel layout shall be somewhat configureable .. (config system) -[2008-11-12 22:55:26] <ichthyo> the problem is just: you can't avoid things like a user binding some external control surface to a certain gui control -[2008-11-12 22:56:02] <cehteh> ichthyo: about the uuid .. does that solve the problem you spotted with addressing for you? -[2008-11-12 22:56:24] <ichthyo> yes... I intended to use them for this purpose -[2008-11-12 22:56:37] <ichthyo> all I wanted is to bring the problem to everyones attention -[2008-11-12 22:56:48] <ichthyo> also, that we need a parameter system -[2008-11-12 22:57:02] <ichthyo> and that this parameter system is probably tree-like, not just flat + +[2008-11-12 22:33:29] <cehteh> no thats not needed +[2008-11-12 22:33:29] <cehteh> in the session each object has a uuid thats its address +[2008-11-12 22:33:29] <cehteh> each parameter has a uuid too(interface system) +[2008-11-12 22:33:31] <cehteh> so you can say parameter 0x2344545645654534 of object 0x4543765732525t643 .... +[2008-11-12 22:33:34] <cehteh> "yet unknown" doesnt exist .. it is either unknown or known :) + +[2008-11-12 22:55:26] <ichthyo> well ..the problem is just: you can't avoid things like a user binding + some external control surface to a certain gui control. All I wanted is + to bring the problem to everyones attention, also, that we need a parameter system + and that this parameter system is probably tree-like, not just flat [2008-11-12 22:57:06] <cehteh> i want anythnig be addressable with uuid(manipulating_function)+uuid(object) -[2008-11-12 22:58:41] <ichthyo> for me, this is just a opaque reference which is implemented in some way. So, given, I have the object representing the connectionn to the plugin, I can provide the GUI with some means for re-addressing a certain parameter, e.g. for a key binding -[2008-11-12 22:58:56] <cehteh> yes -[2008-11-12 22:59:09] <ichthyo> and, of course for re-establishing the same binding when re-creating the session from storage -[2008-11-12 22:59:22] <cehteh> internally you prolly dont even need the re-addressing .. the serializer will do that for you -[2008-11-12 22:59:33] <cehteh> but that needs to be worked out -[2008-11-12 22:59:50] <cehteh> means translating between memory addresses and uuids -[2008-11-12 23:00:18] <ichthyo> for now I am just fine with the notion that there is such a "persistent reference". And actually implementint it is just one very special problem -[2008-11-12 23:00:38] <cehteh> yes -[2008-11-12 23:00:50] <ichthyo> but with plugin guis instances is still the further problem is that you probably have more option windows of some plugins as you can show in any reasonable GUI -[2008-11-12 23:00:57] <cehteh> i dont want to care yet either .. i have some more concrete plans .. but that has some tmie -[2008-11-12 23:01:07] * ichthyo nods -[2008-11-12 23:02:31] <cehteh> ok -[2008-11-12 23:03:06] -->| daitheflu_ (n=francois@ill67-3-88-164-129-128.fbx.proxad.net) has joined #lumiera -[2008-11-12 23:03:14] |<-- daitheflu has left kornbluth.freenode.net (Read error: 104 (Connection reset by peer)) -[2008-11-12 23:03:34] <ichthyo> this issues were one of the reasons I made this "FeatureBundle" proposal some time ago. Meaning there is a mechanism allowing you to bundle -[2008-11-12 23:03:34] <ichthyo> - an existing external plugin -[2008-11-12 23:03:34] <ichthyo> - an adaptation we do for connecting it to the gui -[2008-11-12 23:03:34] <ichthyo> - maybe a script or gui -[2008-11-12 23:03:34] <ichthyo> - customisation info we need for the gui layout -[2008-11-12 23:03:34] <ichthyo> ...and provide all together as a single bundle the user can install -[2008-11-12 23:04:40] <ichthyo> joel is washing dishes. Maybe a short break to get some coffee for me? -[2008-11-12 23:05:10] <cehteh> the feature bundle has the deployment problem over different archs -[2008-11-12 23:05:47] <cehteh> but something like that is needed anyways -[2008-11-12 23:05:49] <joelholdsworth> back again -[2008-11-12 23:06:41] <cehteh> ... btw the pluginloader will be able to handle C source plugins which are compiled on the fly someday and things like that -[2008-11-12 23:07:02] <cehteh> lua scripts too .. but thats all far future -[2008-11-12 23:07:23] <cehteh> ah .. and i decided for .lum as plugin extension .. LumieraModule :) -[2008-11-12 23:07:39] <cehteh> (.so works too) -[2008-11-12 23:07:47] <joelholdsworth> that's about as corny as "java beans" -[2008-11-12 23:08:12] <cehteh> .lup lumiera plugin? -[2008-11-12 23:08:30] <cehteh> .lef luimera effect? -[2008-11-12 23:08:32] <cehteh> :) -[2008-11-12 23:09:18] <cehteh> well names dont matter much ... except the basic type of the plugin is decided on that -[2008-11-12 23:09:31] <cehteh> its easy to change/extend -[2008-11-12 23:09:54] * ichthyo back again with a cup of coffee -[2008-11-12 23:10:15] <ichthyo> .lum sounds good -[2008-11-12 23:11:15] <ichthyo> joelholdsworth: maybe you have already some ideas how to deal with all those plugin option windows? -[2008-11-12 23:11:27] <cehteh> really the least concern .. i just wanted to tell something while you get a coffee -[2008-11-12 23:11:36] <ichthyo> they are a real problem for getting a smooth handling and workflow -[2008-11-12 23:12:09] <cehteh> please do not pop up windows for each plugin :) -[2008-11-12 23:12:14] <ichthyo> because: if you work on tweaking, which can be a considerable amount of the whole project time +[2008-11-12 22:58:41] <ichthyo> for me, this is just a opaque reference which is implemented in some way. So, assumed + I have the object representing the connectionn to the plugin, I can provide the GUI with + some means for re-addressing a certain parameter, e.g. for a key binding, and, of course + for re-establishing the same binding when re-creating the session from storage + +[2008-11-12 23:00:50] <ichthyo> Further, with plugin guis instances there is yet another problem in that you probably + have more option windows of some plugins as you can show in any reasonable GUI + These issues were one of the reasons I made this "FeatureBundle" proposal some time ago, + Meaning there is a mechanism allowing you to bundle + - an existing external plugin + - an adaptation we do for connecting it to the gui + - maybe a script or gui + - customisation info we need for the gui layout + ...and provide all together as a single bundle the user can install +[2008-11-12 23:11:15] <ichthyo> joelholdsworth: maybe you have already some ideas how to deal with all those plugin + option windows? they are a real problem for getting a smooth handling and workflow. + because: you'll spend a considerable amount of the whole project time with tewaking [2008-11-12 23:12:28] <ichthyo> (yes, that's what I am about to point out....) -[2008-11-12 23:12:34] <cehteh> i'd like to see that integrated in the whole screen concept -[2008-11-12 23:12:55] <cehteh> how about making 'resources' tabbed and have one 'plugin guis' tab there -[2008-11-12 23:13:05] <ichthyo> ...if you work on tweaking, you typically have a small amount of plugin windows which need to stay open -[2008-11-12 23:13:09] <cehteh> with maybe subtabs for all active plugins +[2008-11-12 23:13:05] <ichthyo> ...if you work on tweaking, you typically have a small amount of plugin windows + which need to stay open with maybe subtabs for all active plugins + say some color correction on 2 tracks, and then you open and close variouos plugins + for several different clips ...plus, maybe the possibility to "pin" some of the plugin + windows, while others just allocate the next free slot + [2008-11-12 23:13:14] <joelholdsworth> I guess we need some modeless way of putting plugin controls in a panel -[2008-11-12 23:13:48] <cehteh> then you can tile the resources pane ... or open 2 or more showing different plugin guis -[2008-11-12 23:13:48] <ichthyo> say some color correction on 2 tracks, and then you open and close variouos plugins for several different clips -[2008-11-12 23:14:23] <cehteh> joelholdsworth: see my screen concept .. :/ -[2008-11-12 23:14:39] <joelholdsworth> where? -[2008-11-12 23:14:49] <cehteh> anything should be tileable and detachable and open in multiple instances -[2008-11-12 23:14:54] <cehteh> gui brainstorm -[2008-11-12 23:14:57] <joelholdsworth> yes that's fine -[2008-11-12 23:14:58] <ichthyo> ...plus, maybe the possibility to "pin" some of the plugin windows, while others just allocate the next free slot -[2008-11-12 23:15:32] <joelholdsworth> so maybe having panels open up - or do it like inkscape with a scrollable panel container that gets longer as more panels are opened -[2008-11-12 23:15:48] <cehteh> joelholdsworth: well currently the gtk docks by default are still little special to the window manager -[2008-11-12 23:16:01] <cehteh> would be nice to make them all first class windows -[2008-11-12 23:16:32] <cehteh> .. think about tiled window managers :) -[2008-11-12 23:16:37] <joelholdsworth> I do -[2008-11-12 23:16:52] <joelholdsworth> and I plan to make it compatible with them -[2008-11-12 23:17:15] <cehteh> and provide tiling for the users which dont have a tiled window manager :P -[2008-11-12 23:17:23] <joelholdsworth> but I do want to get it working well for non-tiled WMs who are the vast majority of the user base -[2008-11-12 23:17:48] <cehteh> yes -[2008-11-12 23:18:09] <cehteh> but i expect that most people will run the lumiera window fullscreen anyways -[2008-11-12 23:18:34] <joelholdsworth> maybe -[2008-11-12 23:18:35] <cehteh> (at least resized to full screen dimensions, not fulscreen mdoe) -[2008-11-12 23:18:42] <joelholdsworth> yeah -[2008-11-12 23:19:08] <cehteh> video editing is nothing you do in a 400x300 window -[2008-11-12 23:19:15] <joelholdsworth> :) -[2008-11-12 23:19:25] <raffa> (Hi everyone!) +[2008-11-12 23:15:32] <joelholdsworth> so maybe having panels open up - or do it like inkscape with a scrollable panel + container that gets longer as more panels are opened + +[2008-11-12 23:20:10] <cehteh> Cinelerra's plugin guis who constantly get lost behind the main window are really a mess [2008-11-12 23:19:28] <ichthyo> joelholdsworth: have you already any ideas about focus handling? -[2008-11-12 23:19:31] <ichthyo> hi raffa -[2008-11-12 23:19:32] <cehteh> hi raffa -[2008-11-12 23:19:36] <joelholdsworth> hey -[2008-11-12 23:19:36] <cehteh> .. how was it? -[2008-11-12 23:19:45] <daitheflu_> hi raffa -[2008-11-12 23:20:10] <cehteh> cinelerras plugin guis who constantly get lost behind the main window are really a mess -[2008-11-12 23:20:15] <ichthyo> joelholdsworth: do we get a "current object", a "current track" or the like which recieves keybord input -[2008-11-12 23:20:28] <raffa> (cehteh: Great! :-) ) +[2008-11-12 23:20:15] <ichthyo> joelholdsworth: do we get a "current object", a "current track" or the like + which receives keybord input [2008-11-12 23:20:37] <joelholdsworth> keybaord focus, yes - that was the plan -[2008-11-12 23:20:48] <cehteh> working on gnome/metacity at karins computer -[2008-11-12 23:20:54] <ichthyo> thorwil recently pointed at the concept of having an "active element" within a larger selected group of elements -[2008-11-12 23:21:12] <ichthyo> e.g. you have a "current clip" and within it a currently active effect attachment -[2008-11-12 23:21:38] <ichthyo> and if you press the shortcut, you switch over to the automation curve of this plugin if there is one -[2008-11-12 23:21:43] <ichthyo> etc. etc... +[2008-11-12 23:20:54] <ichthyo> thorwil recently pointed at the concept of having an "active element" within + a larger selected group of elements e.g. you have a "current clip" and within it + a currently active effect attachment and if you press the shortcut, you switch over + to the automation curve of this plugin if there is one, etc. etc... + [2008-11-12 23:21:44] <cehteh> think about future ... multiple elements have focus from different devices [2008-11-12 23:22:06] <cehteh> multitouch ... and other controlers [2008-11-12 23:22:06] <joelholdsworth> I think we have to treat keyboard focus as special [2008-11-12 23:22:23] <cehteh> next X version will utilitze multiple mouse pointers [2008-11-12 23:22:35] <ichthyo> e.g. what's with the mouse wheel? -[2008-11-12 23:22:42] <ichthyo> whats with a jog dial -[2008-11-12 23:22:47] <ichthyo> on a control surface -[2008-11-12 23:22:48] -->| kaarna (n=joonaz@hoas-fe3cdd00-230.dhcp.inet.fi) has joined #lumiera -[2008-11-12 23:22:54] <cehteh> i have an external numeric keypad which i sometimes connect even when working with karins billing software +[2008-11-12 23:22:42] <ichthyo> what's with a jog dial on a control surface [2008-11-12 23:23:03] <ichthyo> or with the play/stop buttons on a control surface? + [2008-11-12 23:23:03] <joelholdsworth> my plan is to treat the jog dial as special [2008-11-12 23:23:23] <joelholdsworth> yes again - special button assignment [2008-11-12 23:23:23] <ichthyo> sort of like the mouse wheel? -[2008-11-12 23:23:34] <cehteh> imagine user wants a 2nd normal pc keyboard as video controler +[2008-11-12 23:23:34] <cehteh> imagine user wants a 2nd normal pc keyboard as video controler [2008-11-12 23:23:42] <ichthyo> because all those rather follow sort of a focus [2008-11-12 23:23:56] <ichthyo> maybe together with modifier keys -[2008-11-12 23:24:00] <cehteh> .o(adaptive mouse wheel) + [2008-11-12 23:24:04] <joelholdsworth> yes again that would work via an auxialiry controller setup config -[2008-11-12 23:24:16] <joelholdsworth> the primary keybaord and mouse work in the normal way -[2008-11-12 23:24:24] <joelholdsworth> we can't have two keyboard focusses -[2008-11-12 23:24:28] <joelholdsworth> - it's just won't work -[2008-11-12 23:24:43] <joelholdsworth> we can have a second keyboard - a board of hotkeys -[2008-11-12 23:24:47] <cehteh> works with numeric pad at least -[2008-11-12 23:24:56] <cehteh> yes thats what i mean -[2008-11-12 23:24:59] <joelholdsworth> yes that's ok - because that's an aux controller -[2008-11-12 23:25:04] <cehteh> that would be cool + the primary keybaord and mouse work in the normal way + we can't have two keyboard focusses - it's just won't work + we can have a second keyboard - a board of hotkeys [2008-11-12 23:25:21] <joelholdsworth> there's mouse, keyboard and auxiliary controllers -[2008-11-12 23:25:27] <cehteh> keyboards are really cheap and have plenty of inputs [2008-11-12 23:25:34] <joelholdsworth> and those controllers would be mapped to the UI using a cunning dialog -[2008-11-12 23:25:58] <ichthyo> and what I said some time ago today with the binding for the "current" pan is a similar idea. Because also such a binding should rather follow the focus -[2008-11-12 23:26:21] <ichthyo> it doesn't help if you bind it to a specific instance of the panner plugin -[2008-11-12 23:26:34] <ichthyo> the moment you have more then 8 panners, it won't scale -[2008-11-12 23:26:43] <cehteh> see config system ... -[2008-11-12 23:27:07] <ichthyo> rather, it would be helpful, if you had a "current" fade and pan and sound level, which would follow the focus -[2008-11-12 23:27:15] <cehteh> this.panner < focus.panner -[2008-11-12 23:27:23] <cehteh> this.panner < track1.panner -[2008-11-12 23:27:43] <cehteh> meaning: just introduce another level of indirection +[2008-11-12 23:25:58] <ichthyo> and what I said some time ago today with the binding for the "current" pan + is a similar idea. Because also such a binding should rather follow the focus + it doesn't help if you bind it to a specific instance of the panner plugin + the moment you have more then 8 panners, it won't scale + rather, it would be helpful, if you had a "current" fade and pan and sound level, + which would follow the focus, so you could e.g. navigate the focus with the + right hand at the cursor keys, and leave the left hand on your control surface's knobs + please understand me right: I don't want to push features, and I will stay out of GUI + issues. All I want is that everyone in the project is aware of this problems [2008-11-12 23:27:44] <joelholdsworth> yes ok -[2008-11-12 23:27:47] <ichthyo> so you could e.g. navigate the focus with the right hand at the cursor keys, and leave the left hand on your control surface's knobs -[2008-11-12 23:28:28] <ichthyo> please understand me right: I don't want to push features, and I will stay out of GUI issues. All I want is that everyone in the project is aware of this problems -[2008-11-12 23:28:41] <joelholdsworth> yes -[2008-11-12 23:28:45] <joelholdsworth> no I see the problem -[2008-11-12 23:28:50] <cehteh> instead having aux controlers you bind directly to some functionality +[2008-11-12 23:28:45] <joelholdsworth> I see the problem [2008-11-12 23:28:58] <ichthyo> ok thanks -[2008-11-12 23:29:02] |<-- kettuz has left kornbluth.freenode.net (Read error: 110 (Connection timed out)) -[2008-11-12 23:29:28] <cehteh> bind the aux controlers to some virtual controlers .. why get mapped to some virtual control points which then mapping to the real control -[2008-11-12 23:29:40] <cehteh> then you can configure it from both directions -[2008-11-12 23:29:55] -->| kettuz (n=kettuz@cs160222.pp.htv.fi) has joined #lumiera -[2008-11-12 23:30:45] <ichthyo> Wii controller for Lumiera :-) -[2008-11-12 23:30:53] <joelholdsworth> :) -[2008-11-12 23:30:56] <cehteh> having a trackball which always controls the main volume .. but with some modifier for example it controls the volume of the focused object -[2008-11-12 23:31:11] <ichthyo> yeah, this sort of things. -[2008-11-12 23:31:24] <ichthyo> Well, I think that's enough for this point on the agenda -[2008-11-12 23:31:35] <ichthyo> nothing of it is immediately urgend -[2008-11-12 23:31:45] <cehteh> adding one or 2 layers of indirection there wont be much complicated and makes it much more flexible -[2008-11-12 23:31:54] <ichthyo> but it's fine we are all aware of the problem and seemingly share the same aproach -[2008-11-12 23:32:20] <ichthyo> config system + some sort of automatic persisitent references within proc + some indirection + maybe a focus system -[2008-11-12 23:32:40] <cehteh> multi focus system -[2008-11-12 23:33:10] <ichthyo> and another point I want to note is: all of us are rather reluctant to allow plugins to create GUIs on their own + +[2008-11-12 23:31:54] <ichthyo> but it's fine we are all aware of the problem and seemingly share the same approach + config system + some sort of automatic persistent references within proc + some + indirection + maybe a focus system + +[2008-11-12 23:33:10] <ichthyo> and another point I want to note is: all of us are rather reluctant to allow plugins + to create GUIs on their own [2008-11-12 23:33:27] <joelholdsworth> depends what you mean by that [2008-11-12 23:33:29] <cehteh> i guess we cant avoid that [2008-11-12 23:33:38] <ichthyo> but note: there is a great urge from some some users for such things -[2008-11-12 23:33:39] <cehteh> for really extrn plugins +[2008-11-12 23:33:39] <cehteh> for really external plugins [2008-11-12 23:33:47] <joelholdsworth> yes [2008-11-12 23:34:01] <cehteh> i am more cared about them fireing threads up :P [2008-11-12 23:34:13] <ichthyo> thats part of the problem -[2008-11-12 23:34:33] <cehteh> well we cant completely avoid uncooperative plugins [2008-11-12 23:34:37] <ichthyo> some start registering their own key shortcuts and all sorts of horrible things -[2008-11-12 23:34:42] <joelholdsworth> if you let each plugin create every bit of it's ui, you get a lot of code copy/paste and a lot of diverging GUI -[2008-11-12 23:34:45] <cehteh> neither from the core side, nor from the gui side -[2008-11-12 23:35:02] <joelholdsworth> so you need to "help" the plugins by putting them in a strict framework -[2008-11-12 23:35:09] <cehteh> yes +[2008-11-12 23:34:42] <joelholdsworth> if you let each plugin create every bit of it's ui, you get a lot of code + copy/paste and a lot of diverging GUI, so you need to "help" the plugins + by putting them in a strict framework. Then, on top of that we allow + extensions which allow a little more flexibility + [2008-11-12 23:35:20] <ichthyo> Steinberg failed to address this problem properly and VST bears on this heritage until today -[2008-11-12 23:35:35] <cehteh> make it in a way where the 'correct' way is also the most convinient for any programmer is all and the best we can do +[2008-11-12 23:35:35] <cehteh> make it in a way where the 'correct' way is also the most convenient for any programmer is + all and the best we can do [2008-11-12 23:35:49] * ichthyo nods [2008-11-12 23:36:04] <joelholdsworth> yes [2008-11-12 23:36:07] <cehteh> note the 'any' ... [2008-11-12 23:36:20] <ichthyo> :-P -[2008-11-12 23:36:25] <joelholdsworth> then we allow extensions which allow a little more flexibility [2008-11-12 23:36:26] <cehteh> means mad programmers, beginner programmers, experienced programmers, html programmers ... -[2008-11-12 23:36:52] <joelholdsworth> but I'm thinking we do need to remember that the majority of our plugin developers will be part of this project team -[2008-11-12 23:37:21] <ichthyo> and at some point I am much in favor of the idea of a official "plugin list" or site which describes good and compatible plugins, and maybe offers customisation bindings for them -[2008-11-12 23:38:09] <ichthyo> joelholdsworth: no, it won't be this way. Rather, there are some mainstream plugin systems. People will foremost want to use plugins they know already -[2008-11-12 23:38:21] <ichthyo> besides a small set of core plugins, yes -[2008-11-12 23:38:34] <ichthyo> those are the ones we provide as native lumiera plugins + +[2008-11-12 23:36:52] <joelholdsworth> but I'm thinking we do need to remember that the majority of our plugin developers + will be part of this project team +[2008-11-12 23:38:09] <ichthyo> joelholdsworth: no, it won't be this way. Rather, there are some mainstream plugin systems. + People will foremost want to use plugins they know already, letting asside a small set of + core plugins, yes, those are the ones we provide as native lumiera plugins [2008-11-12 23:38:42] <joelholdsworth> hmm ok [2008-11-12 23:38:44] <ichthyo> a fader, a mask etc. [2008-11-12 23:38:50] <ichthyo> but anything besides that [2008-11-12 23:39:06] <ichthyo> every sound engenieer has his favorite eq and compressor -[2008-11-12 23:39:26] <ichthyo> and he will rather swich to another application than being forced to use a compressor he isn't fine with -[2008-11-12 23:39:32] <cehteh> btw the plugin system is quite strict currently -[2008-11-12 23:39:36] <ichthyo> same for advanced video stuff -[2008-11-12 23:39:51] <cehteh> a plugin by default sees nothing from the main app -[2008-11-12 23:40:07] <joelholdsworth> good -[2008-11-12 23:40:07] <cehteh> all it gets is a handle which it can use to open interfaces -[2008-11-12 23:40:13] <joelholdsworth> that's the way it's meant to be -[2008-11-12 23:40:20] <ichthyo> yes, fully agreed -[2008-11-12 23:40:29] <cehteh> well its circumventable .. with some efforts -[2008-11-12 23:40:37] <ichthyo> "criminal energy" -[2008-11-12 23:40:46] <cehteh> exactly -[2008-11-12 23:40:47] <ichthyo> allways works like a charm -[2008-11-12 23:41:13] <cehteh> the other way aroud would be a plugin which starts up tcltk for a gui -[2008-11-12 23:41:18] <cehteh> that would be horror -[2008-11-12 23:41:38] <ichthyo> well... I think we are done with the points on the meeting agenda, I mean everything besides "DesignProcess" -[2008-11-12 23:41:50] <daitheflu_> cehteh: I second that -[2008-11-12 23:41:51] <cehteh> and i bet people will do that -[2008-11-12 23:42:25] <cehteh> so i think it might be better to provide a controlled side channel than let the people to ugly hacks -[2008-11-12 23:42:53] <ichthyo> especially for communication with the gui options window of the plugin -[2008-11-12 23:43:18] <cehteh> well lets see then -[2008-11-12 23:43:26] <cehteh> so DesignProcess .. -[2008-11-12 23:44:02] <cehteh> ichthyo: about ApplicationStructure .. do we want to start that one from scratch together -[2008-11-12 23:44:04] <ichthyo> is there anything we want/need to discuss this time -[2008-11-12 23:44:06] <ichthyo> ? -[2008-11-12 23:44:22] <cehteh> not scratch ... but heavily modifying it? -[2008-11-12 23:44:32] <cehteh> then i place it back as idea -[2008-11-12 23:44:40] <ichthyo> probably yes -[2008-11-12 23:45:13] <cehteh> i think our ideas are again not that different .. just the approach was -[2008-11-12 23:45:45] <ichthyo> most of the things we discussed on the private channel aren't really prominently reflected in this proposal. You can see them rather if you read very carefully -[2008-11-12 23:45:51] <cehteh> ok back to 'idea' -[2008-11-12 23:46:27] <cehteh> yes ... ApplicationStructure was actually misnamed -[2008-11-12 23:46:33] <ichthyo> as proposed: I will make an example in a devel branch, so we can compare? is that ok? -[2008-11-12 23:46:53] <cehteh> dunno -[2008-11-12 23:47:02] <ichthyo> not completely. I wouldn't say misnamed -[2008-11-12 23:47:10] <cehteh> you can just look at mine and modify what you want modified -[2008-11-12 23:47:33] <cehteh> i guess that would yield to earlier results than comparing -[2008-11-12 23:47:36] <ichthyo> because the questions of App structure are close and got into focus by this proposal -[2008-11-12 23:47:40] <cehteh> http://www.lumiera.org/gitweb?p=lumiera/ct;a=blob;f=src/lumiera/lumiera.c;h=3499e795b47292d41b8aae27662e08fa470efb13;hb=0140a048c1376dde8622778617c77cee94df5117 -[2008-11-12 23:47:50] <cehteh> as i saied there isnt much yet -[2008-11-12 23:48:14] <ichthyo> yes. I would just start my branch off at this point of course -[2008-11-12 23:48:18] <cehteh> i can make that compileable with C++ too -[2008-11-12 23:48:24] <cehteh> ok -[2008-11-12 23:48:36] <cehteh> well i can merge your branch into my devel -[2008-11-12 23:48:54] <ichthyo> this branch starts off really near to our current master -[2008-11-12 23:49:01] <ichthyo> which is quite clean and builds well -[2008-11-12 23:49:02] <cehteh> most important points for me where that i created the src/lumiera for the main program -[2008-11-12 23:49:10] <cehteh> it doesnt have to be named lumiera -[2008-11-12 23:49:34] <cehteh> but the important thing is that the config system and the interface/plugin system moved from the backend to that -[2008-11-12 23:49:43] <ichthyo> ah yes. There is the namespace issue we left open on the last meeting -[2008-11-12 23:49:57] <cehteh> heh well i dont want to attend that :) -[2008-11-12 23:50:14] <ichthyo> the problem is: I haven't prepared anything for discussing it -[2008-11-12 23:50:34] <cehteh> but its really oblivious that you dont need to reflect the C++ namespaces 1:1 in the directory structure -[2008-11-12 23:50:44] <ichthyo> I wanted to write a short summary, because I see two or three models of organizing the namespaces -[2008-11-12 23:50:56] <ichthyo> and we could best discuss it if we can all look at this -[2008-11-12 23:51:04] <ichthyo> but I didn't find the time to write it down -[2008-11-12 23:51:28] <cehteh> i stil think having all our stuff into lumiera:: would be good .. but :) -[2008-11-12 23:51:33] <ichthyo> in any case, it is closely interconnected to the "interface namespace": which means the following -[2008-11-12 23:51:50] <cehteh> not all .. tool stuff not -[2008-11-12 23:52:12] <ichthyo> there are some interfaces (opaque datatypes plus forward decls for C, abstract base classes for C++) -[2008-11-12 23:52:22] * cehteh has lumiera_ llist_ PPMPL_ cuckoo_ psplay_ in C ... for example -[2008-11-12 23:52:35] <ichthyo> plus the things defined via the interface/plugin system -[2008-11-12 23:52:44] <ichthyo> this stuff is our "public interface" +[2008-11-12 23:39:26] <ichthyo> and he will rather swich to another application than being forced + to use a compressor he isn't fine with. Same for advanced video stuff +---------------------------- + + +Source code organisation +------------------------ +In the weeks before that meeting some discussions regarding the start-up of the main application +took place, including the question how to load and pull up the Lumiera GTK GUI as a plug-in. +Moreover, _cehteh_ just ``landed'' the branch with the first draft of his new plugin/interface +system. Which prompted us to sort out the locations and arrangement of those parts of the source +dealing with the application framework. The decision was to place that code into a small library, +which we call the *Lumiera common application services* (+liblumieracommon.so+), to be kept separate +from the *support library* (+liblumiera.so+). + +.-- Include dir and namespaces -- +[caption="☉Transcript☉ "] +---------------------------- +[2008-11-12 23:49:43] <ichthyo> There is the namespace issue we left open on the last meeting +[2008-11-12 23:50:34] <cehteh> its really oblivious that you dont need to reflect the C++ namespaces + 1:1 in the directory structure. I still think having all our stuff into lumiera:: + would be good .. but :) +[2008-11-12 23:51:33] <ichthyo> in any case, it is closely interconnected to the "interface namespace": + which means the following: there are some interfaces (opaque datatypes + plus forward decls for C, abstract base classes for C++) + plus the things defined via the interface/plugin system + this stuff is our "public interface" [2008-11-12 23:53:01] <ichthyo> and I really try hard to keep it separate from the implementation -[2008-11-12 23:53:14] <cehteh> the interface/plugin system has now its own rules which are somewhat special and good by that way + +[2008-11-12 23:53:14] <cehteh> the interface/plugin system has now its own rules which are somewhat special + and good by that way. I am thinking about a interface_doc.h which can be used + to generate documentation from the interface language with some special gcc/cpp flags + [2008-11-12 23:53:18] <ichthyo> those interface parts will be contained in a "Lumiera sdk" -[2008-11-12 23:53:40] <ichthyo> and probably all those interfaces (for C++) will go into namespace lumiera:: -[2008-11-12 23:53:51] <ichthyo> because it's just the best and most natural pick -[2008-11-12 23:53:53] <cehteh> i am thinking about a interface_doc.h which can be used to generate documentation from the interface language with some special gcc/cpp flags -[2008-11-12 23:54:30] <ichthyo> and, as said, I really want to try hard to get this interface part a real interface part and cleanly separated from the implementation -[2008-11-12 23:54:55] <cehteh> heh .. seen my config interface i made as example? -[2008-11-12 23:55:07] <ichthyo> meaning: I don't want *.c an *.cpp files in this package/directory/namespace, unless they are really needed there -[2008-11-12 23:55:55] <cehteh> well interfaces themself need to be .c they need to be implemented and the implementation might adapt the code -[2008-11-12 23:56:13] <ichthyo> so the question regarding namespaces is connected to the question: how is the implementation organized in relation to the interface part -[2008-11-12 23:56:17] <cehteh> for example the config system somtimes returns internal state as success/failure indicator -[2008-11-12 23:56:35] <cehteh> but its interface just returns integers -[2008-11-12 23:57:08] <cehteh> ah http://www.lumiera.org/gitweb?p=lumiera/ct;a=tree;f=src/lumiera;h=2160d2cbe84977fc5aa865495b3e3adad154d9dd;hb=0140a048c1376dde8622778617c77cee94df5117 -[2008-11-12 23:57:31] <cehteh> i request some nameing rules for interface files -[2008-11-12 23:57:37] <cehteh> config_interface.c .h ... -[2008-11-12 23:57:49] <cehteh> shall that be _if.c|h -[2008-11-12 23:58:27] <cehteh> because when i make the documentation generator i would like to have some easy pattern to filter interface files out -[2008-11-12 23:58:36] <ichthyo> you see: there is for example a "config.c". Does this file actually contain working code? Then it should be moved over into an imiplementation package -[2008-11-12 23:58:59] <cehteh> that is the src/lumiera -[2008-11-12 23:59:08] <ichthyo> yes, I know -[2008-11-12 23:59:12] <cehteh> i just prelimary dropped the _interface there in -[2008-11-12 23:59:38] <ichthyo> so given, src/lumiera contains our interface (I just take it as an example to make my point more clear) -[2008-11-12 23:59:41] <cehteh> config_interface.h needs to be installed someday -[2008-11-12 23:59:48] <cehteh> (with a better name maybe) + and probably all those interfaces (for C++) will go into namespace lumiera:: + because it's just the best and most natural pick + and, as said, I really want us to try hard to get this interface part + a real interface part and cleanly separated from the implementation + meaning: I don't want *.c and *.cpp files in this package/directory/namespace, + unless they are really needed there + +[2008-11-12 23:55:55] <cehteh> well interfaces themself need to be .c they need to be implemented + and the implementation might adapt the code for example the config system + sometimes returns internal state as success/failure indicator + but its interface just returns integers + +[2008-11-12 23:56:13] <ichthyo> so the question regarding namespaces is connected to the question: + how is the implementation organized in relation to the interface part? + +[2008-11-12 23:59:41] <cehteh> config_interface.h needs to be installed someday (with a better name maybe) +[2008-11-13 00:01:52] <cehteh> do we want a src/includes/ ? +[2008-11-13 00:02:04] <cehteh> for includes which get installed [2008-11-13 00:00:07] <ichthyo> so, someone *using* the interface system probably will need to include config_interface.h [2008-11-13 00:00:19] <ichthyo> but he doesn't need config.c -[2008-11-13 00:00:29] <cehteh> config_interface.c is the mapping/actual implementation ... which gets linked in but the user doesnt need it -[2008-11-13 00:00:33] <cehteh> yes -[2008-11-13 00:00:52] <ichthyo> yes, thats my point. So we should try to separte those cleanly. -[2008-11-13 00:01:01] <ichthyo> Let me explain it on another example -[2008-11-13 00:01:02] <cehteh> what is in config_interface.c could be appended to config.c theoretically .. -[2008-11-13 00:01:08] <cehteh> stop -[2008-11-13 00:01:14] <ichthyo> Let me explain it on another example -[2008-11-13 00:01:28] <cehteh> thats intentionally just placed into one dir to be cleaned up :) +[2008-11-13 00:00:29] <cehteh> config_interface.c is the mapping/actual implementation ... + which gets linked in but the user doesn't need it +[2008-11-13 00:00:52] <ichthyo> yes, thats my point. So we should try to separate those cleanly. + + +[2008-11-12 23:59:38] <ichthyo> so lets assume src/lumiera contains our interface + (I just take it as an example to make my point more clear) +[2008-11-13 00:01:01] <ichthyo> Let me explain it on another more elaborate example + [2008-11-13 00:01:47] <ichthyo> I have a class "Session" which is located in a file session.hpp -[2008-11-13 00:01:52] <cehteh> do we want a src/includes/ ? [2008-11-13 00:01:58] <ichthyo> but it is just a ABC -[2008-11-13 00:02:04] <cehteh> for includes which get installed [2008-11-13 00:02:18] <ichthyo> and I have a Class SessionImpl which is contained in sessionimpl.hpp and sessionimpl.cpp [2008-11-13 00:02:44] <ichthyo> and in the "interface package" there should be *only* session.hpp -[2008-11-13 00:02:46] <cehteh> :] [2008-11-13 00:03:11] <ichthyo> probably this class Session should be moved into namespace lumiera:: [2008-11-13 00:03:16] <ichthyo> then the situation would be clean + [2008-11-13 00:03:32] <ichthyo> note: up to now i wrote only implementation code. -[2008-11-13 00:03:49] |<-- kettuz has left kornbluth.freenode.net ("Leaving") -[2008-11-13 00:03:50] <cehteh> yes +[2008-11-13 00:03:50] <cehteh> yes, me too + [2008-11-13 00:04:07] <ichthyo> but I already made the class hierarchy this way, i.e. everyone *uses* just the class Session [2008-11-13 00:04:28] <ichthyo> and only the class SessManagerImpl knows of the implementation class SessionImpl [2008-11-13 00:04:32] <ichthyo> and so on -[2008-11-13 00:04:50] <cehteh> well thats not important yet... i currently have some 'mess' of includes because its just easier for now to mix them all together and relative easy to clean up when done -[2008-11-13 00:05:30] <ichthyo> same her, at least partially. In most cases I've just created the interface classes with the intention to move them into namespace lumiera:: when I am ready -[2008-11-13 00:05:39] <cehteh> and i dont have a differentiation between interface and implementation headers .. because a) its all implementation code yet -[2008-11-13 00:06:06] <cehteh> b) i was under the impression that i only wanted to export interfaces over 'Interfaces' + +[2008-11-13 00:04:50] <cehteh> well thats not important yet... + I currently have some 'mess' of includes because its just easier for now to mix them + all together and relative easy to clean up when done, and I don't have a differentiation + between interface and implementation headers .. because + a) its all implementation code yet +[2008-11-13 00:06:06] <cehteh> b) i was under the impression that i only wanted to export interfaces + over 'Interfaces' [2008-11-13 00:06:25] <ichthyo> ...which is fine -[2008-11-13 00:06:37] <cehteh> the config_interfaces.h | c stuff there is just the proof of concept -[2008-11-13 00:06:59] <ichthyo> same for most of the stuff I wrote. Just for gettting the tests running for now -[2008-11-13 00:07:18] <ichthyo> and probably the same for joel, right? -[2008-11-13 00:08:24] <cehteh> so .. public interfaces ... place in: [ ] src/include/*.h [ ] src/*/*_interface.h -[2008-11-13 00:08:29] <ichthyo> but... on the long run I am sure we don't just get one "includes" package, rather I guess we gett a (very small) tree of interfaces + +[2008-11-13 00:08:24] <cehteh> so .. public interfaces ... place in: + [ ] src/include/*.h + [ ] src/*/*_interface.h [2008-11-13 00:08:45] <ichthyo> I am for the former: put them into a separate directory -[2008-11-13 00:08:53] <cehteh> ok [2008-11-13 00:08:55] <ichthyo> put them in a clearly separated namespace -[2008-11-13 00:09:19] <cehteh> i just prelimary duped them into that dir because i wanted to talk about that with you and joel -[2008-11-13 00:09:20] <ichthyo> thats the reason behind my keeping the implementation namespaces completely apart -[2008-11-13 00:09:37] <ichthyo> cehteh: thats fine. I didn't critisize it +[2008-11-13 00:08:29] <ichthyo> but... on the long run I am sure we don't just get one "includes" package, + rather I guess we get a (very small) tree of interfaces. + That's the rationale behind my keeping the implementation namespaces completely apart + +[2008-11-13 00:08:53] <cehteh> ok [2008-11-13 00:09:43] <cehteh> the _interface.c is the glue code which can stay in the implementation dir imo -[2008-11-13 00:09:48] <cehteh> heh+ [2008-11-13 00:10:16] <cehteh> actually i wanted a include dir too .. i just didnt start because i wanted to ask you too [2008-11-13 00:10:41] <cehteh> ah another thing i asked joel before .. i dump the tests dir out of the Doxyfile [2008-11-13 00:10:58] <ichthyo> really? -[2008-11-13 00:11:09] <cehteh> main documentation should have less noise, the include graphs are messed up with all test dependencies -[2008-11-13 00:11:11] <cehteh> but: -[2008-11-13 00:11:24] <ichthyo> ture -[2008-11-13 00:11:26] <cehteh> we can make special doxyfiles for special purposes -[2008-11-13 00:11:26] <ichthyo> true -[2008-11-13 00:11:37] <ichthyo> yes... just wanted to propose the same -[2008-11-13 00:11:49] <cehteh> Doxyfile.all Doxyfile.small Doxyfile.browser -[2008-11-13 00:12:20] <ichthyo> because, it generates a nice list of test cases, and often I add siginficant notes just to the test cases, because they are documentation too -[2008-11-13 00:12:24] <cehteh> just the main doc shall be sufficent to get a precise overview about the project with no noise -[2008-11-13 00:12:39] |<-- pedro has left kornbluth.freenode.net (Read error: 104 (Connection reset by peer)) -[2008-11-13 00:12:47] <cehteh> yes the test cases are actually a big part of our specification .. true -[2008-11-13 00:12:53] -->| pedro (n=pedro@32.Red-83-61-132.dynamicIP.rima-tde.net) has joined #lumiera -[2008-11-13 00:13:03] <cehteh> but it add also a lot of mess -[2008-11-13 00:13:31] <cehteh> i just noticed that today while browsing the docs .. -[2008-11-13 00:13:34] <ichthyo> yes, thats fine. So we make a "small" doc set with just the core facilities, and a "complete" doc set, which includes: test cases and documentation on private members -[2008-11-13 00:13:59] <cehteh> some include graphs are half as big and much clearer when test is excluded -[2008-11-13 00:14:05] <ichthyo> so someone really needing to find out how something works in detail can use the "complete" set. -[2008-11-13 00:14:28] <cehteh> .small is another .. i have that already here privately -[2008-11-13 00:14:40] <cehteh> that turns most graphs and code inlining off -[2008-11-13 00:14:43] <ichthyo> At the end, I bet we will have almost 60% test code and 30% actual code -[2008-11-13 00:15:07] <cehteh> producing something really compact which might be printed on dead trees eventually -[2008-11-13 00:15:09] <ichthyo> or 40% -[2008-11-13 00:15:51] <cehteh> but defining some doxyfile suites wont be a problem anyways -[2008-11-13 00:16:29] <ichthyo> ok, back to the include: My proposal would be to use src/lumiera/... and namespace lumiera::... (and its children) for the public interfaces -[2008-11-13 00:16:38] * cehteh prepares a few next days -[2008-11-13 00:17:04] <cehteh> mhm thats the usual reveral operation -[2008-11-13 00:17:15] <ichthyo> ? +[2008-11-13 00:11:09] <cehteh> main documentation should have less noise, + the include graphs are messed up with all test dependencies. + But we can make special doxyfiles for special purposes + Doxyfile.all Doxyfile.small Doxyfile.browser + +[2008-11-13 00:13:34] <ichthyo> yes, thats fine. + So we make a "small" doc set with just the core facilities, and a "complete" doc set, + which includes: test cases and documentation on private members, so someone really + needing to find out how something works in detail can use the "complete" set. + At the end, I bet we will have almost 60% test code and 40% actual code + +[2008-11-13 00:16:29] <ichthyo> ok, back to the include dir question: + My proposal would be to use src/lumiera/... and namespace lumiera::... + (and its children) for the public interfaces + +[2008-11-13 00:17:04] <cehteh> mhm thats the usual reversal operation [2008-11-13 00:17:31] <cehteh> src/lumiera/include/ ... gets installed in /usr/include/lumiera :P [2008-11-13 00:17:40] <cehteh> ok you dont have the include -[2008-11-13 00:17:52] <cehteh> but i rather use the name include +[2008-11-13 00:17:52] <cehteh> but i'd rather use the name include [2008-11-13 00:17:59] <ichthyo> so src/lumiera/** -> /usr/include/lumiera/** -[2008-11-13 00:18:03] <cehteh> yes +[2008-11-13 00:18:22] <ichthyo> this would make the namespaces match 100% to the directories + [2008-11-13 00:18:13] <cehteh> i dont like that much [2008-11-13 00:18:18] <cehteh> better src/include [2008-11-13 00:18:22] <cehteh> rationale: -[2008-11-13 00:18:22] <ichthyo> this would make the namespaces match 100% to the directories -[2008-11-13 00:18:43] <cehteh> when i look at some project i dont know and see a ./include i know that there are the interfaces -[2008-11-13 00:19:14] <cehteh> if it is ./projectname then i expect there a huge tree of code which makes the project -[2008-11-13 00:20:04] <cehteh> which is actually true for most project .. at least i dont know anyone which names its includes projectname +[2008-11-13 00:18:43] <cehteh> when i look at some project i dont know and see a ./include + i know that there are the interfaces. If it is ./projectname then + i expect there a huge tree of code which makes the project +[2008-11-13 00:20:04] <cehteh> which is actually true for most project ... + at least i dont know anyone which names its includes projectname [2008-11-13 00:20:14] <ichthyo> thats a good argument [2008-11-13 00:20:23] <cehteh> the projectname/include is somewhat common .. but bit bloated [2008-11-13 00:20:48] <cehteh> so better include [2008-11-13 00:20:49] <ichthyo> yeah, thats a good point -[2008-11-13 00:20:59] <ichthyo> so namespace lumiera:: - + [2008-11-13 00:21:04] <ichthyo> so namespace lumiera:: -> /src/include -[2008-11-13 00:21:09] <cehteh> yes [2008-11-13 00:21:35] <ichthyo> namespace lumiera::edit -> /src/include/edit (just as an example) -[2008-11-13 00:21:45] <cehteh> nah + [2008-11-13 00:22:00] <ichthyo> while namespace proc::builder -> src/proc/builder -[2008-11-13 00:22:01] <cehteh> /src/include/edit.h would suffice -[2008-11-13 00:22:22] <cehteh> well if you want (boost does this) -[2008-11-13 00:22:25] <ichthyo> no, I mean a really sub-namespace within the interfaces -[2008-11-13 00:22:31] <cehteh> but i think keeping it flat would be ok -[2008-11-13 00:22:58] <ichthyo> I didn't say I want it. But it may happen to be necessary or just better, when the root of the interfaces gets too big -[2008-11-13 00:23:00] <cehteh> at least i dont plan to make a tree behind it + +[2008-11-13 00:22:31] <cehteh> but i think keeping it flat would be ok +[2008-11-13 00:22:58] <ichthyo> I didn't say I want it. But it may happen to be necessary or just better, + when the root of the interfaces gets too big +[2008-11-13 00:23:00] <cehteh> at least i dont plan to make a tree behind it [2008-11-13 00:23:08] <ichthyo> neither do I [2008-11-13 00:23:11] <ichthyo> but... well -[2008-11-13 00:23:14] <cehteh> i doubt that it will get very big +[2008-11-13 00:23:14] <cehteh> i doubt that it will get very big [2008-11-13 00:23:29] <ichthyo> I've seen enough projects to prove the contrary -[2008-11-13 00:23:37] <cehteh> even with 100 files there it will still be manageable [2008-11-13 00:23:45] <ichthyo> and when an interface dir as 60 entries it starts to get messy -[2008-11-13 00:24:02] <cehteh> and if not .. we may reconsider that before we release -[2008-11-13 00:24:38] <cehteh> a deepber hierachy which is very sparse is much more annoying than a dir with up to 100 files -[2008-11-13 00:24:49] <ichthyo> yes, but anyway, the important point is I really want to try hard to get public interfaces and implementation code cleanly separated +[2008-11-13 00:23:37] <cehteh> even with 100 files there it will still be manageable +[2008-11-13 00:24:02] <cehteh> and if not .. we may reconsider that before we release +[2008-11-13 00:24:38] <cehteh> a deeper hierarchy which is very sparse is much more annoying + than a dir with up to 100 files + +[2008-11-13 00:24:49] <ichthyo> but anyway, the important point is I really want to try hard to get public interfaces + and implementation code cleanly separated [2008-11-13 00:25:31] <cehteh> for C++ absolutely .. for C i less that a *little* bit -[2008-11-13 00:25:54] <cehteh> for performance reasons and because most of the backend wont end in public interfaces anyways +[2008-11-13 00:25:54] <cehteh> for performance reasons and because most of the backend wont end in public interfaces anyway + [2008-11-13 00:26:27] <ichthyo> just out of curiosity: what are the performance reasons? -[2008-11-13 00:26:51] <cehteh> i have some static inline accessor functions . which need to be in the .h and need full public structure definitions which expose private details +[2008-11-13 00:26:51] <cehteh> i have some static inline accessor functions .. + which need to be in the .h and need full public structure definitions + which expose private details. I think these will stay private + but if not i wont really care. + [2008-11-13 00:27:06] <ichthyo> ah, I see -[2008-11-13 00:27:08] <cehteh> i think these will stay private -[2008-11-13 00:27:08] <ichthyo> of course -[2008-11-13 00:27:19] <cehteh> but if not i wont really care [2008-11-13 00:27:59] <ichthyo> probably those just count as "reasonable exceptions from the rule" -[2008-11-13 00:28:06] <cehteh> yes -[2008-11-13 00:28:26] <ichthyo> and besides: also within the implementation of one layer, there are further interfaces [2008-11-13 00:28:33] <cehteh> well C has no access protection at all .. you have to do whats allowed/documented [2008-11-13 00:28:47] <cehteh> any improvisation counts as undefined behaviour .. point -[2008-11-13 00:28:57] <ichthyo> e.g the builder has an interface -[2008-11-13 00:29:13] <cehteh> of course -[2008-11-13 00:29:37] <cehteh> i have some deeply hidden things no one needs to know -[2008-11-13 00:29:58] <ichthyo> joelholdsworth: I've seen that you already changed some namespaces in the GUI part, is that correct, or am I mixing something up now? -[2008-11-13 00:30:02] <cehteh> but these have interfaces in the backend -[2008-11-13 00:30:17] <ichthyo> (and same for the proc) -[2008-11-13 00:31:01] <cehteh> btw: -[2008-11-13 00:31:04] <cehteh> LumieraFile file = lumiera_file_new ("filename", mode); -[2008-11-13 00:31:05] <cehteh> LumieraFrameIndex index = lumiera_frameindex_new ("indexfilename", file /*, indexing engine*/); -[2008-11-13 00:31:05] <cehteh> LumieraFrame frame = lumiera_frameindex_frame (index, 123); -[2008-11-13 00:31:25] <joelholdsworth> yes I fixed the namespaces -[2008-11-13 00:31:31] <joelholdsworth> so no more lumiera:: -[2008-11-13 00:31:33] <cehteh> ... thats the brainstorming state how to access frames -[2008-11-13 00:32:03] <ichthyo> actually, I thought we could discuss it further... and I wanted to write something down. -[2008-11-13 00:32:05] <cehteh> mhm not fully complete ... but sufficently -[2008-11-13 00:32:17] <ichthyo> But I may well tell it just informally if that's ok -[2008-11-13 00:32:30] <ichthyo> basically I see two scenarios -[2008-11-13 00:33:02] <ichthyo> I allways assume we have separate interface namespace(s) and implementation is in a different namespace then the exported interface -[2008-11-13 00:33:16] <ichthyo> so, the first scenario I'd call "libarry type" -[2008-11-13 00:33:27] <ichthyo> it means that the implementation namespace is nested -[2008-11-13 00:33:54] <ichthyo> eg namespace mylib <-- the stuff the library users include and use in their code + +[2008-11-13 00:28:26] <ichthyo> and besides: also within the implementation of one layer, + there are further interfaces, e.g the builder has an interface + + +[2008-11-13 00:32:03] <ichthyo> actually, I wanted to write something down, but couldn't find the time +[2008-11-13 00:32:17] <ichthyo> But I may well tell it just informally if that's ok ? + +[2008-11-13 00:33:02] <ichthyo> I always assume we have separate interface namespace(s) + and implementation is in a different namespace then the exported interface + basically I see two scenarios: + + so, the first scenario I'd call "libarry type" + it means that the implementation namespace is nested + eg namespace mylib <-- the stuff the library users include and use in their code [2008-11-13 00:34:23] <ichthyo> namespace mylib::impl and mylib::impl::special <- the actual code making the library work -[2008-11-13 00:34:35] <ichthyo> this is the first scenario -[2008-11-13 00:34:50] <ichthyo> second scenario is rathe what I was aiming for -[2008-11-13 00:35:13] <ichthyo> namespace lumiera <-- the interfaces -[2008-11-13 00:35:45] <ichthyo> namespace proc::builder:.... <-- the implementation has a completely separate hierarchy for each subsystem + +[2008-11-13 00:34:50] <ichthyo> second scenario is rather what I was aiming for + namespace lumiera <-- the interfaces +[2008-11-13 00:35:45] <ichthyo> namespace proc::builder:.... <-- the implementation has a completely separate hierarchy + for each subsystem + [2008-11-13 00:36:08] <ichthyo> my rationale for prefering the second aproach is: -[2008-11-13 00:36:32] <ichthyo> in our case, we are "implementation heavy". We will certainly have much more implementation code than interfaces -[2008-11-13 00:37:17] <ichthyo> we often will build more nested namespaces for the implementation, but can live with just one shallow public interface -[2008-11-13 00:38:07] <ichthyo> besides, you can just grab one directory and install it as "include", the way we discussed just -[2008-11-13 00:38:36] <ichthyo> for a real library, the situation is reversed: Often you have much code close to the interface, maybe even inline -[2008-11-13 00:38:41] <ichthyo> e.g. think at boost -[2008-11-13 00:39:14] <ichthyo> half of the code is immediately inline within the interface classes you use (I don't say I like this... but well) -[2008-11-13 00:39:34] <ichthyo> and the more technical parts are in shallow sub-namespaces below -[2008-11-13 00:40:12] <ichthyo> the good side of course is, that the implementation code doesn't need to pull in the interface, because he already sees it because of the nesting. -[2008-11-13 00:41:00] <ichthyo> but for our situation, for me, the need to pull in explicitely any interface you want to use or implement counts rather as a good thing -[2008-11-13 00:41:19] <ichthyo> because it clearly documents what non-local parts you use -[2008-11-13 00:41:43] <ichthyo> that was my rationale for keeping the implementation part in a completely separate hierarchy +[2008-11-13 00:36:32] <ichthyo> in our case, we are "implementation heavy". + We will certainly have much more implementation code than interfaces + we often will build more nested namespaces for the implementation, + but can live with just one shallow public interface + besides, you can just grab one directory and install it as "include", + the way we discussed + for a real library, the situation is reversed: Often you have much code close + to the interface, maybe even inline + e.g. think at boost: + half of the code is immediately inline within the interface classes you use + and the more technical parts are in shallow sub-namespaces below + the good side of course is, that the implementation code doesn't need + to pull in the interface, because he already sees it because of the nesting. + but for our situation, for me, the need to pull in explicitly any interface + you want to use or implement counts rather as a good thing + because it clearly documents what non-local parts you use + that was my rationale for keeping the implementation part + in a completely separate hierarchy. + [2008-11-13 00:43:17] <ichthyo> joelholdsworth: so you moved your gui code within namespace gui, is that correct? [2008-11-13 00:43:50] <joelholdsworth> yes that's right! -[2008-11-13 00:43:59] <ichthyo> because, then the issue seems to be settled. Originally I had still more shallow trees. But your argument with the matching directory names and the consistency is an important point -[2008-11-13 00:44:29] <ichthyo> so then, I'll go ahead and move my implementationn code into a new root namespace proc +[2008-11-13 00:43:59] <ichthyo> because, then the issue seems to be settled. + Originally I had yet more shallow trees. + But your argument with the matching directory names and the consistency + is an important point. So then, I'll go ahead and move my implementationn code + into a new root namespace proc and then start cleaning up the library part [2008-11-13 00:44:47] <joelholdsworth> great :) -[2008-11-13 00:44:48] <ichthyo> and then start cleaning up the library part [2008-11-13 00:45:20] <ichthyo> meaning, you can expect quite some stuff moving between /src/common and /src/lib [2008-11-13 00:45:40] <ichthyo> on the long run, probably the intention should be for "common" to disappear -[2008-11-13 00:46:08] <ichthyo> either stuff goes into namespace lumiera::, meaning it's an interface and the header should go into "src/include" -[2008-11-13 00:46:30] <ichthyo> or stuff is heavy support lib implementation stuff and thus goes below src/lib -[2008-11-13 00:47:06] <ichthyo> which brings us to another question (for my part it's the last question for today) -[2008-11-13 00:47:08] <ichthyo> :-) +[2008-11-13 00:46:08] <ichthyo> either stuff goes into namespace lumiera::, meaning it's an interface + and the header should go into "src/include" or stuff is heavy support lib + implementation stuff and thus goes below src/lib +---------------------------- + + +.-- build system, plugin tree -- +[caption="☉Transcript☉ "] +---------------------------- +[2008-11-13 00:47:06] <ichthyo> which brings us to another question [2008-11-13 00:47:18] <ichthyo> how do we organize building plugins? -[2008-11-13 00:47:27] <ichthyo> I mean, the plugins which are in-tree + because: one plugin may contain several source files and headers, + but needs to be linked into one module + [2008-11-13 00:47:55] <cehteh> plugins dir -[2008-11-13 00:48:00] <ichthyo> yes, exactly [2008-11-13 00:48:11] <cehteh> and a tree there [2008-11-13 00:48:22] <cehteh> plugins/video/effects -[2008-11-13 00:48:24] <ichthyo> background is: i am aming to get the build process as much rules-directed as possible -[2008-11-13 00:48:27] <ichthyo> yes -[2008-11-13 00:48:28] <cehteh> etc -[2008-11-13 00:48:54] <ichthyo> can we come up with a rule about how plugins will be built? -[2008-11-13 00:49:15] <ichthyo> because: one plugin may contain several source files and headers, but needs to be linked into one module -[2008-11-13 00:49:16] <cehteh> i am curious if plugin building with scons produces the same results as autoconfg [2008-11-13 00:49:56] <cehteh> plugins/video/effects/foo/ ... with foobar.c foobaz.c links to foo.lum -[2008-11-13 00:50:20] <ichthyo> well ... sorry +[2008-11-13 00:52:38] <cehteh> plugins/video/effects/foo/foo.c builds foo.lum and + installs it in $(pkglibdir)/plugins/video/effects/foo.lum + +[2008-11-13 00:48:24] <ichthyo> background is: I am aiming to get the build process as much rules-directed as possible +[2008-11-13 00:48:27] <ichthyo> thus: +[2008-11-13 00:48:54] <ichthyo> can we come up with a rule about how plugins will be built? [2008-11-13 00:50:33] <ichthyo> again the notorious namespace question -[2008-11-13 00:50:34] <cehteh> (or foo.so) -[2008-11-13 00:50:40] <cehteh> heh -[2008-11-13 00:51:08] <ichthyo> if it's a rather large plugin, e.g. a plugin providing an adapter for an external media type libarary or such stuff -[2008-11-13 00:51:16] <ichthyo> then it probably can have nested namespaces -[2008-11-13 00:51:30] <cehteh> huh? +[2008-11-13 00:51:08] <ichthyo> if it's a rather large plugin, e.g. a plugin providing an adapter for an + external media type libarary or such stuff then it probably can have nested namespaces [2008-11-13 00:51:56] <cehteh> we really dont need a 1:1 relation between dirs and namespaces -[2008-11-13 00:52:08] <ichthyo> so, then, how can I tell at what level of the tree below plugins I have to start with building one shared libarary? -[2008-11-13 00:52:32] <ichthyo> would the following rule be ok for you? -[2008-11-13 00:52:38] <cehteh> plugins/video/effects/foo/foo.c builds foo.lum and installs it in $(pkglibdir)/plugins/video/effects/foo.lum +[2008-11-13 00:52:08] <ichthyo> so, then, how can the build system tell at what level of the tree below plugins + we'd need to start with building one shared libarary? + [2008-11-13 00:52:46] <ichthyo> would the following rule be ok for you? [2008-11-13 00:52:58] <ichthyo> start with dir src/plugins and descend [2008-11-13 00:53:12] <ichthyo> depth-first tree search [2008-11-13 00:53:28] <ichthyo> when you enter a directory which contains a real source file -[2008-11-13 00:53:31] <cehteh> well i just suggesting here .. for the plugin loader ist relative simple [2008-11-13 00:53:46] <ichthyo> then build everything below it into one shared module + +[2008-11-13 00:53:31] <cehteh> well i just suggesting here .. for the plugin loader ist relative simple [2008-11-13 00:53:56] <cehteh> it just searches in the paths you give him (and doesnt descend itself) [2008-11-13 00:54:19] <ichthyo> no, I really want to configure as few pathes as possible [2008-11-13 00:54:23] <cehteh> if you think thats ok, then do it -[2008-11-13 00:54:29] <ichthyo> would this be ok -[2008-11-13 00:54:57] <ichthyo> the limitation is: you can't just put some isolated code file *.c *cpp in some of the root directories + +[2008-11-13 00:54:57] <ichthyo> note, the limitation is: you can't just put some isolated code + file *.c *cpp in some of the root directories... + the moment you add a *c or *cpp file in some new sub tree, + you'll find a <subtree-rootdirname>.lum in the corresponding bin dir + [2008-11-13 00:55:17] <cehteh> thats prolly a good choice anyways [2008-11-13 00:55:28] <cehteh> plugins shall be only at the leaves of the tree -[2008-11-13 00:55:29] <ichthyo> but the nice part is: I write a small python function and am finished with the plugins issue altogether + [2008-11-13 00:55:42] <cehteh> be careful when linking [2008-11-13 00:56:16] <cehteh> the autoconf build included a lot libs from configure which where not needed for plugins -[2008-11-13 00:56:18] <ichthyo> the moment you add a *c or *cpp file in some new sub tree, you'll fine a <subtree-rootdirname>.lum in the corresponding bind dir + [2008-11-13 00:56:57] <ichthyo> besides, I will set up a different "build environment" for plugins [2008-11-13 00:57:16] <ichthyo> in SCons, each build environment has a fixed set of libraries attached [2008-11-13 00:57:28] <cehteh> you likely need specialized ones for some plugins [2008-11-13 00:57:43] <cehteh> but you can inherit them -[2008-11-13 00:57:46] |<-- dmj726 has left kornbluth.freenode.net (Read error: 104 (Connection reset by peer)) -[2008-11-13 00:57:50] <ichthyo> currently I have two environments. The standard, and the enviromenent for the GUI code [2008-11-13 00:58:02] <ichthyo> yes, I can inherit them [2008-11-13 00:58:12] <ichthyo> and I match them from the directory name -[2008-11-13 00:58:14] * cehteh thinks [2008-11-13 00:58:20] <ichthyo> the rest is fully automatic -[2008-11-13 00:58:26] -->| dmj726 (n=david@c-24-1-30-137.hsd1.il.comcast.net) has joined #lumiera + +[2008-11-13 00:58:14] * cehteh thinks [2008-11-13 00:58:38] <cehteh> a bare plugin doesnt need any lib -[2008-11-13 00:58:42] <ichthyo> compiling, linking, switches, includes [2008-11-13 00:59:04] <ichthyo> not even the lumiera support lib? [2008-11-13 00:59:11] <ichthyo> liblumiera.a [2008-11-13 00:59:12] <ichthyo> ? [2008-11-13 00:59:29] <cehteh> nope [2008-11-13 00:59:43] <cehteh> only if it needs somthing from that -[2008-11-13 01:00:00] <ichthyo> yes, true... but probably you can guess my goal? + [2008-11-13 01:00:01] <cehteh> -DLUMIERA_PLUGIN for compiling plugins -[2008-11-13 01:00:06] <ichthyo> thats clear [2008-11-13 01:00:11] <ichthyo> plus libdl -[2008-11-13 01:00:24] <cehteh> no -[2008-11-13 01:00:34] <cehteh> libdl actually breaks the isolation :P +[2008-11-13 01:00:24] <cehteh> stop. no +[2008-11-13 01:00:34] <cehteh> libdl actually breaks the isolation :P [2008-11-13 01:00:41] <ichthyo> ah, only for the part which loads the lib [2008-11-13 01:00:51] <cehteh> the host needs libdl not the plugin [2008-11-13 01:00:54] <ichthyo> but what is when a plugin wants to open another interface [2008-11-13 01:01:11] <cehteh> it asks the host to do it [2008-11-13 01:01:17] <ichthyo> doesn't it need to link against your plugin system impl? [2008-11-13 01:01:32] <cehteh> nope .. just the interface.h -[2008-11-13 01:01:49] <ichthyo> ok -[2008-11-13 01:02:14] <cehteh> (which pull a lot cruft in currently .. ) -[2008-11-13 01:02:29] <ichthyo> but probably you understand my goal: I want to make the build as automatic and rules based as possible [2008-11-13 01:02:49] <cehteh> interface.h defines some functions .. but these are not available for the plugin either + but the nature of plugins is to extend the system .. + thus follows that they will need some more specific libs in many cases + +[2008-11-13 01:02:29] <ichthyo> please understand my goal: I want to make the build as automatic and rules based as possible [2008-11-13 01:02:58] <cehteh> yes ... -[2008-11-13 01:03:09] <ichthyo> for SCons, this is already mostly the case currently, but the autotools build is a maintainance nightmare right now +[2008-11-13 01:03:09] <ichthyo> for SCons, this is already mostly the case currently, + but the autotools build is a maintainance nightmare right now + because the paths and dependencies are so much hard wired there [2008-11-13 01:03:24] <ichthyo> i spent already a considerable time to fix it again and again -[2008-11-13 01:03:32] <cehteh> but the nature of plugins is to extend the system .. thus follows that they will need some more specific libs in many cases -[2008-11-13 01:03:39] <ichthyo> because the paths and dependencies are so much hard wired there [2008-11-13 01:03:52] <cehteh> huh i feel comfortable with autotools -[2008-11-13 01:04:04] <ichthyo> fine, but I fix it all the time -[2008-11-13 01:04:17] <cehteh> but i secretly hacked a bit on the prolog build system idea some time ago -[2008-11-13 01:04:35] <cehteh> maybe i do some more when i am in mood for it -[2008-11-13 01:05:19] <ichthyo> and now, after I will start using interfaces separate, there will be much more refactoreing -[2008-11-13 01:05:28] <cehteh> check_LTLIBRARIES += examplepluginc.la -[2008-11-13 01:05:28] <cehteh> examplepluginc_la_SOURCES = $(tests_srcdir)/lumiera/example_plugin.c -[2008-11-13 01:05:29] <cehteh> examplepluginc_la_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -DLUMIERA_PLUGIN -I$(top_srcdir)/src/ -[2008-11-13 01:05:29] <cehteh> examplepluginc_la_LDFLAGS = -module -avoid-version -no-undefined -rpath /dev/null -[2008-11-13 01:05:51] <cehteh> mhm the LDFLAGS .. how do you do that in scons? -[2008-11-13 01:06:10] <cehteh> you can build a normal versioned .so.1.2 lib -[2008-11-13 01:06:39] <cehteh> or moment no only .so +[2008-11-13 01:04:04] <ichthyo> fine for you, but I fix it all the time + [2008-11-13 01:06:44] <ichthyo> for now I didn't use any special switches [2008-11-13 01:06:59] <ichthyo> I just defined exampleplugin to be an dynamic module [2008-11-13 01:07:10] <cehteh> sounds reasonable -[2008-11-13 01:07:41] <ichthyo> as said, in future I'll tell scons "build everything below this root dir into a dynamic module" -[2008-11-13 01:07:57] <ichthyo> and scons will derive the necessary compile and link commands -[2008-11-13 01:07:58] <cehteh> first autotools stuff linked xlibs and all crap with the plugin that was a bug -[2008-11-13 01:08:29] <ichthyo> as said, I will make a new third build environment for plugins -[2008-11-13 01:08:41] <ichthyo> currently I have one env for the core and one for the GUI part -[2008-11-13 01:08:45] <cehteh> -rwxrwx--- 1 ct ct 23K Nov 7 09:28 examplepluginc.so -[2008-11-13 01:08:53] <ichthyo> yes -[2008-11-13 01:08:59] <cehteh> if its around that size its prolly ok -[2008-11-13 01:09:39] <ichthyo> and anyway, we can adjust the compiler/linker switches in SCons for each build environment -[2008-11-13 01:09:47] <ichthyo> of course with inheritance -[2008-11-13 01:10:22] <ichthyo> well.. fine... +[2008-11-13 01:07:41] <ichthyo> as said, in future I'll tell scons + "build everything below this root dir into a dynamic module" + and scons will derive the necessary compile and link commands + [2008-11-13 01:10:32] <ichthyo> ah, another little issue [2008-11-13 01:10:42] <ichthyo> we have two tools directories currently [2008-11-13 01:10:53] <ichthyo> one as "src/tool" @@ -914,194 +646,117 @@ Conclusion [2008-11-13 01:11:12] <ichthyo> just "admin" [2008-11-13 01:11:29] <ichthyo> within admin, there is the icon rendering and the vgsuppression [2008-11-13 01:12:01] <ichthyo> the idea was, that all this special support tools would be in src/tool -[2008-11-13 01:12:05] <cehteh> LUMIERA_PLUGIN_PATH=.libs ./test-interfaces plugin_examplepluginc_nested -[2008-11-13 01:12:17] <cehteh> .. dont forget to set the PLUGIN_PATH for tests -[2008-11-13 01:12:52] <ichthyo> huh? [2008-11-13 01:12:52] <cehteh> admin are administrative scripts .. tools shall be moved into src/tool [2008-11-13 01:13:00] <ichthyo> ok [2008-11-13 01:13:07] <ichthyo> so joelholdsworth: [2008-11-13 01:13:29] <ichthyo> would it be ok for you to move the icon building executable into src/tool ? [2008-11-13 01:13:41] <ichthyo> and probably vgsuppression too? -[2008-11-13 01:13:58] <cehteh> dunno .. leave that there [2008-11-13 01:14:00] <ichthyo> so the build process doesn't need to build anything in admin -[2008-11-13 01:14:12] <cehteh> because the vgsuppression.sh is a administrative script :P -[2008-11-13 01:14:17] <cehteh> heh -[2008-11-13 01:14:29] <ichthyo> but vgsuppression.c is built like a tool -[2008-11-13 01:14:33] <ichthyo> and the iconrenderer too -[2008-11-13 01:14:37] <cehteh> not even that -[2008-11-13 01:14:47] <cehteh> just dont build it .. its unfinished code -[2008-11-13 01:15:03] <ichthyo> ok.. then I stop building it with scons too -[2008-11-13 01:15:05] <cehteh> vgsuppression needs some maintenance -[2008-11-13 01:15:13] <cehteh> yes thats ok for now -[2008-11-13 01:15:25] <ichthyo> but the rsvg-convert.c [2008-11-13 01:15:29] <joelholdsworth> yes that's ok! [2008-11-13 01:15:39] <joelholdsworth> that really is just a tool [2008-11-13 01:15:43] <ichthyo> that is built lilke a tool, so I'd like to move it into src/tool -[2008-11-13 01:16:14] <cehteh> mhm or move vgsuppression.c over to tools [2008-11-13 01:16:15] <joelholdsworth> it's the standard rsvg utility with some options added -[2008-11-13 01:16:23] <ichthyo> and then I can ditch the admin/SConscript alltogether -[2008-11-13 01:16:32] <cehteh> i dont care for it anytime soon .. so its oh when you move it -[2008-11-13 01:16:58] <ichthyo> a propos: joelholdsworth: I've built some additional directory checks into the accompaning python script -[2008-11-13 01:17:29] <ichthyo> you have probably noticed that I call directly from the SConstruct into your python script to invoke the icon rendering -[2008-11-13 01:17:30] <joelholdsworth> ok -[2008-11-13 01:17:50] <ichthyo> and I built some additional cheks and move non-empty dirs away as "*.bak" +[2008-11-13 01:16:58] <ichthyo> a propos: joelholdsworth: + I've built some additional directory checks into the accompanying python script + you have probably noticed that I call directly from the SConstruct into your + python script to invoke the icon rendering, and I built some additional checks + and move non-empty dirs away as "*.bak" [2008-11-13 01:17:50] <joelholdsworth> I hadn't actually looked -[2008-11-13 01:17:53] <cehteh> -rwxrwx--- 1 ct ct 9.2K Nov 13 01:17 examplepluginc.so -[2008-11-13 01:17:56] <cehteh> hah stripped [2008-11-13 01:17:59] <joelholdsworth> but yes that makes perfect snese -[2008-11-13 01:18:14] <ichthyo> the icon rendering works well in the SCons build too [2008-11-13 01:18:31] <joelholdsworth> actually it is supposed to overwrite any prior renderings [2008-11-13 01:18:39] <ichthyo> ah? [2008-11-13 01:19:00] <joelholdsworth> so if you change the SVG the PNGs get created fresh -[2008-11-13 01:19:01] <ichthyo> so I could chane it and just do a recursive remove if the target dir exists? +[2008-11-13 01:19:01] <ichthyo> so I could change it and just do a recursive remove if the target dir exists? [2008-11-13 01:19:10] <joelholdsworth> yes -[2008-11-13 01:19:31] <ichthyo> fine. But anyway, I'll leave in the move-away in case the target name exists as file [2008-11-13 01:19:32] <joelholdsworth> like obj files - it's supposed to just overwrite any previous bits -[2008-11-13 01:19:50] <joelholdsworth> well the targets are just build termoraries +[2008-11-13 01:19:50] <joelholdsworth> well the targets are just build temporaries [2008-11-13 01:20:01] <ichthyo> ok -[2008-11-13 01:20:52] <ichthyo> yes, SCons uses a md5 or sha hash on the source files by default. So if you change the contents of a SVG, it figures out all PNGs which depend on it and rebuilds them -[2008-11-13 01:21:21] <joelholdsworth> clever! [2008-11-13 01:22:25] <ichthyo> ok. then regariding the build system, the only bit of cleanup is the tests -[2008-11-13 01:22:33] <joelholdsworth> anyway... time for bed -[2008-11-13 01:22:33] -->| nick4 (n=fffeop@adsl205-29.kln.forthnet.gr) has joined #lumiera [2008-11-13 01:22:46] <ichthyo> cehteh: we talked about it when I visited you [2008-11-13 01:23:02] <ichthyo> it's not urgent right now [2008-11-13 01:23:14] <ichthyo> but.. as we agreed at that time [2008-11-13 01:23:39] <ichthyo> we should make a separate tree for the test-support code, maybe in a subdirectory of /tests [2008-11-13 01:24:01] <ichthyo> and this tree of course reflects the main tree. That was the idea if I recall correct -[2008-11-13 01:24:16] |<-- joelholdsworth has left kornbluth.freenode.net (Remote closed the connection) -[2008-11-13 01:24:38] |<-- kaarna has left kornbluth.freenode.net ("Ex-Chat") -[2008-11-13 01:25:02] <ichthyo> we could move your /tests/test.h in the root of this test-support tree [2008-11-13 01:25:11] <cehteh> ichthyo: yes .. i know [2008-11-13 01:25:13] <cehteh> pending [2008-11-13 01:25:18] <ichthyo> yes, not urgent [2008-11-13 01:25:26] <ichthyo> works as-is for now [2008-11-13 01:25:45] <ichthyo> I have some similar cleanup for my part -[2008-11-13 01:25:58] <cehteh> you might noticed that i placed new code in proper order .. just the old cruft need to be cleaned up -[2008-11-13 01:26:09] <ichthyo> yes, thanks, I noted it -[2008-11-13 01:26:34] <ichthyo> resulting in most of the special treatment functions in tests/SConscript to disappear :-) -[2008-11-13 01:27:02] <ichthyo> even the plugin test is much more simple right now :) -[2008-11-13 01:27:14] <cehteh> ah -[2008-11-13 01:27:19] <cehteh> back to before + [2008-11-13 01:27:24] <ichthyo> btw: do we need "test plugins"? -[2008-11-13 01:27:32] <cehteh> we settled with src/include now -[2008-11-13 01:27:46] <cehteh> that means src/lumiera remains for the main [2008-11-13 01:27:52] <ichthyo> i.e. something similar to the src/plugin subdirs we discussed just a moment ago -[2008-11-13 01:28:04] <ichthyo> yes src/lumiera remains for main and startup -[2008-11-13 01:28:30] <ichthyo> btw: do we need "test plugins"? -[2008-11-13 01:28:34] <cehteh> main and startup and config and interface/plugin -[2008-11-13 01:28:41] <cehteh> so to test-plugins [2008-11-13 01:28:53] <cehteh> you mean some which get installed? [2008-11-13 01:29:09] <ichthyo> no, just plugins which are needed only from within tests [2008-11-13 01:29:18] <cehteh> yes we need them [2008-11-13 01:29:19] <ichthyo> i.e. /tests/plugin [2008-11-13 01:29:39] <ichthyo> and everything below will be built exactly similar as everything below /src/plugin [2008-11-13 01:29:44] <cehteh> i currently build them in place example_plugin.c -[2008-11-13 01:29:46] <cehteh> etc [2008-11-13 01:29:54] <ichthyo> just it will be only used for test runs -[2008-11-13 01:29:56] <ichthyo> yes -[2008-11-13 01:30:06] <cehteh> if you want we can move them to tests/plugins someday -[2008-11-13 01:30:18] <ichthyo> so example_plugin.c would move over into /tests/plugin/example -[2008-11-13 01:30:27] <cehteh> i also thought about test plugins which get installed -[2008-11-13 01:30:28] <ichthyo> or something similar one day -[2008-11-13 01:30:36] <cehteh> to test the installation -[2008-11-13 01:30:37] <ichthyo> yes -[2008-11-13 01:30:48] <ichthyo> but I think those should go below src/plugin -[2008-11-13 01:31:02] <ichthyo> or do you consider them to be part of the test system? -[2008-11-13 01:31:05] <cehteh> maybe the example_plugin can just move to be installed -[2008-11-13 01:31:24] <cehteh> and in production it will just be blacklisted by the plugindb later -[2008-11-13 01:31:41] <cehteh> then not -[2008-11-13 01:31:48] <ichthyo> we could also use diagnostics plugins -[2008-11-13 01:31:58] <ichthyo> which are similarily blacklisted -[2008-11-13 01:32:18] <cehteh> another think shall plugins override existing interfaces or barf out? -[2008-11-13 01:32:30] <ichthyo> but which we could use to get detailed diagnostics info in case we are investigating problems in a user setup.... +[2008-11-13 01:29:56] <ichthyo> yes, ok + +[2008-11-13 01:32:18] <cehteh> another thing: shall plugins override existing interfaces or barf out? [2008-11-13 01:32:33] <cehteh> both has pros and cons [2008-11-13 01:32:46] <cehteh> both can easily circumvented with some effort -[2008-11-13 01:33:26] <cehteh> src/plugins/maintenance -[2008-11-13 01:33:27] <ichthyo> ehm, I don't fully understand the situation. Could you explain it? Lets say: we have interface A -[2008-11-13 01:33:41] <ichthyo> now a plugin wants to provide an implementation of interface A -[2008-11-13 01:34:03] <cehteh> in a mail to you i explained that i would like mockup plugins which override core functionality -[2008-11-13 01:34:04] <ichthyo> i.e. the client of this plugin would use interface A to access it -[2008-11-13 01:34:42] <ichthyo> is this correct? -[2008-11-13 01:34:49] <cehteh> yes +[2008-11-13 01:33:27] <ichthyo> ehm, I don't fully understand the situation. + Could you explain it? Lets say: we have interface A + now a plugin wants to provide an implementation of interface A + i.e. the client of this plugin would use interface A to access it + +[2008-11-13 01:34:03] <cehteh> in a mail to you i explained that i would like mockup plugins which + override core functionality + [2008-11-13 01:35:07] <ichthyo> so, wouldn't the mockup just be another implementation of interface A ? -[2008-11-13 01:35:09] <cehteh> well you query interface+implementation tuples -[2008-11-13 01:35:16] <cehteh> yes +[2008-11-13 01:35:09] <cehteh> well you query an interface+implementation tuple [2008-11-13 01:35:35] <cehteh> you cant just open a interface .. you open a implementation of an interface [2008-11-13 01:35:49] <ichthyo> yes, but you use this (interface+implemntation) just for opening the interface, or? [2008-11-13 01:36:05] <cehteh> so mocplugin can provide blurfoo [2008-11-13 01:36:15] <cehteh> even if blurfoo.lum provides it too -[2008-11-13 01:36:26] <cehteh> yes -[2008-11-13 01:36:50] <raffa> (good night!) -[2008-11-13 01:37:03] <ichthyo> raffa: good night -[2008-11-13 01:37:06] <cehteh> i tend to barf if anything wants to provide the same implementation a 2nd time -[2008-11-13 01:37:11] <cehteh> n8 raffa -[2008-11-13 01:37:12] <ichthyo> raffa: wasnt really interesting for you today -[2008-11-13 01:37:16] <ichthyo> much tech talk -[2008-11-13 01:37:33] <raffa> Very promising. :-) -[2008-11-13 01:37:39] <ichthyo> originally, we said we wait for raffa to discuss the logo -[2008-11-13 01:37:51] <cehteh> you missed the logo discussion :P -[2008-11-13 01:37:51] <ichthyo> but we were in the middle of tech stuff -[2008-11-13 01:38:03] <cehteh> hehe -[2008-11-13 01:38:24] <ichthyo> raffa: basically we said: the logo contest seems to be running just fine -[2008-11-13 01:38:34] <raffa> I agree. :-) -[2008-11-13 01:39:03] <raffa> I see if I can stimulate more comments -[2008-11-13 01:39:14] <ichthyo> yes, maybe a good idea -[2008-11-13 01:39:19] <cehteh> ichthyo: well . i think we can stop it here .. i just come up with a solution -[2008-11-13 01:39:30] <cehteh> raffa: yes thats a good plan -[2008-11-13 01:39:51] <ichthyo> cehteh: well .. I rather wanted to understand the problem, because it interests me -[2008-11-13 01:40:26] <raffa> Ok. I go to bed and try to dream something good for comments. ;-) -[2008-11-13 01:40:27] <ichthyo> raffa: well then .. its already late. good night, nice dreams... -[2008-11-13 01:40:30] <raffa> Bye! -[2008-11-13 01:40:32] <cehteh> imagine 2 .lum modules implement and export exactly the same thing (or at least announce it as exactly the same thing) -[2008-11-13 01:40:33] <ichthyo> bye -[2008-11-13 01:40:49] |<-- raffa has left kornbluth.freenode.net (Remote closed the connection) -[2008-11-13 01:40:50] |<-- pedro has left kornbluth.freenode.net (Read error: 104 (Connection reset by peer)) -[2008-11-13 01:40:51] <cehteh> ... but i just solved it -[2008-11-13 01:40:57] <ichthyo> I just try to relate it to the similar situation with classes -[2008-11-13 01:41:00] -->| pedro (n=pedro@32.Red-83-61-132.dynamicIP.rima-tde.net) has joined #lumiera -[2008-11-13 01:41:00] <ichthyo> let me explain -[2008-11-13 01:41:12] <cehteh> nah classes are different + +[2008-11-13 01:40:32] <cehteh> imagine 2 *.lum modules implement and export exactly the same thing + (or at least announce it as exactly the same thing) + +[2008-11-13 01:40:57] <ichthyo> I'd just try to relate it to the similar situation with classes [2008-11-13 01:41:16] <ichthyo> you have an interface and now you have two subclasses +[2008-11-13 01:41:12] <cehteh> nah classes are different [2008-11-13 01:41:27] <ichthyo> no they aren't. really different [2008-11-13 01:41:43] <cehteh> subtle different [2008-11-13 01:41:47] <ichthyo> so both subclasses claim to implement the contract which is defined by the interface [2008-11-13 01:42:04] <ichthyo> now I have a factory which instantiates one of those subclasses [2008-11-13 01:42:14] <ichthyo> or the other or both -[2008-11-13 01:42:18] <ichthyo> same situation -[2008-11-13 01:42:22] <cehteh> yes +[2008-11-13 01:42:18] <ichthyo> same situation as for the plugins + [2008-11-13 01:42:44] <cehteh> err no [2008-11-13 01:42:48] <cehteh> you have only one class -[2008-11-13 01:42:49] <ichthyo> the client of the whole apparatus just wants an "instance" of the contract defined by the interface +[2008-11-13 01:42:49] <ichthyo> the client just wants an "instance" of the contract defined by the interface [2008-11-13 01:43:01] <ichthyo> and doesn't care which one it is -[2008-11-13 01:43:14] <cehteh> you didnt understand my prioblem because it doesnt exist in C -[2008-11-13 01:43:15] <cehteh> C++ +[2008-11-13 01:43:14] <cehteh> you didnt understand my problem because it doesnt exist in C++ [2008-11-13 01:43:33] <cehteh> class foo {...}; [2008-11-13 01:43:43] <cehteh> foo this_is_it; [2008-11-13 01:43:44] <cehteh> foo this_is_it; [2008-11-13 01:43:57] <cehteh> ... redefinition of the same instance is my problem [2008-11-13 01:44:08] <cehteh> not even exactly -[2008-11-13 01:44:37] <ichthyo> ah, in C++ the two would either be rejected by the compiler, or they would be allowed if in different scopes +[2008-11-13 01:44:37] <ichthyo> ah, in C++ the two would either be rejected by the compiler, + or they would be allowed if in different scopes [2008-11-13 01:44:43] <cehteh> even worse the 2nd can say the it is a identical instance but it isnt [2008-11-13 01:45:07] <cehteh> thats a good way to inject moc objects -[2008-11-13 01:45:09] <ichthyo> called "slicing" in C++ + [2008-11-13 01:45:15] <cehteh> so now my solution: -[2008-11-13 01:45:51] <cehteh> eh [2008-11-13 01:46:01] <cehteh> *thinking* -[2008-11-13 01:46:03] <cehteh> yes [2008-11-13 01:46:29] <cehteh> minor versions must differ, biggier minor will win [2008-11-13 01:46:41] <cehteh> (newer one for certain) [2008-11-13 01:46:53] <cehteh> thats intended [2008-11-13 01:47:11] <cehteh> now a mock object might claim it is some *exact* same .. -[2008-11-13 01:47:57] <cehteh> that will be rejected .. so a mock object must do some heavy work to throw out the old one from the nest +[2008-11-13 01:47:57] <cehteh> that will be rejected .. so a mock object must do some heavy work + to throw out the old one from the nest [2008-11-13 01:48:10] <cehteh> thats ok for testing purposes -[2008-11-13 01:48:12] <ichthyo> well... at some point you will be able to tell the loader: load this module for use as interface A + +[2008-11-13 01:48:12] <ichthyo> well... at some point you will be able to tell the loader: + load this module for use as interface A [2008-11-13 01:48:20] <cehteh> nah -[2008-11-13 01:48:24] <cehteh> well yes [2008-11-13 01:48:37] <cehteh> but you forget that modules != interfaces [2008-11-13 01:49:07] <ichthyo> I understand the modules as being like a "subclass" of the interface -[2008-11-13 01:49:18] <cehteh> nope wrong [2008-11-13 01:49:23] <ichthyo> meaning, each of them can "stand-in" for the interface +[2008-11-13 01:49:18] <cehteh> nope wrong [2008-11-13 01:49:41] <cehteh> modules are a collection of interfaces [2008-11-13 01:49:47] <cehteh> completely unrelated [2008-11-13 01:49:54] <ichthyo> huh? @@ -1113,138 +768,18 @@ Conclusion [2008-11-13 01:51:08] <ichthyo> because, at an extension point [2008-11-13 01:51:21] <ichthyo> you just want to tell: this is the interface to be used at this extension point [2008-11-13 01:51:28] <cehteh> but the module is just a bunch of interfaces .. and module itself doesnt give them a purpose -[2008-11-13 01:51:46] <cehteh> yes -[2008-11-13 01:52:09] <ichthyo> thats clear. Something like the feature bundle could give it a spefici purpose, or something similar, which itself is loaded as plugin -[2008-11-13 01:52:23] <cehteh> for example +[2008-11-13 01:52:09] <ichthyo> thats clear. Something like the feature bundle could give it a speficic purpose, + or something similar, which itself is loaded as plugin [2008-11-13 01:52:46] <cehteh> modules are just the bucket .. it is pretty unspecified what you put in [2008-11-13 01:53:01] <ichthyo> yes thats fine, to start with -[2008-11-13 01:53:10] <cehteh> (except that must be interfaces ... ) [2008-11-13 01:53:23] <ichthyo> anything more specific can be built on top of such a system [2008-11-13 01:53:35] <ichthyo> for example take the video effect -[2008-11-13 01:53:56] <ichthyo> at some point, you define an interface "VideoEffect" -[2008-11-13 01:54:27] <ichthyo> probably this interface isn't loaded from a module, rather it's defined from the core, but this doesn't matter for the point in question here -[2008-11-13 01:55:02] <cehteh> huh -[2008-11-13 01:55:04] <ichthyo> now, everything you can ever use as an implementation of this "VideoEffect" must use this interface, -[2008-11-13 01:55:28] <ichthyo> because the client of the interface (which in this case is the core which uses this interface for rendering) -[2008-11-13 01:55:37] <cehteh> yes -[2008-11-13 01:55:45] <ichthyo> doen't care about the implementation and doesn't know any differences -[2008-11-13 01:55:56] <cehteh> probably this interface isn't loaded from a module << interfaced themself are never loaded -[2008-11-13 01:56:08] <ichthyo> I know, they are just opened -[2008-11-13 01:56:14] <cehteh> the just exist as header at compiletime -[2008-11-13 01:56:25] <cehteh> you open implementations thereof -[2008-11-13 01:56:33] <ichthyo> ah, still better, yes -[2008-11-13 01:56:47] <cehteh> hehe i had some struggles to get the names right -[2008-11-13 01:57:04] <ichthyo> and each of these implementations is just like a class instance -[2008-11-13 01:57:24] <cehteh> yes -[2008-11-13 01:57:29] <cehteh> they can map 1:1 -[2008-11-13 01:57:33] <ichthyo> unless you created it yourself, you never can tell its subclass (unless there is some sort of introspection mechanism) -[2008-11-13 01:58:33] <cehteh> well since every function has a uuid for each implementations they have quite strong identity -[2008-11-13 01:58:43] <cehteh> you cant tell if its a subclass .. -[2008-11-13 01:58:49] <cehteh> but you know when it differs -[2008-11-13 01:59:06] <cehteh> thats even stronger than C++ -[2008-11-13 01:59:11] <ichthyo> now all you need is some "backdoor" wich is only used for the mock tests. By use of this backdoor-function, you tell the plugin loader/plugindb: and now replace temporarily the instance XYZ by the mock instance ABC -[2008-11-13 01:59:40] <ichthyo> thats the way I'd propose to handle this problem -[2008-11-13 02:00:05] <ichthyo> so a test can "push aside" what is loaded temporarily and place a mock in its place -[2008-11-13 02:00:18] <cehteh> yes .. so my backdoor is as described -[2008-11-13 02:00:30] <ichthyo> and before finishing, the test should remove the mock, and the original instance will be uncovered -[2008-11-13 02:01:07] <cehteh> heh .. thinking about my solution .. i have this backdoor already very well hidden :) -[2008-11-13 02:01:34] * ichthyo has almost the same in place on class level for some tests -[2008-11-13 02:01:39] <cehteh> -DLUMIERA_MOCKPLUGIN :) -[2008-11-13 02:02:09] <ichthyo> wouldn't it be still better to make it completely dynamic, i.e from the running test? -[2008-11-13 02:02:22] <cehteh> nope -[2008-11-13 02:02:36] <ichthyo> the test just loads a mock impl he knows, and places it temporarily to shaddow the real thing -[2008-11-13 02:03:17] <cehteh> well maybe .. but what happens when the real thing was in use and keeps some state? -[2008-11-13 02:03:40] <cehteh> i think all-or-nothing is the way to go -[2008-11-13 02:03:53] <ichthyo> any user which at this point already has a pointer to the real thing will just continue to use it -[2008-11-13 02:04:14] <cehteh> yes but when its state is global? -[2008-11-13 02:04:34] <cehteh> shared between all instances -[2008-11-13 02:04:49] <cehteh> dont ask me about a concrete example ... but i can imagine this -[2008-11-13 02:04:59] <ichthyo> then the mock is just another one to use this shared data -[2008-11-13 02:05:20] <cehteh> but its hidden in the original plugin -[2008-11-13 02:05:26] <cehteh> inaccessible for the moc -[2008-11-13 02:06:22] <cehteh> at least not trivially .. and you dont want nontrivial mockups (at least not more than the usually are) -[2008-11-13 02:06:55] <cehteh> well the mockup could proxy to a real one -[2008-11-13 02:07:08] <cehteh> thats prolly much better -[2008-11-13 02:07:08] <ichthyo> and you dont have a class hierarchy to reslove the problem. the only solution for this extra problem is for the mock to know the real thing -[2008-11-13 02:07:28] <ichthyo> similar to a subclass which can use protected data from the base -[2008-11-13 02:07:38] <cehteh> anyways .. this is not actual yet -[2008-11-13 02:07:52] <ichthyo> or static protected data to make it analogus to the global data in the example -[2008-11-13 02:07:54] <cehteh> and i go to bed ... :P -[2008-11-13 02:08:02] <ichthyo> I prolly too -[2008-11-13 02:08:06] <ichthyo> as I told you -[2008-11-13 02:08:16] <ichthyo> I have a real heavy CD deadline next week -[2008-11-13 02:08:27] <cehteh> .. taxes .. urgs :P -[2008-11-13 02:08:34] <ichthyo> I am working almost all the time and almost no sleep -[2008-11-13 02:08:36] |<-- nick4 has left kornbluth.freenode.net () -[2008-11-13 02:08:45] <daitheflu_> good night guys, don't forget to sleep a bit :p -[2008-11-13 02:08:52] <ichthyo> well... I'll survive it -[2008-11-13 02:08:55] <cehteh> ok .. n8 -[2008-11-13 02:09:02] |<-- daitheflu_ has left kornbluth.freenode.net ("Ex-Chat") -[2008-11-13 02:09:19] * cehteh works on the taxes next days and then backend -[2008-11-13 02:09:22] <ichthyo> and after that, I'll make my proposal for the main on a dev branch -[2008-11-13 02:09:39] <cehteh> the main() and startup stuff can be fleshed out when we have both more time -[2008-11-13 02:09:43] <cehteh> yes -[2008-11-13 02:09:56] <cehteh> look at mine .. i guess you wont change that much -[2008-11-13 02:09:56] <ichthyo> yeah, I've seen you started on filehandles and similar, right? -[2008-11-13 02:10:23] <cehteh> actually i started there months ago .. that lied uncommited here becasue of the config system -[2008-11-13 02:10:24] <ichthyo> btw: maybe you noticed it -[2008-11-13 02:10:29] <cehteh> plugin loader -[2008-11-13 02:10:35] <cehteh> builddrone -[2008-11-13 02:10:39] <cehteh> etcetc ... -[2008-11-13 02:10:48] <ichthyo> I made a draft implementation for the memory allocation of render nodes -[2008-11-13 02:11:01] <ichthyo> and leave the rest for later or still better for someone else -[2008-11-13 02:11:09] <cehteh> i seen it but didnt looked into the details -[2008-11-13 02:11:20] <ichthyo> I wrote percy tigalo a mail if he is interested to work on it -[2008-11-13 02:11:27] <cehteh> how about persistent rendernodes in the backend? -[2008-11-13 02:11:53] <ichthyo> what doesn this mean? -[2008-11-13 02:11:56] <cehteh> (another time :P) -[2008-11-13 02:12:21] <ichthyo> ok.. well let's have some sleep -[2008-11-13 02:12:36] <cehteh> they store state .. let this state be maped to files by the backend -[2008-11-13 02:12:55] <ichthyo> yes, of course. I think we already discussed this point -[2008-11-13 02:13:04] <cehteh> but makes no much sense this is short lived data which can be recalculated -[2008-11-13 02:13:16] <ichthyo> and said: we use data frames which will be handled similarily by the backend -[2008-11-13 02:13:29] <ichthyo> its not so much of importance for the rendernodes -[2008-11-13 02:13:29] <cehteh> yes -[2008-11-13 02:13:36] <cehteh> yes -[2008-11-13 02:13:38] <ichthyo> but its very important for the automation -[2008-11-13 02:13:54] <cehteh> no i mean your (actually non-state) rendernode allocation :P -[2008-11-13 02:14:13] <cehteh> forget it :) -[2008-11-13 02:14:25] <ichthyo> they have a per-invocation state -[2008-11-13 02:14:32] <cehteh> i see that i deliver some frames next -[2008-11-13 02:14:46] <cehteh> did you seen my first inkscape drawing :P -[2008-11-13 02:14:52] <ichthyo> oh -[2008-11-13 02:14:56] <ichthyo> yes, of course -[2008-11-13 02:15:00] <ichthyo> looks really nice -[2008-11-13 02:15:07] <ichthyo> and the contents are promising... -[2008-11-13 02:15:27] <ichthyo> was it difficult to get aquainted to inkscape? -[2008-11-13 02:15:36] <cehteh> i ditched the writebuffer idea .. in favor of writing directly into the file area -[2008-11-13 02:15:44] <cehteh> not that much -[2008-11-13 02:16:10] <ichthyo> I found the status line of inkscape quite helpful -[2008-11-13 02:16:11] <cehteh> the upper half is working btw .. indexing soon -[2008-11-13 02:16:26] <cehteh> yes my proposal for lumiera too .. -[2008-11-13 02:16:29] <ichthyo> I mean, it tells you what will happen if you hover -[2008-11-13 02:16:35] <ichthyo> yes, I know. I support this -[2008-11-13 02:16:43] <ichthyo> we'll convince Joel, I think -[2008-11-13 02:16:59] <cehteh> xfig and other graphic programs have that too -[2008-11-13 02:17:27] <ichthyo> wir brauchen keine Büroklammern, gell... -[2008-11-13 02:17:31] <ichthyo> :-) -[2008-11-13 02:17:43] <cehteh> about indexing engines .. i guess that needs some turnarounds with the proc -[2008-11-13 02:17:54] <ichthyo> prolly yes -[2008-11-13 02:19:16] <ichthyo> btw: probably I will leave the impl. of the render nodes in the half-way done state they are now and move up to the interface towards GUI, because I get the feeling this will boost up matters at this edge much more if I can deliver somethinc concrete for joel there -[2008-11-13 02:19:26] <cehteh> maybe i need a proc mockup there :P single avdecoder rendernode -[2008-11-13 02:19:40] <ichthyo> prolly yes -[2008-11-13 02:19:45] <cehteh> yes i have plenty to do -[2008-11-13 02:19:47] <ichthyo> good Idea -[2008-11-13 02:19:57] <ichthyo> I'll provide you such a beast -[2008-11-13 02:20:16] <cehteh> even if the file/maping/indexing stuff works .. scheduler, profiler, threads ... -[2008-11-13 02:20:22] <cehteh> shitloads of things -[2008-11-13 02:20:30] <ichthyo> same here -[2008-11-13 02:20:38] <ichthyo> Lumiera is a huge dry sponge -[2008-11-13 02:20:46] <cehteh> hehe -[2008-11-13 02:20:51] <cehteh> ok really off to bed now -[2008-11-13 02:20:57] <ichthyo> ok... -[2008-11-13 02:21:02] <ichthyo> sleep well! -[2008-11-13 02:21:02] <cehteh> n8 -[2008-11-13 02:21:05] <ichthyo> n8 -[2008-11-13 02:22:35] [QUIT] Disconnected from irc://kornbluth.freenode.net/ (irc://kornbluth.freenode.net/). [[Reconnect][Reconnect to kornbluth.freenode.net][reconnect]] + at some point, you define an interface "VideoEffect" + probably this interface isn't loaded from a module, rather it's defined from the core, + but this doesn't matter for the point in question here + now, everything you can ever use as an implementation of this "VideoEffect" + must use this interface, because the client of the interface + (which in this case is the core which uses this interface for rendering) + doesn't care about the implementation and doesn't know any differences +---------------------------- diff --git a/doc/devel/meeting_summary/index.txt b/doc/devel/meeting_summary/index.txt index a65295735..6a1d1cb06 100644 --- a/doc/devel/meeting_summary/index.txt +++ b/doc/devel/meeting_summary/index.txt @@ -12,6 +12,15 @@ Anyone interested in Lumiera development is also encouraged to read mailing list archives and other documentation. +During Summer 2011 we kept up our monthly IRC meetings -- rather casual exchange +for the time being. + +- 9 Nov 2011 +- 12 Oct 2011 +- 14 Sep 2011 +- 10 Aug 2011 +- 13 Jul 2011 +- 8 Jun 2011 11 May 2011 @@ -143,7 +152,7 @@ Topics Summary ^^^^^^^ -_(missing)_ + * link:2008-11-12.html[Transcript (supplemented by Ichthyo in 2011)] From 89a9202c6cd3f93ef68578699dc6bddf4a811ca7 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 1 Dec 2011 23:32:34 +0100 Subject: [PATCH 285/496] cleanup: remove precompiled headers we don't need them and they even tend to increase build times due to unnecessary compound-includes at some core headers --- SConstruct | 7 --- src/lib/frameid.hpp | 1 + src/lib/maybe.hpp | 1 - src/lib/result.hpp | 1 - src/lib/simple-allocator.hpp | 1 - src/lib/test/run.hpp | 5 +- src/lib/typed-allocation-manager.hpp | 1 - src/lib/typed-counter.hpp | 1 - src/pre.hpp | 51 ------------------- src/pre_a.hpp | 48 ----------------- src/proc/asset.hpp | 4 +- src/proc/asset/db.hpp | 4 +- src/proc/asset/media.cpp | 2 +- src/proc/asset/media.hpp | 2 - src/proc/asset/meta.hpp | 2 - src/proc/asset/proc.hpp | 2 - src/proc/asset/struct.hpp | 2 - src/proc/assetmanager.hpp | 2 - src/proc/common.hpp | 1 - src/proc/control/command-closure.hpp | 1 - src/proc/control/command-def.hpp | 1 - src/proc/control/command-invocation.hpp | 1 - src/proc/control/command-mutation.hpp | 1 - src/proc/control/command-registry.hpp | 1 - src/proc/control/command-signature.hpp | 1 - src/proc/control/command.hpp | 2 +- src/proc/control/handling-pattern.hpp | 1 - src/proc/control/handling-patterns.hpp | 1 - src/proc/control/proc-dispatcher.hpp | 1 - src/proc/engine/procnode.hpp | 3 +- src/proc/mobject/mobject-ref.hpp | 1 - src/proc/mobject/mobject.hpp | 3 -- src/proc/mobject/placement-ref.hpp | 1 - src/proc/mobject/placement.hpp | 2 +- .../placement-index-query-resolver.cpp | 1 - .../placement-index-query-resolver.hpp | 1 - src/proc/mobject/session/placement-index.hpp | 1 - src/proc/state.hpp | 4 +- tests/components/proc/asset/testasset.hpp | 4 -- .../proc/control/test-dummy-commands.hpp | 1 - .../session/defs-manager-impl-test.cpp | 2 - .../mobject/session/defs-manager-test.cpp | 2 - 42 files changed, 10 insertions(+), 165 deletions(-) delete mode 100644 src/pre.hpp delete mode 100644 src/pre_a.hpp diff --git a/SConstruct b/SConstruct index 318f3f53c..038958348 100644 --- a/SConstruct +++ b/SConstruct @@ -333,13 +333,6 @@ def defineBuildTargets(env, artifacts): We use a custom function to declare a whole tree of srcfiles. """ - # use PCH to speed up building // disabled for now due to strange failures -# env['GCH'] = ( env.PrecompiledHeader('src/pre.hpp') -# + env.PrecompiledHeader('src/pre_a.hpp') -# ) - - - lLib = env.SharedLibrary('lumiera', srcSubtree(env,'src/lib'), install=True) lApp = env.SharedLibrary('lumieracommon', srcSubtree(env,'src/common'), install=True, LIBS=lLib) lBack = env.SharedLibrary('lumierabackend', srcSubtree(env,'src/backend'),install=True) diff --git a/src/lib/frameid.hpp b/src/lib/frameid.hpp index a8db83077..d8833ddb4 100644 --- a/src/lib/frameid.hpp +++ b/src/lib/frameid.hpp @@ -25,6 +25,7 @@ #define LUMIERA_FRAMEID_H +#include <boost/operators.hpp> namespace lumiera { diff --git a/src/lib/maybe.hpp b/src/lib/maybe.hpp index 085160a92..f8e7204ff 100644 --- a/src/lib/maybe.hpp +++ b/src/lib/maybe.hpp @@ -48,7 +48,6 @@ #ifndef LIB_MAYBE_H #define LIB_MAYBE_H -//#include "pre.hpp" #include "lib/error.hpp" //#include "lib/wrapper.hpp" #include "lib/util.hpp" diff --git a/src/lib/result.hpp b/src/lib/result.hpp index 2f42c7f64..38b3ba123 100644 --- a/src/lib/result.hpp +++ b/src/lib/result.hpp @@ -40,7 +40,6 @@ #ifndef LIB_RESULT_H #define LIB_RESULT_H -//#include "pre.hpp" #include "lib/error.hpp" #include "lib/wrapper.hpp" #include "lib/util.hpp" diff --git a/src/lib/simple-allocator.hpp b/src/lib/simple-allocator.hpp index b79b2081d..f36b19a30 100644 --- a/src/lib/simple-allocator.hpp +++ b/src/lib/simple-allocator.hpp @@ -46,7 +46,6 @@ #ifndef LIB_SIMPLE_ALLOCATOR_H #define LIB_SIMPLE_ALLOCATOR_H -//#include "pre.hpp" #include "lib/error.hpp" #include "lib/meta/generator.hpp" #include "lib/meta/typelist-util.hpp" diff --git a/src/lib/test/run.hpp b/src/lib/test/run.hpp index b47a46e4c..bcb38e222 100644 --- a/src/lib/test/run.hpp +++ b/src/lib/test/run.hpp @@ -36,15 +36,12 @@ #ifndef TESTHELPER_RUN_H #define TESTHELPER_RUN_H -#include "pre.hpp" - - +#include "proc/common.hpp" #include "include/logging.h" #include "lib/test/suite.hpp" #include "lib/util.hpp" -#include <vector> #include <string> diff --git a/src/lib/typed-allocation-manager.hpp b/src/lib/typed-allocation-manager.hpp index d1a04b19e..a4d8810fc 100644 --- a/src/lib/typed-allocation-manager.hpp +++ b/src/lib/typed-allocation-manager.hpp @@ -66,7 +66,6 @@ #ifndef LIB_TYPED_ALLOCATION_MANAGER_H #define LIB_TYPED_ALLOCATION_MANAGER_H -//#include "pre.hpp" #include "lib/error.hpp" #include "lib/format.hpp" #include "lib/typed-counter.hpp" diff --git a/src/lib/typed-counter.hpp b/src/lib/typed-counter.hpp index f208f8cdc..8201e6eb3 100644 --- a/src/lib/typed-counter.hpp +++ b/src/lib/typed-counter.hpp @@ -56,7 +56,6 @@ #ifndef LIB_TYPED_COUNTER_H #define LIB_TYPED_COUNTER_H -//#include "pre.hpp" #include "lib/error.hpp" #include "lib/sync-classlock.hpp" diff --git a/src/pre.hpp b/src/pre.hpp deleted file mode 100644 index 1170769b9..000000000 --- a/src/pre.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - PRE.hpp - precompiled header collection - - - Copyright (C) Lumiera.org - 2008, Christian Thaeter <ct@pipapo.org> - Hermann Vosseler <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file pre.hpp - ** Precompiled Header Collection. - ** Assortment of standard util, error handling and basic lib-boost components, - ** frequently used in conjunction. Precompiling these can speedup building - ** significantly. When used, this header should be included prior to any other - ** headers (and it needs to be compiled by gcc into a "pre.gch" file prior to - ** building the object files including this header). - ** - ** @see mobject.hpp usage example - ** @see pre_a.hpp precompiled header including Asset subsystem - */ - -#ifndef LUMIERA_PRE_HPP -#define LUMIERA_PRE_HPP - -#include <string> -#include <vector> -#include <map> -#include <tr1/memory> -#include <tr1/functional> -#include <boost/format.hpp> - -#include "proc/common.hpp" - - - -#endif /*LUMIERA_PRE_HPP*/ diff --git a/src/pre_a.hpp b/src/pre_a.hpp deleted file mode 100644 index 153fbb01a..000000000 --- a/src/pre_a.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - PRE_A.hpp - precompiled header (including assets) - - - Copyright (C) Lumiera.org - 2008, Christian Thaeter <ct@pipapo.org> - Hermann Vosseler <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file pre.hpp - ** Precompiled Header including Asset subsystem. - ** Assortment of standard util, error handling and basic lib-boost components, - ** together with the basic asset.hpp. - ** - ** @see pre.hpp - */ - -#ifndef LUMIERA_PRE_A_HPP -#define LUMIERA_PRE_A_HPP - - -#include <string> -#include <vector> -#include <map> -#include <tr1/memory> -#include <tr1/functional> -#include <boost/format.hpp> - -#include "proc/common.hpp" -#include "proc/asset.hpp" - - -#endif /*LUMIERA_PRE_A_HPP*/ diff --git a/src/proc/asset.hpp b/src/proc/asset.hpp index bd26b92ae..c7332560d 100644 --- a/src/proc/asset.hpp +++ b/src/proc/asset.hpp @@ -55,9 +55,9 @@ #define PROC_INTERFACE_ASSET_H -#include "proc/asset/category.hpp" -#include "include/logging.h" #include "lib/error.hpp" +#include "include/logging.h" +#include "proc/asset/category.hpp" #include "lib/p.hpp" #include <boost/type_traits/is_base_of.hpp> diff --git a/src/proc/asset/db.hpp b/src/proc/asset/db.hpp index b2d9b073f..07455995f 100644 --- a/src/proc/asset/db.hpp +++ b/src/proc/asset/db.hpp @@ -25,11 +25,9 @@ #define ASSET_DB_H -#include "pre_a.hpp" - #include "lib/sync.hpp" -#include "proc/asset.hpp" #include "lib/error.hpp" +#include "proc/asset.hpp" #include <tr1/memory> #include <tr1/unordered_map> diff --git a/src/proc/asset/media.cpp b/src/proc/asset/media.cpp index 7ba45d628..d76617144 100644 --- a/src/proc/asset/media.cpp +++ b/src/proc/asset/media.cpp @@ -21,8 +21,8 @@ * *****************************************************/ -#include "pre.hpp" #include "lib/error.hpp" +#include "proc/common.hpp" #include "proc/assetmanager.hpp" #include "proc/asset/media.hpp" #include "proc/asset/clip.hpp" diff --git a/src/proc/asset/media.hpp b/src/proc/asset/media.hpp index bdd37da18..9331d456f 100644 --- a/src/proc/asset/media.hpp +++ b/src/proc/asset/media.hpp @@ -35,8 +35,6 @@ #ifndef ASSET_MEDIA_H #define ASSET_MEDIA_H -#include "pre_a.hpp" - #include "proc/asset.hpp" #include "lib/factory.hpp" #include "lib/time/timevalue.hpp" diff --git a/src/proc/asset/meta.hpp b/src/proc/asset/meta.hpp index 9ac73ea4f..e633f188e 100644 --- a/src/proc/asset/meta.hpp +++ b/src/proc/asset/meta.hpp @@ -58,8 +58,6 @@ #ifndef ASSET_META_H #define ASSET_META_H -#include "pre_a.hpp" - #include "proc/asset.hpp" #include "proc/asset/entry-id.hpp" #include "lib/factory.hpp" diff --git a/src/proc/asset/proc.hpp b/src/proc/asset/proc.hpp index cced6e638..dc0e33388 100644 --- a/src/proc/asset/proc.hpp +++ b/src/proc/asset/proc.hpp @@ -35,8 +35,6 @@ #ifndef ASSET_PROC_H #define ASSET_PROC_H -#include "pre_a.hpp" - #include "proc/asset.hpp" #include "lib/factory.hpp" #include "lib/streamtype.hpp" diff --git a/src/proc/asset/struct.hpp b/src/proc/asset/struct.hpp index 4fd1b7f2b..c873304fe 100644 --- a/src/proc/asset/struct.hpp +++ b/src/proc/asset/struct.hpp @@ -57,8 +57,6 @@ #ifndef ASSET_STRUCT_H #define ASSET_STRUCT_H -#include "pre_a.hpp" - #include "proc/asset.hpp" #include "lib/query.hpp" #include "lib/factory.hpp" diff --git a/src/proc/assetmanager.hpp b/src/proc/assetmanager.hpp index d5102eeb4..3b69baa7c 100644 --- a/src/proc/assetmanager.hpp +++ b/src/proc/assetmanager.hpp @@ -38,8 +38,6 @@ #define PROC_INTERFACE_ASSETMANAGER_H -#include "pre_a.hpp" - #include "proc/asset.hpp" #include "lib/error.hpp" #include "lib/singleton.hpp" diff --git a/src/proc/common.hpp b/src/proc/common.hpp index b6f42f445..3b18c731b 100644 --- a/src/proc/common.hpp +++ b/src/proc/common.hpp @@ -30,7 +30,6 @@ ** the primary Proc-Layer namespaces ** ** @see main.cpp - ** @see pre.hpp ** */ diff --git a/src/proc/control/command-closure.hpp b/src/proc/control/command-closure.hpp index 90b29435c..eb59a1c54 100644 --- a/src/proc/control/command-closure.hpp +++ b/src/proc/control/command-closure.hpp @@ -67,7 +67,6 @@ #ifndef CONTROL_COMMAND_CLOSURE_H #define CONTROL_COMMAND_CLOSURE_H -//#include "pre.hpp" #include "lib/bool-checkable.hpp" #include "lib/meta/typelist.hpp" #include "lib/meta/function.hpp" diff --git a/src/proc/control/command-def.hpp b/src/proc/control/command-def.hpp index 07490ce0b..2f87f89d6 100644 --- a/src/proc/control/command-def.hpp +++ b/src/proc/control/command-def.hpp @@ -53,7 +53,6 @@ #ifndef CONTROL_COMMAND_DEF_H #define CONTROL_COMMAND_DEF_H -//#include "pre.hpp" #include "lib/error.hpp" #include "include/logging.h" #include "lib/symbol.hpp" diff --git a/src/proc/control/command-invocation.hpp b/src/proc/control/command-invocation.hpp index 660814098..e357fcd9f 100644 --- a/src/proc/control/command-invocation.hpp +++ b/src/proc/control/command-invocation.hpp @@ -46,7 +46,6 @@ #ifndef CONTROL_COMMAND_INVOCACTION_H #define CONTROL_COMMAND_INVOCACTION_H -//#include "pre.hpp" #include "proc/control/command.hpp" #include "proc/control/handling-pattern.hpp" #include "proc/control/argument-tuple-accept.hpp" diff --git a/src/proc/control/command-mutation.hpp b/src/proc/control/command-mutation.hpp index 9a4f2d13d..fd4df2760 100644 --- a/src/proc/control/command-mutation.hpp +++ b/src/proc/control/command-mutation.hpp @@ -45,7 +45,6 @@ #ifndef CONTROL_COMMAND_MUTATION_H #define CONTROL_COMMAND_MUTATION_H -//#include "pre.hpp" #include "lib/error.hpp" #include "lib/bool-checkable.hpp" #include "proc/control/command-closure.hpp" diff --git a/src/proc/control/command-registry.hpp b/src/proc/control/command-registry.hpp index 1a94ee13f..833840528 100644 --- a/src/proc/control/command-registry.hpp +++ b/src/proc/control/command-registry.hpp @@ -57,7 +57,6 @@ #ifndef CONTROL_COMMAND_REGISTRY_H #define CONTROL_COMMAND_REGISTRY_H -//#include "pre.hpp" #include "lib/error.hpp" #include "lib/singleton.hpp" #include "lib/sync.hpp" diff --git a/src/proc/control/command-signature.hpp b/src/proc/control/command-signature.hpp index 44354c300..f37f82c85 100644 --- a/src/proc/control/command-signature.hpp +++ b/src/proc/control/command-signature.hpp @@ -46,7 +46,6 @@ #ifndef CONTROL_COMMAND_SIGNATURE_H #define CONTROL_COMMAND_SIGNATURE_H -//#include "pre.hpp" #include "lib/meta/function.hpp" #include "lib/meta/typelist.hpp" #include "lib/meta/typelist-manip.hpp" diff --git a/src/proc/control/command.hpp b/src/proc/control/command.hpp index 86632febc..2ab26d0dd 100644 --- a/src/proc/control/command.hpp +++ b/src/proc/control/command.hpp @@ -52,9 +52,9 @@ #ifndef CONTROL_COMMAND_H #define CONTROL_COMMAND_H -#include "pre.hpp" #include "lib/error.hpp" #include "lib/symbol.hpp" +#include "proc/common.hpp" #include "proc/control/argument-erasure.hpp" #include "proc/control/argument-tuple-accept.hpp" #include "proc/control/handling-pattern.hpp" diff --git a/src/proc/control/handling-pattern.hpp b/src/proc/control/handling-pattern.hpp index 6cc947211..84ae17237 100644 --- a/src/proc/control/handling-pattern.hpp +++ b/src/proc/control/handling-pattern.hpp @@ -44,7 +44,6 @@ #ifndef CONTROL_HANDLING_PATTERN_H #define CONTROL_HANDLING_PATTERN_H -//#include "pre.hpp" #include "lib/error.hpp" #include "lib/symbol.hpp" #include "lib/bool-checkable.hpp" diff --git a/src/proc/control/handling-patterns.hpp b/src/proc/control/handling-patterns.hpp index 19d3d7d5f..41a87fbeb 100644 --- a/src/proc/control/handling-patterns.hpp +++ b/src/proc/control/handling-patterns.hpp @@ -42,7 +42,6 @@ #ifndef CONTROL_HANDLING_PATTERNS_DEF_H #define CONTROL_HANDLING_PATTERNS_DEF_H -//#include "pre.hpp" #include "lib/error.hpp" #include "lib/multifact.hpp" #include "proc/control/handling-pattern.hpp" diff --git a/src/proc/control/proc-dispatcher.hpp b/src/proc/control/proc-dispatcher.hpp index a4161aa42..e955b66c5 100644 --- a/src/proc/control/proc-dispatcher.hpp +++ b/src/proc/control/proc-dispatcher.hpp @@ -35,7 +35,6 @@ #ifndef CONTROL_PROC_DISPATCHER_H #define CONTROL_PROC_DISPATCHER_H -//#include "pre.hpp" //#include "lib/symbol.hpp" #include "proc/control/command.hpp" #include "lib/singleton.hpp" diff --git a/src/proc/engine/procnode.hpp b/src/proc/engine/procnode.hpp index 4017140c6..a5eac5bc0 100644 --- a/src/proc/engine/procnode.hpp +++ b/src/proc/engine/procnode.hpp @@ -40,8 +40,7 @@ #ifndef ENGINE_PROCNODE_H #define ENGINE_PROCNODE_H -#include "pre.hpp" - +#include "lib/error.hpp" #include "proc/common.hpp" #include "proc/state.hpp" #include "proc/asset/proc.hpp" diff --git a/src/proc/mobject/mobject-ref.hpp b/src/proc/mobject/mobject-ref.hpp index 36cbdd102..4e8adaeb6 100644 --- a/src/proc/mobject/mobject-ref.hpp +++ b/src/proc/mobject/mobject-ref.hpp @@ -59,7 +59,6 @@ #ifndef MOBJECT_MOBJECT_REF_H #define MOBJECT_MOBJECT_REF_H -//#include "pre.hpp" #include "lib/handle.hpp" #include "proc/mobject/placement.hpp" #include "proc/mobject/placement-ref.hpp" diff --git a/src/proc/mobject/mobject.hpp b/src/proc/mobject/mobject.hpp index c20bd64dd..78a98fde7 100644 --- a/src/proc/mobject/mobject.hpp +++ b/src/proc/mobject/mobject.hpp @@ -24,9 +24,6 @@ #ifndef MOBJECT_MOBJECT_H #define MOBJECT_MOBJECT_H -#include "pre.hpp" - - #include "proc/common.hpp" #include "proc/mobject/builder/buildertool.hpp" #include "proc/mobject/placement.hpp" diff --git a/src/proc/mobject/placement-ref.hpp b/src/proc/mobject/placement-ref.hpp index d5fb4fa59..338f4ae60 100644 --- a/src/proc/mobject/placement-ref.hpp +++ b/src/proc/mobject/placement-ref.hpp @@ -65,7 +65,6 @@ #ifndef MOBJECT_PLACEMENT_REF_H #define MOBJECT_PLACEMENT_REF_H -//#include "pre.hpp" #include "lib/error.hpp" #include "lib/bool-checkable.hpp" #include "proc/mobject/placement.hpp" diff --git a/src/proc/mobject/placement.hpp b/src/proc/mobject/placement.hpp index 94f6b2cca..52d4bf805 100644 --- a/src/proc/mobject/placement.hpp +++ b/src/proc/mobject/placement.hpp @@ -67,7 +67,7 @@ #ifndef MOBJECT_PLACEMENT_H #define MOBJECT_PLACEMENT_H -#include "pre.hpp" +#include "lib/error.hpp" #include "lib/hash-indexed.hpp" #include "lib/time/timevalue.hpp" #include "proc/mobject/session/locatingpin.hpp" diff --git a/src/proc/mobject/session/placement-index-query-resolver.cpp b/src/proc/mobject/session/placement-index-query-resolver.cpp index ea366b6ac..35fa5ecdc 100644 --- a/src/proc/mobject/session/placement-index-query-resolver.cpp +++ b/src/proc/mobject/session/placement-index-query-resolver.cpp @@ -21,7 +21,6 @@ * *****************************************************/ -//#include "pre.hpp" #include "proc/mobject/session/placement-index-query-resolver.hpp" #include "proc/mobject/session/scope-query.hpp" #include "proc/mobject/placement.hpp" diff --git a/src/proc/mobject/session/placement-index-query-resolver.hpp b/src/proc/mobject/session/placement-index-query-resolver.hpp index 6e4ab424d..bac26d96d 100644 --- a/src/proc/mobject/session/placement-index-query-resolver.hpp +++ b/src/proc/mobject/session/placement-index-query-resolver.hpp @@ -56,7 +56,6 @@ #ifndef MOBJECT_SESSION_PLACEMENT_INDEX_QUERY_RESOLVER_H #define MOBJECT_SESSION_PLACEMENT_INDEX_QUERY_RESOLVER_H -//#include "pre.hpp" #include "proc/mobject/session/placement-index.hpp" #include "proc/mobject/session/query-resolver.hpp" #include "proc/mobject/session/scope-query.hpp" diff --git a/src/proc/mobject/session/placement-index.hpp b/src/proc/mobject/session/placement-index.hpp index 51dde737d..9dbc283ae 100644 --- a/src/proc/mobject/session/placement-index.hpp +++ b/src/proc/mobject/session/placement-index.hpp @@ -105,7 +105,6 @@ #ifndef MOBJECT_PLACEMENT_INDEX_H #define MOBJECT_PLACEMENT_INDEX_H -//#include "pre.hpp" #include "lib/util.hpp" #include "lib/error.hpp" #include "lib/itertools.hpp" diff --git a/src/proc/state.hpp b/src/proc/state.hpp index f0e863aaa..4cee7bc9a 100644 --- a/src/proc/state.hpp +++ b/src/proc/state.hpp @@ -25,9 +25,7 @@ #define PROC_INTERFACE_STATE_H -#include "pre.hpp" - -#include "proc/common.hpp" +#include "lib/error.hpp" #include "lib/frameid.hpp" #include "proc/engine/buffhandle.hpp" diff --git a/tests/components/proc/asset/testasset.hpp b/tests/components/proc/asset/testasset.hpp index 7dbcbe636..ff1044e83 100644 --- a/tests/components/proc/asset/testasset.hpp +++ b/tests/components/proc/asset/testasset.hpp @@ -25,11 +25,7 @@ #define ASSET_TESTASSET_H -#include "pre_a.hpp" - #include "proc/asset.hpp" -//#include "lib/util.hpp" - #include <boost/format.hpp> diff --git a/tests/components/proc/control/test-dummy-commands.hpp b/tests/components/proc/control/test-dummy-commands.hpp index 1a8bd5eb3..f6dce546d 100644 --- a/tests/components/proc/control/test-dummy-commands.hpp +++ b/tests/components/proc/control/test-dummy-commands.hpp @@ -38,7 +38,6 @@ #ifndef COMMAND_TEST_DUMMY_COMMANDS_H #define COMMAND_TEST_DUMMY_COMMANDS_H -//#include "pre.hpp" #include "lib/error.hpp" #include "lib/test/test-helper.hpp" diff --git a/tests/components/proc/mobject/session/defs-manager-impl-test.cpp b/tests/components/proc/mobject/session/defs-manager-impl-test.cpp index f3cc8d5e0..48ad7f98c 100644 --- a/tests/components/proc/mobject/session/defs-manager-impl-test.cpp +++ b/tests/components/proc/mobject/session/defs-manager-impl-test.cpp @@ -21,8 +21,6 @@ * *****************************************************/ -#include "pre_a.hpp" - #include "lib/test/run.hpp" #include "lib/util.hpp" diff --git a/tests/components/proc/mobject/session/defs-manager-test.cpp b/tests/components/proc/mobject/session/defs-manager-test.cpp index 3f9ae390e..e2d618a1b 100644 --- a/tests/components/proc/mobject/session/defs-manager-test.cpp +++ b/tests/components/proc/mobject/session/defs-manager-test.cpp @@ -21,8 +21,6 @@ * *****************************************************/ -#include "pre_a.hpp" - #include "lib/test/run.hpp" #include "lib/symbol.hpp" #include "lib/query.hpp" From eb79a00cf4adb3947529b4cdedf52450c7b10e58 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 2 Dec 2011 16:10:03 +0100 Subject: [PATCH 286/496] cleanup: rectify Proc-Layer namespaces (I) --- src/common/configrules.hpp | 16 +-- src/common/query/fake-configrules.hpp | 4 +- src/lib/element-tracker.hpp | 3 +- src/lib/meta/trait.hpp | 6 +- src/lib/p.hpp | 8 +- src/lib/wrapperptr.hpp | 18 +-- src/proc/asset.cpp | 3 +- src/proc/asset.hpp | 19 ++-- src/proc/asset/asset-format.hpp | 3 +- src/proc/asset/buildinstruct.hpp | 9 +- src/proc/asset/category.cpp | 6 +- src/proc/asset/category.hpp | 7 +- src/proc/asset/clip.cpp | 3 +- src/proc/asset/clip.hpp | 23 ++-- src/proc/asset/codec.cpp | 3 +- src/proc/asset/codec.hpp | 3 +- src/proc/asset/compoundmedia.cpp | 3 +- src/proc/asset/compoundmedia.hpp | 3 +- src/proc/asset/dataset.hpp | 12 +- src/proc/asset/db.hpp | 3 +- src/proc/asset/effect.cpp | 3 +- src/proc/asset/effect.hpp | 3 +- src/proc/asset/entry-id.hpp | 10 +- src/proc/asset/inventory.hpp | 3 +- src/proc/asset/media.cpp | 23 ++-- src/proc/asset/media.hpp | 18 +-- src/proc/asset/meta.cpp | 3 +- src/proc/asset/meta.hpp | 3 +- src/proc/asset/meta/time-grid.cpp | 3 +- src/proc/asset/meta/time-grid.hpp | 5 +- src/proc/asset/pipe.cpp | 3 +- src/proc/asset/pipe.hpp | 7 +- src/proc/asset/preview.cpp | 3 +- src/proc/asset/preview.hpp | 3 +- src/proc/asset/proc.cpp | 3 +- src/proc/asset/proc.hpp | 7 +- src/proc/asset/procpatt.cpp | 6 +- src/proc/asset/procpatt.hpp | 4 +- src/proc/asset/sequence.cpp | 3 +- src/proc/asset/sequence.hpp | 5 +- src/proc/asset/struct-factory-impl.hpp | 49 ++++---- src/proc/asset/struct-scheme.hpp | 3 +- src/proc/asset/struct.cpp | 6 +- src/proc/asset/struct.hpp | 3 +- src/proc/asset/timeline.cpp | 3 +- src/proc/asset/timeline.hpp | 6 +- src/proc/asset/typed-id.hpp | 3 +- src/proc/asset/typed-lookup.cpp | 3 +- src/proc/asset/unknown.cpp | 5 +- src/proc/asset/unknown.hpp | 5 +- src/proc/asset/viewer.cpp | 3 +- src/proc/asset/viewer.hpp | 6 +- src/proc/assetmanager.cpp | 7 +- src/proc/assetmanager.hpp | 6 +- src/proc/common.hpp | 105 +++++++++++------- src/proc/control/mediaimpllib.hpp | 2 +- src/proc/control/stypemanager.cpp | 1 - src/proc/control/stypemanager.hpp | 3 +- src/proc/control/styperegistry.hpp | 2 +- src/proc/controllerfacade.cpp | 3 +- src/proc/engine/buffer-local-key.hpp | 3 +- src/proc/engine/buffer-metadata.hpp | 3 +- src/proc/engine/buffer-provider.cpp | 3 +- src/proc/engine/buffer-provider.hpp | 3 +- src/proc/engine/buffhandle-attach.hpp | 3 +- src/proc/engine/buffhandle.hpp | 9 +- src/proc/engine/bufftable-obsolete.hpp | 3 +- src/proc/engine/bufftable.hpp | 3 +- src/proc/engine/channel-descriptor.hpp | 5 +- .../engine/diagnostic-buffer-provider.cpp | 3 +- .../engine/diagnostic-buffer-provider.hpp | 3 +- src/proc/engine/dispatcher.cpp | 3 +- src/proc/engine/dispatcher.hpp | 3 +- src/proc/engine/link.cpp | 3 +- src/proc/engine/link.hpp | 6 +- src/proc/engine/mask.cpp | 6 +- src/proc/engine/mask.hpp | 3 +- src/proc/engine/nodefactory.cpp | 3 +- src/proc/engine/nodefactory.hpp | 3 +- src/proc/engine/nodeinvocation.hpp | 3 +- src/proc/engine/nodeoperation.hpp | 1 + src/proc/engine/nodewiring-config.hpp | 1 + src/proc/engine/nodewiring-def.hpp | 3 +- src/proc/engine/nodewiring.cpp | 3 +- src/proc/engine/nodewiring.hpp | 3 +- src/proc/engine/pluginadapter.cpp | 6 +- src/proc/engine/pluginadapter.hpp | 3 +- src/proc/engine/procnode.hpp | 3 +- src/proc/engine/render-invocation.cpp | 3 +- src/proc/engine/render-invocation.hpp | 3 +- src/proc/engine/renderengine.cpp | 3 +- src/proc/engine/renderengine.hpp | 3 +- src/proc/engine/rendergraph.cpp | 3 +- src/proc/engine/rendergraph.hpp | 3 +- src/proc/engine/source.cpp | 3 +- src/proc/engine/source.hpp | 3 +- src/proc/engine/stateproxy.cpp | 3 +- src/proc/engine/stateproxy.hpp | 3 +- .../engine/tracking-heap-block-provider.cpp | 3 +- .../engine/tracking-heap-block-provider.hpp | 3 +- src/proc/engine/type-handler.hpp | 5 +- src/{common => proc}/external/libgavl.cpp | 10 +- src/{common => proc}/external/libgavl.hpp | 13 +-- src/proc/facade.cpp | 13 ++- src/proc/facade.hpp | 17 ++- .../applicable-builder-target-types.hpp | 4 +- src/proc/mobject/builder/assembler.cpp | 23 ++-- src/proc/mobject/builder/assembler.hpp | 42 ++++--- src/proc/mobject/builder/buildertool.hpp | 17 +-- src/proc/mobject/builder/common.hpp | 4 +- src/proc/mobject/builder/conmanager.cpp | 27 +++-- src/proc/mobject/builder/conmanager.hpp | 53 +++++---- .../builder/fixture-change-detector.hpp | 3 +- .../mobject/builder/model-port-registry.cpp | 5 +- .../mobject/builder/model-port-registry.hpp | 3 +- src/proc/mobject/builder/mould.hpp | 103 +++++++++-------- src/proc/mobject/builder/nodecreatortool.cpp | 76 ++++++------- src/proc/mobject/builder/nodecreatortool.hpp | 3 +- src/proc/mobject/builder/operationpoint.cpp | 6 +- src/proc/mobject/builder/operationpoint.hpp | 4 +- src/proc/mobject/builder/renderstate.hpp | 39 ++++--- .../mobject/builder/segmentation-tool.cpp | 3 +- .../mobject/builder/segmentation-tool.hpp | 3 +- src/proc/mobject/builder/segmentation.cpp | 3 +- src/proc/mobject/builder/segmentation.hpp | 3 +- src/proc/mobject/builder/toolfactory.cpp | 3 +- src/proc/mobject/builder/toolfactory.hpp | 3 +- src/proc/mobject/builder/wiringrequest.hpp | 41 ++++--- src/proc/mobject/builderfacade.cpp | 3 +- src/proc/mobject/builderfacade.hpp | 4 +- src/proc/mobject/explicitplacement.hpp | 3 +- src/proc/mobject/interpolator.cpp | 14 +-- src/proc/mobject/interpolator.hpp | 16 +-- src/proc/mobject/mobject-ref.cpp | 5 +- src/proc/mobject/mobject-ref.hpp | 15 +-- src/proc/mobject/mobject.cpp | 3 +- src/proc/mobject/mobject.hpp | 6 +- src/proc/mobject/model-port.hpp | 6 +- src/proc/mobject/output-designation.cpp | 3 +- src/proc/mobject/output-designation.hpp | 3 +- src/proc/mobject/output-mapping.hpp | 3 +- src/proc/mobject/parameter.cpp | 3 +- src/proc/mobject/parameter.hpp | 3 +- src/proc/mobject/paramprovider.cpp | 6 +- src/proc/mobject/paramprovider.hpp | 6 +- src/proc/mobject/placement-ref.hpp | 18 +-- src/proc/mobject/placement.cpp | 3 +- src/proc/mobject/placement.hpp | 3 +- src/proc/mobject/session.hpp | 7 +- src/proc/mobject/session/abstractmo.cpp | 5 +- src/proc/mobject/session/abstractmo.hpp | 9 +- src/proc/mobject/session/allocation.cpp | 33 +++--- src/proc/mobject/session/allocation.hpp | 61 +++++----- src/proc/mobject/session/auto.cpp | 33 +++--- src/proc/mobject/session/auto.hpp | 53 +++++---- src/proc/mobject/session/binding.cpp | 3 +- src/proc/mobject/session/binding.hpp | 9 +- src/proc/mobject/session/bus-mo.cpp | 3 +- src/proc/mobject/session/bus-mo.hpp | 7 +- src/proc/mobject/session/clip.cpp | 3 +- src/proc/mobject/session/clip.hpp | 9 +- src/proc/mobject/session/compoundclip.cpp | 38 ------- src/proc/mobject/session/compoundclip.hpp | 53 --------- src/proc/mobject/session/constraint.cpp | 21 ++-- src/proc/mobject/session/constraint.hpp | 33 +++--- src/proc/mobject/session/defs-manager.cpp | 7 +- src/proc/mobject/session/defs-manager.hpp | 18 +-- src/proc/mobject/session/defs-registry.hpp | 9 +- src/proc/mobject/session/effect.cpp | 33 +++--- src/proc/mobject/session/effect.hpp | 8 +- src/proc/mobject/session/element-query.hpp | 7 +- src/proc/mobject/session/fixedlocation.cpp | 62 +++++------ src/proc/mobject/session/fixedlocation.hpp | 26 ++--- src/proc/mobject/session/fixture.cpp | 11 +- src/proc/mobject/session/fixture.hpp | 9 +- src/proc/mobject/session/generator-mo.cpp | 3 +- src/proc/mobject/session/generator-mo.hpp | 7 +- src/proc/mobject/session/label.cpp | 3 +- src/proc/mobject/session/label.hpp | 7 +- .../mobject/session/lifecycle-advisor.hpp | 7 +- src/proc/mobject/session/locatingpin.cpp | 3 +- src/proc/mobject/session/locatingpin.hpp | 9 +- src/proc/mobject/session/meta.cpp | 21 ++-- src/proc/mobject/session/meta.hpp | 12 +- src/proc/mobject/session/mobjectfactory.cpp | 3 +- src/proc/mobject/session/mobjectfactory.hpp | 7 +- .../placement-index-query-resolver.cpp | 3 +- .../placement-index-query-resolver.hpp | 3 +- src/proc/mobject/session/placement-index.cpp | 3 +- src/proc/mobject/session/placement-index.hpp | 7 +- src/proc/mobject/session/plug.cpp | 21 ++-- src/proc/mobject/session/plug.hpp | 33 +++--- .../mobject/session/query-focus-stack.hpp | 3 +- src/proc/mobject/session/query-focus.cpp | 3 +- src/proc/mobject/session/query-focus.hpp | 3 +- src/proc/mobject/session/query-resolver.cpp | 3 +- src/proc/mobject/session/query-resolver.hpp | 3 +- src/proc/mobject/session/relativelocation.cpp | 41 ++++--- src/proc/mobject/session/relativelocation.hpp | 3 +- src/proc/mobject/session/root.cpp | 3 +- src/proc/mobject/session/root.hpp | 11 +- src/proc/mobject/session/scope-locator.hpp | 7 +- src/proc/mobject/session/scope-path.cpp | 3 +- src/proc/mobject/session/scope-path.hpp | 9 +- src/proc/mobject/session/scope-query.hpp | 7 +- src/proc/mobject/session/scope.cpp | 3 +- src/proc/mobject/session/scope.hpp | 7 +- src/proc/mobject/session/segment.cpp | 17 ++- src/proc/mobject/session/segment.hpp | 59 +++++----- src/proc/mobject/session/segmentation.cpp | 17 ++- src/proc/mobject/session/segmentation.hpp | 47 ++++---- .../mobject/session/sess-manager-impl.cpp | 3 +- .../mobject/session/sess-manager-impl.hpp | 3 +- src/proc/mobject/session/session-impl.cpp | 3 +- src/proc/mobject/session/session-impl.hpp | 7 +- .../session/session-interface-modules.hpp | 7 +- src/proc/mobject/session/session-query.hpp | 3 +- .../session/session-service-defaults.hpp | 3 +- .../session/session-service-explore-scope.hpp | 5 +- .../mobject/session/session-service-fetch.hpp | 3 +- .../session/session-service-mock-index.hpp | 3 +- .../session/session-service-mutate.hpp | 3 +- src/proc/mobject/session/session-services.cpp | 3 +- src/proc/mobject/session/session-services.hpp | 3 +- src/proc/mobject/session/session.cpp | 3 +- src/proc/mobject/session/simpleclip.cpp | 38 ------- src/proc/mobject/session/simpleclip.hpp | 50 --------- .../session/specific-contents-query.hpp | 3 +- src/proc/mobject/session/track.cpp | 3 +- src/proc/mobject/session/track.hpp | 29 ++--- src/proc/mobject/session/wish.cpp | 21 ++-- src/proc/mobject/session/wish.hpp | 37 +++--- src/proc/mobject/test-dummy-mobject.hpp | 3 +- src/proc/play/dummy-play-connection.cpp | 30 ++--- src/proc/play/output-director.hpp | 2 +- src/proc/play/output-slot-connection.hpp | 4 +- src/proc/play/output-slot.hpp | 4 +- src/proc/play/play-process.hpp | 2 +- src/proc/state.cpp | 3 +- src/proc/state.hpp | 9 +- src/{lib => proc}/streamtype.cpp | 7 +- src/{lib => proc}/streamtype.hpp | 8 +- .../proc/asset/asset-diagnostics.hpp | 2 +- tests/components/proc/asset/basicpipetest.cpp | 3 +- tests/components/proc/asset/testasset.hpp | 2 +- tests/components/proc/asset/testclipasset.hpp | 2 +- tests/components/proc/asset/typed-id-test.cpp | 2 +- .../proc/control/command-basic-test.cpp | 2 +- .../session/defs-manager-impl-test.cpp | 3 +- .../mobject/session/defs-manager-test.cpp | 3 +- .../session/defs-registry-impl-test.cpp | 2 +- .../lib/advice/advice-configuration-test.cpp | 2 +- tests/lib/advice/advice-multiplicity-test.cpp | 2 +- tests/lib/advice/advice-situations-test.cpp | 2 +- tests/lib/custom-shared-ptr-test.cpp | 1 - tests/lib/teststreamtypes.hpp | 2 +- 256 files changed, 1265 insertions(+), 1326 deletions(-) rename src/{common => proc}/external/libgavl.cpp (94%) rename src/{common => proc}/external/libgavl.hpp (92%) delete mode 100644 src/proc/mobject/session/compoundclip.cpp delete mode 100644 src/proc/mobject/session/compoundclip.hpp delete mode 100644 src/proc/mobject/session/simpleclip.cpp delete mode 100644 src/proc/mobject/session/simpleclip.hpp rename src/{lib => proc}/streamtype.cpp (93%) rename src/{lib => proc}/streamtype.hpp (97%) diff --git a/src/common/configrules.hpp b/src/common/configrules.hpp index 841858883..1e91d4813 100644 --- a/src/common/configrules.hpp +++ b/src/common/configrules.hpp @@ -68,7 +68,7 @@ namespace lumiera { ///////TODO: shouldn't that be namespace lib? or proc? using std::string; - using lumiera::P; + using lib::P; @@ -212,13 +212,13 @@ namespace lumiera { ///////TODO: shouldn't that be namespace lib? or proc? * the list of all concrete types participating in the * rule based config query system */ - typedef lumiera::typelist::Types < mobject::session::Track - , asset::Pipe - , const asset::ProcPatt - , asset::Timeline - , asset::Sequence - > ::List - InterfaceTypes; + typedef typelist::Types < proc::mobject::session::Track + , proc::asset::Pipe + , const proc::asset::ProcPatt + , proc::asset::Timeline + , proc::asset::Sequence + > ::List + InterfaceTypes; /** * user-visible Interface to the ConfigRules subsystem. diff --git a/src/common/query/fake-configrules.hpp b/src/common/query/fake-configrules.hpp index bbc9f91ca..2a527e63c 100644 --- a/src/common/query/fake-configrules.hpp +++ b/src/common/query/fake-configrules.hpp @@ -54,10 +54,12 @@ namespace lumiera { namespace query { + namespace asset = proc::asset; + using asset::Pipe; using asset::ProcPatt; using asset::PProcPatt; - using mobject::Session; + using proc::mobject::Session; using util::isnil; diff --git a/src/lib/element-tracker.hpp b/src/lib/element-tracker.hpp index d7d6c1794..117f1855a 100644 --- a/src/lib/element-tracker.hpp +++ b/src/lib/element-tracker.hpp @@ -66,7 +66,6 @@ namespace lib { - using lumiera::P; using util::isSameObject; /** @@ -189,7 +188,7 @@ namespace lib { } - typedef lumiera::P<TAR> PTarget; + typedef P<TAR> PTarget; /** factory for creating smart-ptr managed * TAR instances, automatically registered diff --git a/src/lib/meta/trait.hpp b/src/lib/meta/trait.hpp index ee77db289..0f25822fb 100644 --- a/src/lib/meta/trait.hpp +++ b/src/lib/meta/trait.hpp @@ -45,7 +45,7 @@ namespace tr1 { template<class X> class reference_wrapper; template<class X> class shared_ptr; }} -namespace lumiera{ +namespace lib{ template<class X, class B> class P; } namespace mobject{ @@ -127,12 +127,12 @@ namespace meta { }; template<typename X, class B> - struct Unwrap<lumiera::P<X, B> > + struct Unwrap<P<X, B> > { typedef X Type; static X& - extract (lumiera::P<X,B> ptr) + extract (P<X,B> ptr) { ASSERT (ptr); return *ptr; diff --git a/src/lib/p.hpp b/src/lib/p.hpp index cd7ccd776..51979aa5d 100644 --- a/src/lib/p.hpp +++ b/src/lib/p.hpp @@ -46,15 +46,15 @@ */ -#ifndef LUMIERA_P_H -#define LUMIERA_P_H +#ifndef LIB_P_H +#define LIB_P_H #include "lib/error.hpp" #include <tr1/memory> -namespace lumiera { +namespace lib { using std::tr1::shared_ptr; using std::tr1::weak_ptr; @@ -125,5 +125,5 @@ namespace lumiera { }; -} // namespace lumiera +} // namespace lib #endif diff --git a/src/lib/wrapperptr.hpp b/src/lib/wrapperptr.hpp index 16a20a2f2..5ee48f544 100644 --- a/src/lib/wrapperptr.hpp +++ b/src/lib/wrapperptr.hpp @@ -34,16 +34,20 @@ #include "lib/p.hpp" -namespace asset { class Asset; } -namespace mobject { class MObject; } +namespace proc { + namespace asset { class Asset; } + namespace mobject { class MObject; } + + + typedef ::lumiera::typelist::Types < mobject::Placement<mobject::MObject>* + , lib::P<asset::Asset>* + > ::List + WrapperTypes; +} namespace lumiera { - typedef typelist::Types < mobject::Placement<mobject::MObject>* - , P<asset::Asset>* - > ::List - WrapperTypes; /** * helper to treat various sorts of smart-ptrs uniformly. @@ -55,7 +59,7 @@ namespace lumiera { * error reporting is similar to the behaviour of dynamic_cast<T>: when retrieving * a pointer, NULL is returned in case of mismatch. */ - typedef lib::Variant<WrapperTypes, util::AccessCasted> WrapperPtr; + typedef lib::Variant<proc::WrapperTypes, util::AccessCasted> WrapperPtr; diff --git a/src/proc/asset.cpp b/src/proc/asset.cpp index 0f8560ad1..531566b63 100644 --- a/src/proc/asset.cpp +++ b/src/proc/asset.cpp @@ -43,6 +43,7 @@ using util::isnil; using util::cStr; +namespace proc { namespace asset { using ::NOBUG_FLAG(memory); @@ -198,4 +199,4 @@ namespace asset { } -} // namespace asset +}} // namespace proc::asset diff --git a/src/proc/asset.hpp b/src/proc/asset.hpp index c7332560d..f40721b6d 100644 --- a/src/proc/asset.hpp +++ b/src/proc/asset.hpp @@ -75,16 +75,17 @@ using std::vector; using std::set; - +namespace proc { namespace asset { + namespace error = lumiera::error; + using std::size_t; using std::tr1::shared_ptr; using std::tr1::static_pointer_cast; - using lumiera::P; + using lib::P; - //NOBUG_DECLARE_FLAG (assetmem); typedef size_t HashVal; /////////////////TICKET #722 @@ -358,17 +359,17 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset namespace proc_interface { - using asset::Asset; - using asset::Category; - using asset::ID; - using asset::IDA; - using asset::PAsset; + using proc::asset::Asset; + using proc::asset::Category; + using proc::asset::ID; + using proc::asset::IDA; + using proc::asset::PAsset; } #endif diff --git a/src/proc/asset/asset-format.hpp b/src/proc/asset/asset-format.hpp index 8ee6c71ce..ec3ffd5b3 100644 --- a/src/proc/asset/asset-format.hpp +++ b/src/proc/asset/asset-format.hpp @@ -31,6 +31,7 @@ +namespace proc { namespace asset { @@ -49,5 +50,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/buildinstruct.hpp b/src/proc/asset/buildinstruct.hpp index 715e40954..1408037c2 100644 --- a/src/proc/asset/buildinstruct.hpp +++ b/src/proc/asset/buildinstruct.hpp @@ -28,8 +28,8 @@ */ -#ifndef ASSET_BUILDINSTRUCT_H -#define ASSET_BUILDINSTRUCT_H +#ifndef PROC_ASSET_BUILDINSTRUCT_H +#define PROC_ASSET_BUILDINSTRUCT_H #include "lib/symbol.hpp" @@ -41,9 +41,10 @@ using std::string; +namespace proc { namespace asset { - using lumiera::P; + using lib::P; using lib::Symbol; using lib::Literal; @@ -127,5 +128,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/category.cpp b/src/proc/asset/category.cpp index 8e98a68dd..5016384d3 100644 --- a/src/proc/asset/category.cpp +++ b/src/proc/asset/category.cpp @@ -30,8 +30,8 @@ using boost::algorithm::starts_with; using util::isnil; -namespace asset - { +namespace proc { +namespace asset { /** human readable representation of the asset::Category. * @todo to be localized. @@ -69,4 +69,4 @@ namespace asset } -} // namespace asset +}} // namespace asset diff --git a/src/proc/asset/category.hpp b/src/proc/asset/category.hpp index b7c8df2f2..b0da8e796 100644 --- a/src/proc/asset/category.hpp +++ b/src/proc/asset/category.hpp @@ -21,8 +21,8 @@ */ -#ifndef ASSET_CATEGORY_H -#define ASSET_CATEGORY_H +#ifndef PROC_ASSET_CATEGORY_H +#define PROC_ASSET_CATEGORY_H #include "lib/symbol.hpp" @@ -31,6 +31,7 @@ +namespace proc { namespace asset { using lib::Literal; @@ -111,5 +112,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/clip.cpp b/src/proc/asset/clip.cpp index aa651de74..52d735259 100644 --- a/src/proc/asset/clip.cpp +++ b/src/proc/asset/clip.cpp @@ -27,6 +27,7 @@ using std::tr1::static_pointer_cast; +namespace proc { namespace asset { namespace { @@ -105,4 +106,4 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset diff --git a/src/proc/asset/clip.hpp b/src/proc/asset/clip.hpp index d3b1a3aaa..a73d4f4be 100644 --- a/src/proc/asset/clip.hpp +++ b/src/proc/asset/clip.hpp @@ -30,8 +30,9 @@ /////////////////////////TODO: 1/2010 very likely the handling of the clip-asset needs to be rewritten -namespace asset - { +namespace proc { +namespace asset { + /** * bookkeeping (Asset) view of a media clip. */ @@ -52,14 +53,14 @@ namespace asset virtual PClip getClipAsset (); virtual PMedia checkCompound (); - + }; - - typedef P<const asset::Clip> PClipAsset; - - const string CLIP_SUBFOLDER = "clips"; // TODO: handling of hard-wired constants.... - - - -} // namespace asset + + typedef P<const asset::Clip> PClipAsset; + + const string CLIP_SUBFOLDER = "clips"; // TODO: handling of hard-wired constants.... + + + +}} // namespace proc:asset #endif diff --git a/src/proc/asset/codec.cpp b/src/proc/asset/codec.cpp index 8810ea0da..08fd63713 100644 --- a/src/proc/asset/codec.cpp +++ b/src/proc/asset/codec.cpp @@ -23,8 +23,9 @@ #include "proc/asset/codec.hpp" +namespace proc { namespace asset { -} // namespace asset +}} // namespace proc::asset diff --git a/src/proc/asset/codec.hpp b/src/proc/asset/codec.hpp index b7d7e5e34..093470859 100644 --- a/src/proc/asset/codec.hpp +++ b/src/proc/asset/codec.hpp @@ -28,6 +28,7 @@ +namespace proc { namespace asset { /** @@ -40,5 +41,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/compoundmedia.cpp b/src/proc/asset/compoundmedia.cpp index e77416247..115ce7708 100644 --- a/src/proc/asset/compoundmedia.cpp +++ b/src/proc/asset/compoundmedia.cpp @@ -23,6 +23,7 @@ #include "proc/asset/compoundmedia.hpp" +namespace proc { namespace asset { @@ -30,4 +31,4 @@ namespace asset -} // namespace asset + }} // namespace proc::asset diff --git a/src/proc/asset/compoundmedia.hpp b/src/proc/asset/compoundmedia.hpp index 22fdb6922..c73c2c7d9 100644 --- a/src/proc/asset/compoundmedia.hpp +++ b/src/proc/asset/compoundmedia.hpp @@ -32,6 +32,7 @@ using std::vector; +namespace proc { namespace asset { @@ -49,5 +50,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/dataset.hpp b/src/proc/asset/dataset.hpp index e488302c2..15d6f633c 100644 --- a/src/proc/asset/dataset.hpp +++ b/src/proc/asset/dataset.hpp @@ -28,15 +28,15 @@ -namespace asset - { - - +namespace proc { +namespace asset { + + /** * meta asset describing a collection of control data */ class Dataset : public Meta {}; - -} // namespace asset + +}} // namespace proc::asset #endif diff --git a/src/proc/asset/db.hpp b/src/proc/asset/db.hpp index 07455995f..bfe99a029 100644 --- a/src/proc/asset/db.hpp +++ b/src/proc/asset/db.hpp @@ -35,6 +35,7 @@ #include <boost/utility.hpp> +namespace proc { namespace asset { using std::tr1::static_pointer_cast; @@ -167,5 +168,5 @@ namespace asset { }; -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/effect.cpp b/src/proc/asset/effect.cpp index 999b5c118..4230177c3 100644 --- a/src/proc/asset/effect.cpp +++ b/src/proc/asset/effect.cpp @@ -23,6 +23,7 @@ #include "proc/asset/effect.hpp" +namespace proc { namespace asset { Proc::ProcFunc* @@ -33,4 +34,4 @@ namespace asset { -} // namespace asset +}} // namespace asset diff --git a/src/proc/asset/effect.hpp b/src/proc/asset/effect.hpp index e2e8c3fd4..fa9eed8fa 100644 --- a/src/proc/asset/effect.hpp +++ b/src/proc/asset/effect.hpp @@ -28,6 +28,7 @@ +namespace proc { namespace asset { /** @@ -50,5 +51,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/entry-id.hpp b/src/proc/asset/entry-id.hpp index c8b6849ac..7667c8ec6 100644 --- a/src/proc/asset/entry-id.hpp +++ b/src/proc/asset/entry-id.hpp @@ -54,12 +54,12 @@ #include <string> +namespace proc { namespace asset { using std::string; using std::ostream; - using lumiera::error::LUMIERA_ERROR_WRONG_TYPE; namespace idi { @@ -233,9 +233,9 @@ namespace asset { recast (BareEntryID const& bID) { if (!canRecast(bID)) - throw lumiera::error::Logic ("unable to recast EntryID: desired type " - "doesn't match original definition" - , LUMIERA_ERROR_WRONG_TYPE); + throw error::Logic ("unable to recast EntryID: desired type " + "doesn't match original definition" + , error::LUMIERA_ERROR_WRONG_TYPE); return EntryID (bID.getSym()); } @@ -278,5 +278,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/inventory.hpp b/src/proc/asset/inventory.hpp index 2853e2697..6858aeeb0 100644 --- a/src/proc/asset/inventory.hpp +++ b/src/proc/asset/inventory.hpp @@ -28,6 +28,7 @@ +namespace proc { namespace asset { @@ -43,5 +44,5 @@ namespace asset { typedef lumiera::P<Inventory> PInv; -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/media.cpp b/src/proc/asset/media.cpp index d76617144..a80628159 100644 --- a/src/proc/asset/media.cpp +++ b/src/proc/asset/media.cpp @@ -52,6 +52,7 @@ using std::tr1::dynamic_pointer_cast; namespace error = lumiera::error; +namespace proc { namespace asset { namespace { // Implementation details @@ -229,18 +230,18 @@ namespace asset { * @throw Invalid if the given media asset is not top-level, * but rather part or a multichannel (compound) media */ - P<asset::Clip> - MediaFactory::operator() (asset::Media& mediaref) throw(lumiera::error::Invalid) + P<Clip> + MediaFactory::operator() (Media& mediaref) { if (mediaref.checkCompound()) - throw lumiera::error::Invalid (str(format("Attempt to create a asset::Clip from the media %s, " - "which is not toplevel but rather part or a compound " - "(multichannel) media. Found parent Media %s.") - % string(mediaref) - % string(*mediaref.checkCompound())) - ,LUMIERA_ERROR_PART_OF_COMPOUND - ); - asset::Clip* pC = new asset::Clip (mediaref); + throw error::Invalid (str(format("Attempt to create a asset::Clip from the media %s, " + "which is not toplevel but rather part or a compound " + "(multichannel) media. Found parent Media %s.") + % string(mediaref) + % string(*mediaref.checkCompound())) + ,LUMIERA_ERROR_PART_OF_COMPOUND + ); + Clip* pC = new Clip (mediaref); return AssetManager::instance().wrap (*pC); } @@ -249,4 +250,4 @@ namespace asset { -} // namespace asset +}} // namespace asset diff --git a/src/proc/asset/media.hpp b/src/proc/asset/media.hpp index 9331d456f..3d3c0546c 100644 --- a/src/proc/asset/media.hpp +++ b/src/proc/asset/media.hpp @@ -43,6 +43,7 @@ +namespace proc { namespace asset { class Clip; @@ -50,7 +51,6 @@ namespace asset { class MediaFactory; class ProcPatt; - using lumiera::P; using lib::time::Duration; @@ -74,9 +74,9 @@ namespace asset { public: typedef P<Media> PMedia; - typedef P<asset::Clip> PClip; - typedef P<asset::ProcPatt> PProcPatt; - typedef mobject::session::PClipMO PClipMO; + typedef P<proc::asset::Clip> PClip; + typedef P<proc::asset::ProcPatt> PProcPatt; + typedef proc::mobject::session::PClipMO PClipMO; static MediaFactory create; @@ -121,7 +121,7 @@ namespace asset { * @return pointer to parent, or \code null */ virtual PMedia checkCompound () const; - friend class asset::Clip; ////////////////////////TODO better interface!!! + friend class proc::asset::Clip; ////////////////////////TODO better interface!!! }; @@ -141,7 +141,7 @@ namespace asset { class MediaFactory : public lib::Factory<asset::Media> { public: - typedef P<asset::Media> PType; + typedef P<Media> PType; PType operator() (Asset::Ident& key, const string& file=""); PType operator() (const string& file, const Category& cat); @@ -151,8 +151,8 @@ namespace asset { PType operator() (const char* file, const Category& cat); PType operator() (const char* file, asset::Kind); - P<asset::Clip> - operator() (asset::Media& mediaref) throw(lumiera::error::Invalid); + P<Clip> + operator() (Media& mediaref); }; @@ -161,5 +161,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/meta.cpp b/src/proc/asset/meta.cpp index 3e5e15e99..75ff42c6f 100644 --- a/src/proc/asset/meta.cpp +++ b/src/proc/asset/meta.cpp @@ -26,6 +26,7 @@ #include "lib/util.hpp" +namespace proc { namespace asset { using meta::Descriptor; @@ -104,4 +105,4 @@ namespace asset { template Builder<TimeGrid> MetaFactory::operator() (Descriptor const&, EntryID<TimeGrid>); -} // namespace asset +}} // namespace proc::asset diff --git a/src/proc/asset/meta.hpp b/src/proc/asset/meta.hpp index e633f188e..8b6ffb7d1 100644 --- a/src/proc/asset/meta.hpp +++ b/src/proc/asset/meta.hpp @@ -64,6 +64,7 @@ +namespace proc { namespace asset { class Meta; @@ -160,5 +161,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp index 767daa41c..d37b77866 100644 --- a/src/proc/asset/meta/time-grid.cpp +++ b/src/proc/asset/meta/time-grid.cpp @@ -41,6 +41,7 @@ using boost::str; using std::string; +namespace proc { namespace asset { namespace meta { @@ -170,4 +171,4 @@ namespace meta { } -}} // namespace asset::meta +}}} // namespace asset::meta diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp index 5b1f0486b..5dd754865 100644 --- a/src/proc/asset/meta/time-grid.hpp +++ b/src/proc/asset/meta/time-grid.hpp @@ -54,6 +54,7 @@ +namespace proc { namespace asset { namespace meta { @@ -66,7 +67,7 @@ namespace meta { class TimeGrid; - typedef lumiera::P<TimeGrid> PGrid; + typedef P<TimeGrid> PGrid; /** @@ -131,5 +132,5 @@ namespace meta { }; -}} // namespace asset::meta +}}} // namespace proc::asset::meta #endif diff --git a/src/proc/asset/pipe.cpp b/src/proc/asset/pipe.cpp index 4a766cb36..5104b0415 100644 --- a/src/proc/asset/pipe.cpp +++ b/src/proc/asset/pipe.cpp @@ -27,6 +27,7 @@ using util::isnil; +namespace proc { namespace asset { @@ -83,4 +84,4 @@ namespace asset { -} // namespace asset +}} // namespace asset diff --git a/src/proc/asset/pipe.hpp b/src/proc/asset/pipe.hpp index c3e070206..1e91e1965 100644 --- a/src/proc/asset/pipe.hpp +++ b/src/proc/asset/pipe.hpp @@ -26,16 +26,15 @@ #include "proc/asset/struct.hpp" #include "proc/asset/procpatt.hpp" -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" #include <string> +namespace proc { namespace asset { - using lumiera::P; - using lumiera::StreamType; using std::string; class Pipe; @@ -125,5 +124,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/preview.cpp b/src/proc/asset/preview.cpp index d14d54a77..c27879f02 100644 --- a/src/proc/asset/preview.cpp +++ b/src/proc/asset/preview.cpp @@ -23,6 +23,7 @@ #include "proc/asset/preview.hpp" +namespace proc { namespace asset { @@ -64,4 +65,4 @@ namespace asset { -} // namespace asset +}} // namespace asset diff --git a/src/proc/asset/preview.hpp b/src/proc/asset/preview.hpp index 27bacd1b2..f576e1de2 100644 --- a/src/proc/asset/preview.hpp +++ b/src/proc/asset/preview.hpp @@ -28,6 +28,7 @@ +namespace proc { namespace asset { /** @@ -43,5 +44,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/proc.cpp b/src/proc/asset/proc.cpp index 8b6ecb178..54bbde451 100644 --- a/src/proc/asset/proc.cpp +++ b/src/proc/asset/proc.cpp @@ -27,6 +27,7 @@ #include "include/logging.h" +namespace proc { namespace asset { @@ -47,4 +48,4 @@ namespace asset { -} // namespace asset +}} // namespace asset diff --git a/src/proc/asset/proc.hpp b/src/proc/asset/proc.hpp index dc0e33388..0f424aece 100644 --- a/src/proc/asset/proc.hpp +++ b/src/proc/asset/proc.hpp @@ -37,10 +37,11 @@ #include "proc/asset.hpp" #include "lib/factory.hpp" -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" +namespace proc { namespace asset { @@ -75,7 +76,7 @@ namespace asset { return static_cast<const ID<Proc>& > (Asset::getID()); } - typedef lumiera::StreamType::ImplFacade::DataBuffer Buff; + typedef StreamType::ImplFacade::DataBuffer Buff; typedef Buff* PBuff; typedef void (ProcFunc) (PBuff); @@ -124,5 +125,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/procpatt.cpp b/src/proc/asset/procpatt.cpp index 8399f6ccb..37de98de3 100644 --- a/src/proc/asset/procpatt.cpp +++ b/src/proc/asset/procpatt.cpp @@ -29,8 +29,8 @@ using util::isnil; -namespace asset - { +namespace proc { +namespace asset { /** new processing pattern with empty instruction list. @@ -117,4 +117,4 @@ namespace asset -} // namespace asset +}} // namespace asset diff --git a/src/proc/asset/procpatt.hpp b/src/proc/asset/procpatt.hpp index ff714617d..4d981e1d4 100644 --- a/src/proc/asset/procpatt.hpp +++ b/src/proc/asset/procpatt.hpp @@ -33,9 +33,9 @@ using std::vector; +namespace proc { namespace asset { - using lumiera::P; using lib::Symbol; class Proc; @@ -73,5 +73,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/sequence.cpp b/src/proc/asset/sequence.cpp index a84e2fd3b..8991fb864 100644 --- a/src/proc/asset/sequence.cpp +++ b/src/proc/asset/sequence.cpp @@ -27,6 +27,7 @@ //#include "proc/mobject/placement.hpp" //#include "proc/mobject/session/mobjectfactory.hpp" +namespace proc { namespace asset { using lib::AutoRegistered; @@ -62,4 +63,4 @@ namespace asset { -} // namespace asset +}} // namespace asset diff --git a/src/proc/asset/sequence.hpp b/src/proc/asset/sequence.hpp index 91d851d59..d681270e5 100644 --- a/src/proc/asset/sequence.hpp +++ b/src/proc/asset/sequence.hpp @@ -62,6 +62,7 @@ //using std::vector; //using std::string; +namespace proc { namespace mobject { namespace session { @@ -74,7 +75,7 @@ namespace asset { class Sequence; - typedef lumiera::P<Sequence> PSequence; + typedef P<Sequence> PSequence; @@ -104,5 +105,5 @@ namespace asset { ///////////////////////////TODO currently just fleshing the API -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/struct-factory-impl.hpp b/src/proc/asset/struct-factory-impl.hpp index 78986ea74..392cbfed3 100644 --- a/src/proc/asset/struct-factory-impl.hpp +++ b/src/proc/asset/struct-factory-impl.hpp @@ -58,26 +58,29 @@ #include <boost/format.hpp> #include <cstdlib> -using boost::format; -using mobject::Session; -using mobject::MObject; -using mobject::session::Scope; -using mobject::session::match_specificTrack; -using mobject::session::RBinding; -using mobject::session::RTrack; - -using lib::Symbol; -using util::isnil; -using util::contains; -using asset::Query; -using lumiera::query::LUMIERA_ERROR_CAPABILITY_QUERY; -using lumiera::query::extractID; +namespace proc { namespace asset { + using boost::format; + + using lib::Symbol; + using util::isnil; + using util::contains; + using lumiera::query::LUMIERA_ERROR_CAPABILITY_QUERY; + using lumiera::query::extractID; + + using proc::mobject::Session; + using proc::mobject::MObject; + using proc::mobject::session::Scope; + using proc::mobject::session::match_specificTrack; + using proc::mobject::session::RBinding; + using proc::mobject::session::RTrack; + using idi::StructTraits; + namespace { Symbol genericIdSymbol ("id"); @@ -107,7 +110,7 @@ namespace asset { */ template<class STRU> const Asset::Ident - createIdent (const Query<STRU>& query) + createIdent (Query<STRU> const& query) { // does the query somehow specify the desired name-ID? string nameID = extractID (genericIdSymbol, query); @@ -173,10 +176,10 @@ namespace asset { * @todo a real implementation using a resolution engine. */ template<class STRU> - STRU* fabricate (const Query<STRU>& caps) + STRU* fabricate (Query<STRU> const& caps) { - throw lumiera::error::Config ( str(format("The following Query could not be resolved: %s.") % caps.asKey()) - , LUMIERA_ERROR_CAPABILITY_QUERY ); + throw error::Config ( str(format("The following Query could not be resolved: %s.") % caps.asKey()) + , LUMIERA_ERROR_CAPABILITY_QUERY ); } }; @@ -186,7 +189,7 @@ namespace asset { template<> inline const ProcPatt* - StructFactoryImpl::fabricate (const Query<const ProcPatt>& caps) + StructFactoryImpl::fabricate (Query<const ProcPatt> const& caps) { TODO ("actually extract properties/capabilities from the query..."); return new ProcPatt (createIdent (caps)); @@ -194,7 +197,7 @@ namespace asset { template<> inline Pipe* - StructFactoryImpl::fabricate (const Query<Pipe>& caps) + StructFactoryImpl::fabricate (Query<Pipe> const& caps) { const Asset::Ident idi (createIdent (caps)); string streamID = extractID ("stream", caps); @@ -208,7 +211,7 @@ namespace asset { template<> inline Timeline* - StructFactoryImpl::fabricate (const Query<Timeline>& caps) + StructFactoryImpl::fabricate (Query<Timeline> const& caps) { TODO ("extract additional properties/capabilities from the query..."); const Asset::Ident idi (createIdent (caps)); @@ -225,7 +228,7 @@ namespace asset { template<> inline Sequence* - StructFactoryImpl::fabricate (const Query<Sequence>& caps) + StructFactoryImpl::fabricate (Query<Sequence> const& caps) { // when we reach this point it is clear a suitable sequence doesn't yet exist in the model TODO ("actually extract properties/capabilities from the query..."); @@ -244,5 +247,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/struct-scheme.hpp b/src/proc/asset/struct-scheme.hpp index e0f7af463..d0354833a 100644 --- a/src/proc/asset/struct-scheme.hpp +++ b/src/proc/asset/struct-scheme.hpp @@ -45,6 +45,7 @@ using boost::format; +namespace proc { namespace mobject { namespace session { @@ -166,5 +167,5 @@ namespace asset{ } -}} // namespace asset::idi +}}} // namespace asset::idi #endif diff --git a/src/proc/asset/struct.cpp b/src/proc/asset/struct.cpp index 83083c53c..032920ec8 100644 --- a/src/proc/asset/struct.cpp +++ b/src/proc/asset/struct.cpp @@ -43,6 +43,7 @@ using lumiera::ConfigRules; using util::contains; +namespace proc { namespace asset { /****** NOTE: not fully implemented yet. What follows is partially a hack to build simple tests *******/ @@ -154,7 +155,7 @@ namespace asset { -} // namespace asset +}} // namespace asset @@ -169,6 +170,7 @@ namespace asset { #include "proc/asset/sequence.hpp" +namespace proc { namespace asset { template P<Pipe> StructFactory::operator() (Query<Pipe> const&); @@ -186,4 +188,4 @@ namespace asset { template PTimeline StructFactory::made4fake (Query<Timeline> const&); template PSequence StructFactory::made4fake (Query<Sequence>const&); -} // namespace asset +}} // namespace asset diff --git a/src/proc/asset/struct.hpp b/src/proc/asset/struct.hpp index c873304fe..773ddf2c8 100644 --- a/src/proc/asset/struct.hpp +++ b/src/proc/asset/struct.hpp @@ -68,6 +68,7 @@ +namespace proc { namespace asset { using std::string; @@ -161,5 +162,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/timeline.cpp b/src/proc/asset/timeline.cpp index bb2f9e36e..2b4bfdc3f 100644 --- a/src/proc/asset/timeline.cpp +++ b/src/proc/asset/timeline.cpp @@ -29,6 +29,7 @@ #include "proc/assetmanager.hpp" +namespace proc { namespace asset { using lib::AutoRegistered; @@ -69,4 +70,4 @@ namespace asset { -} // namespace asset +}} // namespace asset diff --git a/src/proc/asset/timeline.hpp b/src/proc/asset/timeline.hpp index 7bf168e1e..42ec739c0 100644 --- a/src/proc/asset/timeline.hpp +++ b/src/proc/asset/timeline.hpp @@ -67,6 +67,7 @@ //using std::vector; //using std::string; +namespace proc { namespace mobject { namespace session { @@ -78,9 +79,8 @@ namespace session { namespace asset { -// using lumiera::P; class Timeline; - typedef lumiera::P<Timeline> PTimeline; + typedef P<Timeline> PTimeline; /** @@ -111,5 +111,5 @@ namespace asset { ///////////////////////////TODO currently just fleshing the API -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/typed-id.hpp b/src/proc/asset/typed-id.hpp index a7edcc732..99f625ff3 100644 --- a/src/proc/asset/typed-id.hpp +++ b/src/proc/asset/typed-id.hpp @@ -75,6 +75,7 @@ namespace query { }} +namespace proc { namespace asset { // using std::string; @@ -132,5 +133,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/typed-lookup.cpp b/src/proc/asset/typed-lookup.cpp index a7d9c3368..942090b04 100644 --- a/src/proc/asset/typed-lookup.cpp +++ b/src/proc/asset/typed-lookup.cpp @@ -24,6 +24,7 @@ #include "proc/asset/typed-id.hpp" +namespace proc { namespace asset { LUMIERA_ERROR_DEFINE (MISSING_INSTANCE, "Existing ID registration without associated instance"); @@ -33,4 +34,4 @@ namespace asset { -} // namespace asset +}} // namespace asset diff --git a/src/proc/asset/unknown.cpp b/src/proc/asset/unknown.cpp index f8d3b81f1..2e4e1df68 100644 --- a/src/proc/asset/unknown.cpp +++ b/src/proc/asset/unknown.cpp @@ -27,6 +27,7 @@ using boost::format; +namespace proc { namespace asset { LUMIERA_ERROR_DEFINE (ORIG_NOT_FOUND, "Media referred by placeholder not found"); @@ -49,7 +50,7 @@ namespace asset { * try to access the "real" media it stands for. */ Media::PMedia - Unknown::getOrg() throw(lumiera::error::Invalid) + Unknown::getOrg() { UNIMPLEMENTED ("how to get at the original media from a »Unknown« placeholder"); if (1==0) @@ -63,4 +64,4 @@ namespace asset { -} // namespace asset +}} // namespace asset diff --git a/src/proc/asset/unknown.hpp b/src/proc/asset/unknown.hpp index 31f3df6b0..54f234c71 100644 --- a/src/proc/asset/unknown.hpp +++ b/src/proc/asset/unknown.hpp @@ -29,6 +29,7 @@ +namespace proc { namespace asset { using lib::time::FSecs; @@ -49,7 +50,7 @@ namespace asset { friend class MediaFactory; public: - virtual Media::PMedia getOrg() throw(lumiera::error::Invalid); + virtual Media::PMedia getOrg(); }; @@ -58,5 +59,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/viewer.cpp b/src/proc/asset/viewer.cpp index bd1db8cbf..12e660e67 100644 --- a/src/proc/asset/viewer.cpp +++ b/src/proc/asset/viewer.cpp @@ -29,6 +29,7 @@ //#include "proc/assetmanager.hpp" +namespace proc { namespace asset { // using lib::AutoRegistered; @@ -67,4 +68,4 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset diff --git a/src/proc/asset/viewer.hpp b/src/proc/asset/viewer.hpp index a70c322f2..618e29aa5 100644 --- a/src/proc/asset/viewer.hpp +++ b/src/proc/asset/viewer.hpp @@ -62,6 +62,7 @@ //using std::vector; //using std::string; +namespace proc { namespace mobject { namespace session { @@ -73,9 +74,8 @@ namespace session { namespace asset { - using lumiera::P; class Viewer; - typedef lumiera::P<Viewer> PViewer; + typedef P<Viewer> PViewer; /** @@ -103,5 +103,5 @@ namespace asset { ///////////////////////////TODO currently just fleshing the API -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index 0de48828e..1b4fdb4e3 100644 --- a/src/proc/assetmanager.cpp +++ b/src/proc/assetmanager.cpp @@ -40,6 +40,7 @@ using lib::Singleton; using lib::Sync; +namespace proc { namespace asset { /** @@ -220,7 +221,7 @@ namespace asset { } -} // namespace asset +}} // namespace proc::asset @@ -240,7 +241,7 @@ namespace asset { #include "proc/asset/sequence.hpp" #include "proc/asset/meta/time-grid.hpp" - +namespace proc { namespace asset { template ID<Asset> AssetManager::reg (Asset* obj, const Asset::Ident& idi); @@ -265,4 +266,4 @@ namespace asset { template P<TimeGrid> AssetManager::wrap (const TimeGrid& asset); -} // namespace asset +}} // namespace proc::asset diff --git a/src/proc/assetmanager.hpp b/src/proc/assetmanager.hpp index 3b69baa7c..61567c582 100644 --- a/src/proc/assetmanager.hpp +++ b/src/proc/assetmanager.hpp @@ -52,7 +52,7 @@ using std::string; using std::list; - +namespace proc { namespace asset { class DB; @@ -120,13 +120,13 @@ namespace asset { LUMIERA_ERROR_DECLARE (UNKNOWN_ASSET_ID); ///< use of a non-registered Asset ID. LUMIERA_ERROR_DECLARE (WRONG_ASSET_KIND); ///< Asset ID of wrong Asset kind, unable to cast. -} // namespace asset +}} // namespace proc::asset namespace proc_interface { - using asset::AssetManager; + using proc::asset::AssetManager; } #endif diff --git a/src/proc/common.hpp b/src/proc/common.hpp index 3b18c731b..6e6e2e491 100644 --- a/src/proc/common.hpp +++ b/src/proc/common.hpp @@ -41,7 +41,7 @@ - /* common types frequently used... */ + /* frequently used common types... */ #include "lib/p.hpp" #include "lib/util.hpp" @@ -60,7 +60,7 @@ namespace lumiera { /* additional global configuration goes here... */ - + } // namespace lumiera @@ -68,54 +68,75 @@ namespace lumiera { * Implementation namespace for support and library code. */ namespace lib { - + } /** - * The asset subsystem of the Proc-Layer. - * @todo refactor proc namespaces + * Proc-Layer implementation namespace root. + * Lumiera's middle layer contains the core models, both + * high-level (session) and low-level (render nodes), together with + * the Builder to translate between those two models, the command frontend, + * session support system, the playback-and-render-control subsystem and + * the API and backbone of the renderengine. Most render implementation + * code resides in the backend or is loaded from plug-ins though. */ -namespace asset { } - - -/** - * Proc-Layer dispatcher, controller and administrative facilities. - * @todo refactor proc namespaces - */ -namespace control { } - - -/** - * Render engine code as part of the Proc-Layer. - * Backbone of the engine, render nodes base and cooperation. - * A good deal of the active engine code is outside the scope of the - * Proc-Layer, e.g. code located in backend services and plugins. - * @todo refactor proc namespaces - */ -namespace engine { } - - - -/** - * Media-Objects, edit operations and high-level session. - * @todo is this interface or implementation ?? - * @todo refactor proc namespaces - */ -namespace mobject { - - +namespace proc { + + + /** - * Namespace of Session and user visible high-level objects. + * The asset subsystem of the Proc-Layer. */ - namespace session { } - - + namespace asset { } + + /** - * Namespace of the Builder, transforming high-level into low-level. + * Proc-Layer dispatcher, controller and administrative facilities. */ - namespace builder { } - -} + namespace control { } + + + /** + * Playback and rendering control subsystem. + * The so called "player" combines and orchestrates services from + * the engine, session and backend to perform playback or rendering + */ + namespace play { + + } + + + /** + * Render engine code as part of the Proc-Layer. + * Backbone of the engine, render nodes base and cooperation. + * A good deal of the active engine code is outside the scope of the + * Proc-Layer, e.g. code located in backend services and plugins. + */ + namespace engine { } + + + + /** + * Media-Objects, edit operations and high-level session. + * @todo is this interface or implementation ?? + */ + namespace mobject { + + + /** + * Namespace of Session and user visible high-level objects. + */ + namespace session { } + + + /** + * Namespace of the Builder, transforming high-level into low-level. + */ + namespace builder { } + + } + +} // proc #endif /*LUMIERA_H*/ diff --git a/src/proc/control/mediaimpllib.hpp b/src/proc/control/mediaimpllib.hpp index 0afa07b8a..4a5fd683f 100644 --- a/src/proc/control/mediaimpllib.hpp +++ b/src/proc/control/mediaimpllib.hpp @@ -25,7 +25,7 @@ #define CONTROL_MEDIAIMPLLIB_H -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" diff --git a/src/proc/control/stypemanager.cpp b/src/proc/control/stypemanager.cpp index 8c4d50672..31b5fc8c4 100644 --- a/src/proc/control/stypemanager.cpp +++ b/src/proc/control/stypemanager.cpp @@ -29,7 +29,6 @@ namespace control { - using lumiera::StreamType; using lib::Symbol; /* ======= stream type manager lifecycle ==========*/ diff --git a/src/proc/control/stypemanager.hpp b/src/proc/control/stypemanager.hpp index 0f40408cc..f3cbd3444 100644 --- a/src/proc/control/stypemanager.hpp +++ b/src/proc/control/stypemanager.hpp @@ -25,7 +25,7 @@ #define CONTROL_STYPEMANAGER_H -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" #include "lib/singleton.hpp" #include <boost/scoped_ptr.hpp> @@ -34,7 +34,6 @@ namespace control { using lib::Symbol; - using lumiera::StreamType; class STypeManager diff --git a/src/proc/control/styperegistry.hpp b/src/proc/control/styperegistry.hpp index a5593f9c7..73bf97ba1 100644 --- a/src/proc/control/styperegistry.hpp +++ b/src/proc/control/styperegistry.hpp @@ -50,7 +50,7 @@ namespace control { -// using lumiera::P; +// using lib::P; // using lumiera::Query; // using std::string; diff --git a/src/proc/controllerfacade.cpp b/src/proc/controllerfacade.cpp index 737f85589..84076b44e 100644 --- a/src/proc/controllerfacade.cpp +++ b/src/proc/controllerfacade.cpp @@ -23,8 +23,7 @@ #include "proc/controllerfacade.hpp" -namespace proc_interface - { +namespace proc_interface { /** */ diff --git a/src/proc/engine/buffer-local-key.hpp b/src/proc/engine/buffer-local-key.hpp index 15bfe07ef..b8fc66ee3 100644 --- a/src/proc/engine/buffer-local-key.hpp +++ b/src/proc/engine/buffer-local-key.hpp @@ -34,6 +34,7 @@ namespace lib { typedef size_t HashVal; } +namespace proc { namespace engine { namespace metadata { @@ -124,5 +125,5 @@ namespace engine { }; -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp index c33c59ea7..7aad2ceeb 100644 --- a/src/proc/engine/buffer-metadata.hpp +++ b/src/proc/engine/buffer-metadata.hpp @@ -65,6 +65,7 @@ #include <boost/noncopyable.hpp> +namespace proc { namespace engine { using lib::HashVal; @@ -789,5 +790,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp index 1303d1458..e4224556a 100644 --- a/src/proc/engine/buffer-provider.cpp +++ b/src/proc/engine/buffer-provider.cpp @@ -28,6 +28,7 @@ using util::isSameObject; +namespace proc { namespace engine { @@ -312,4 +313,4 @@ namespace engine { -} // namespace engine +}} // namespace engine diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 37add12f1..e0aaff78d 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -52,6 +52,7 @@ #include <boost/scoped_ptr.hpp> +namespace proc { namespace engine { using boost::scoped_ptr; @@ -165,5 +166,5 @@ namespace engine { } -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/buffhandle-attach.hpp b/src/proc/engine/buffhandle-attach.hpp index 0b2d3a0ae..aee29c386 100644 --- a/src/proc/engine/buffhandle-attach.hpp +++ b/src/proc/engine/buffhandle-attach.hpp @@ -50,6 +50,7 @@ #include "proc/engine/buffhandle.hpp" +namespace proc { namespace engine { @@ -125,5 +126,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index 9ed842cb4..24816854d 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -52,13 +52,14 @@ #include "lib/error.hpp" -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" #include "lib/bool-checkable.hpp" +namespace proc { namespace engine { - namespace error = lumiera::error; + namespace error = ::lumiera::error; using error::LUMIERA_ERROR_LIFECYCLE; typedef size_t HashVal; ////////////TICKET #722 @@ -110,7 +111,7 @@ namespace engine { class BuffHandle : public lib::BoolCheckable<BuffHandle> { - typedef lumiera::StreamType::ImplFacade::DataBuffer Buff; + typedef StreamType::ImplFacade::DataBuffer Buff; BufferDescriptor descriptor_; Buff* pBuffer_; @@ -180,5 +181,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/bufftable-obsolete.hpp b/src/proc/engine/bufftable-obsolete.hpp index 391808f65..665726804 100644 --- a/src/proc/engine/bufftable-obsolete.hpp +++ b/src/proc/engine/bufftable-obsolete.hpp @@ -38,6 +38,7 @@ ////////////////////////////////WARNING: ...just left in tree to keep it compiling ////////////////////////////////TICKET #826 need to be reworked entirely +namespace proc { namespace engine { using std::pair; @@ -183,5 +184,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/bufftable.hpp b/src/proc/engine/bufftable.hpp index 00738cd64..14ea61977 100644 --- a/src/proc/engine/bufftable.hpp +++ b/src/proc/engine/bufftable.hpp @@ -36,6 +36,7 @@ +namespace proc { namespace engine { using std::pair; @@ -154,5 +155,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/channel-descriptor.hpp b/src/proc/engine/channel-descriptor.hpp index 0a8e9887a..5133bc5f8 100644 --- a/src/proc/engine/channel-descriptor.hpp +++ b/src/proc/engine/channel-descriptor.hpp @@ -37,10 +37,11 @@ #include "lib/error.hpp" -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" #include "lib/bool-checkable.hpp" +namespace proc { namespace engine { namespace error = lumiera::error; @@ -71,5 +72,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/diagnostic-buffer-provider.cpp b/src/proc/engine/diagnostic-buffer-provider.cpp index 98f65cf94..e681cdc0c 100644 --- a/src/proc/engine/diagnostic-buffer-provider.cpp +++ b/src/proc/engine/diagnostic-buffer-provider.cpp @@ -28,6 +28,7 @@ #include "proc/engine/tracking-heap-block-provider.hpp" +namespace proc { namespace engine { @@ -104,4 +105,4 @@ namespace engine { } -} // namespace engine +}} // namespace engine diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp index cef2c1e0d..8355188b6 100644 --- a/src/proc/engine/diagnostic-buffer-provider.hpp +++ b/src/proc/engine/diagnostic-buffer-provider.hpp @@ -40,6 +40,7 @@ #include <boost/noncopyable.hpp> +namespace proc { namespace engine { namespace error = lumiera::error; @@ -106,5 +107,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/dispatcher.cpp b/src/proc/engine/dispatcher.cpp index 795b56c68..40f5e3c53 100644 --- a/src/proc/engine/dispatcher.cpp +++ b/src/proc/engine/dispatcher.cpp @@ -27,6 +27,7 @@ +namespace proc { namespace engine { /** */ @@ -34,4 +35,4 @@ namespace engine { -} // namespace engine +}} // namespace engine diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp index 7565c70eb..6be0a2634 100644 --- a/src/proc/engine/dispatcher.hpp +++ b/src/proc/engine/dispatcher.hpp @@ -30,6 +30,7 @@ +namespace proc { namespace engine { using lib::time::TimeSpan; @@ -58,5 +59,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/link.cpp b/src/proc/engine/link.cpp index e3e9096c6..0ee39458e 100644 --- a/src/proc/engine/link.cpp +++ b/src/proc/engine/link.cpp @@ -23,6 +23,7 @@ #include "proc/engine/link.hpp" +namespace proc { namespace engine { @@ -31,4 +32,4 @@ namespace engine -} // namespace engine + }} // namespace engine diff --git a/src/proc/engine/link.hpp b/src/proc/engine/link.hpp index ac3f70f01..ea9b17edb 100644 --- a/src/proc/engine/link.hpp +++ b/src/proc/engine/link.hpp @@ -28,8 +28,8 @@ -namespace engine - { +namespace proc { +namespace engine { /** @@ -50,5 +50,5 @@ namespace engine -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/mask.cpp b/src/proc/engine/mask.cpp index e742a9a91..b341f51b9 100644 --- a/src/proc/engine/mask.cpp +++ b/src/proc/engine/mask.cpp @@ -23,12 +23,12 @@ #include "proc/engine/mask.hpp" -namespace engine - { +namespace proc { +namespace engine { /** */ -} // namespace engine +}} // namespace engine diff --git a/src/proc/engine/mask.hpp b/src/proc/engine/mask.hpp index b805467a3..364f370a8 100644 --- a/src/proc/engine/mask.hpp +++ b/src/proc/engine/mask.hpp @@ -28,6 +28,7 @@ +namespace proc { namespace engine { @@ -38,5 +39,5 @@ namespace engine -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/nodefactory.cpp b/src/proc/engine/nodefactory.cpp index eb3ec1913..5812bf40a 100644 --- a/src/proc/engine/nodefactory.cpp +++ b/src/proc/engine/nodefactory.cpp @@ -27,6 +27,7 @@ #include "proc/engine/nodewiring.hpp" +namespace proc { namespace engine { namespace { // Details of node fabrication @@ -53,4 +54,4 @@ namespace engine { } -} // namespace engine +}} // namespace engine diff --git a/src/proc/engine/nodefactory.hpp b/src/proc/engine/nodefactory.hpp index 49f605ca4..63b5ca719 100644 --- a/src/proc/engine/nodefactory.hpp +++ b/src/proc/engine/nodefactory.hpp @@ -33,6 +33,7 @@ namespace lib { class AllocationCluster; } +namespace proc { namespace mobject { namespace session { @@ -75,5 +76,5 @@ namespace engine { }; -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/nodeinvocation.hpp b/src/proc/engine/nodeinvocation.hpp index 7bb92f1e2..18f1bb75e 100644 --- a/src/proc/engine/nodeinvocation.hpp +++ b/src/proc/engine/nodeinvocation.hpp @@ -61,6 +61,7 @@ +namespace proc { namespace engine { @@ -232,5 +233,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp index 34e8b684f..e656e9813 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -70,6 +70,7 @@ +namespace proc { namespace engine { namespace config { diff --git a/src/proc/engine/nodewiring-config.hpp b/src/proc/engine/nodewiring-config.hpp index 62ce24abc..713c68828 100644 --- a/src/proc/engine/nodewiring-config.hpp +++ b/src/proc/engine/nodewiring-config.hpp @@ -60,6 +60,7 @@ #include <map> +namespace proc { namespace engine { namespace config { diff --git a/src/proc/engine/nodewiring-def.hpp b/src/proc/engine/nodewiring-def.hpp index ef0173820..20477aa00 100644 --- a/src/proc/engine/nodewiring-def.hpp +++ b/src/proc/engine/nodewiring-def.hpp @@ -50,6 +50,7 @@ +namespace proc { namespace engine { using lib::RefArray; @@ -184,5 +185,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/nodewiring.cpp b/src/proc/engine/nodewiring.cpp index 9be5dd541..f216c0d14 100644 --- a/src/proc/engine/nodewiring.cpp +++ b/src/proc/engine/nodewiring.cpp @@ -29,6 +29,7 @@ #include "lib/meta/typelist-manip.hpp" +namespace proc { namespace engine { namespace config { @@ -149,4 +150,4 @@ namespace engine { } -} // namespace engine +}} // namespace engine diff --git a/src/proc/engine/nodewiring.hpp b/src/proc/engine/nodewiring.hpp index 9f263ab06..d420643f7 100644 --- a/src/proc/engine/nodewiring.hpp +++ b/src/proc/engine/nodewiring.hpp @@ -35,6 +35,7 @@ +namespace proc { namespace engine { @@ -96,5 +97,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/pluginadapter.cpp b/src/proc/engine/pluginadapter.cpp index 7d6a16031..5bbc144e8 100644 --- a/src/proc/engine/pluginadapter.cpp +++ b/src/proc/engine/pluginadapter.cpp @@ -23,12 +23,12 @@ #include "proc/engine/pluginadapter.hpp" -namespace engine - { +namespace proc { +namespace engine { /** */ -} // namespace engine +}} // namespace engine diff --git a/src/proc/engine/pluginadapter.hpp b/src/proc/engine/pluginadapter.hpp index 19ed23f3b..38d7152a3 100644 --- a/src/proc/engine/pluginadapter.hpp +++ b/src/proc/engine/pluginadapter.hpp @@ -28,6 +28,7 @@ +namespace proc { namespace engine { @@ -44,5 +45,5 @@ namespace engine -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/procnode.hpp b/src/proc/engine/procnode.hpp index a5eac5bc0..47d7a6d75 100644 --- a/src/proc/engine/procnode.hpp +++ b/src/proc/engine/procnode.hpp @@ -53,6 +53,7 @@ +namespace proc { namespace engine { using std::vector; @@ -172,5 +173,5 @@ namespace engine { } -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/render-invocation.cpp b/src/proc/engine/render-invocation.cpp index 90c9bf147..adf7d67ac 100644 --- a/src/proc/engine/render-invocation.cpp +++ b/src/proc/engine/render-invocation.cpp @@ -25,6 +25,7 @@ #include "proc/engine/stateproxy.hpp" +namespace proc { namespace engine { namespace { // Details... @@ -55,4 +56,4 @@ namespace engine { } -} // namespace engine +}} // namespace engine diff --git a/src/proc/engine/render-invocation.hpp b/src/proc/engine/render-invocation.hpp index 481d5e691..deb2b27f7 100644 --- a/src/proc/engine/render-invocation.hpp +++ b/src/proc/engine/render-invocation.hpp @@ -42,6 +42,7 @@ +namespace proc { namespace engine { @@ -74,5 +75,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/renderengine.cpp b/src/proc/engine/renderengine.cpp index 588d1276b..706a87e07 100644 --- a/src/proc/engine/renderengine.cpp +++ b/src/proc/engine/renderengine.cpp @@ -23,6 +23,7 @@ #include "proc/engine/renderengine.hpp" +namespace proc { namespace engine { @@ -30,4 +31,4 @@ namespace engine { -} // namespace engine +}} // namespace engine diff --git a/src/proc/engine/renderengine.hpp b/src/proc/engine/renderengine.hpp index ebbd46e5a..abe036bef 100644 --- a/src/proc/engine/renderengine.hpp +++ b/src/proc/engine/renderengine.hpp @@ -37,6 +37,7 @@ using std::list; //////////TODO for the "real" engine API: look at engine-serivce.hpp +namespace proc { namespace engine { @@ -59,5 +60,5 @@ namespace engine { }; -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/rendergraph.cpp b/src/proc/engine/rendergraph.cpp index fefaf7c63..0c100fde3 100644 --- a/src/proc/engine/rendergraph.cpp +++ b/src/proc/engine/rendergraph.cpp @@ -32,6 +32,7 @@ namespace lumiera { } +namespace proc { namespace engine { /** */ @@ -39,4 +40,4 @@ namespace engine { -} // namespace engine +}} // namespace engine diff --git a/src/proc/engine/rendergraph.hpp b/src/proc/engine/rendergraph.hpp index eb811de6f..63c039e0f 100644 --- a/src/proc/engine/rendergraph.hpp +++ b/src/proc/engine/rendergraph.hpp @@ -34,6 +34,7 @@ /////////////////////////////TODO consequently this will become some kind of root or anchor point for this network +namespace proc { namespace engine { using lib::time::TimeSpan; @@ -64,5 +65,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/source.cpp b/src/proc/engine/source.cpp index 68cad7a62..477bc768e 100644 --- a/src/proc/engine/source.cpp +++ b/src/proc/engine/source.cpp @@ -23,6 +23,7 @@ #include "proc/engine/source.hpp" +namespace proc { namespace engine { @@ -31,4 +32,4 @@ namespace engine -} // namespace engine +}} // namespace engine diff --git a/src/proc/engine/source.hpp b/src/proc/engine/source.hpp index 8587c3d67..fedeb1784 100644 --- a/src/proc/engine/source.hpp +++ b/src/proc/engine/source.hpp @@ -28,6 +28,7 @@ +namespace proc { namespace engine { @@ -47,5 +48,5 @@ namespace engine -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/stateproxy.cpp b/src/proc/engine/stateproxy.cpp index 90e36a094..c62a468a5 100644 --- a/src/proc/engine/stateproxy.cpp +++ b/src/proc/engine/stateproxy.cpp @@ -23,6 +23,7 @@ #include "proc/engine/stateproxy.hpp" +namespace proc { namespace engine { @@ -84,4 +85,4 @@ namespace engine { } -} // namespace engine +}} // namespace engine diff --git a/src/proc/engine/stateproxy.hpp b/src/proc/engine/stateproxy.hpp index bca680be3..269e47dae 100644 --- a/src/proc/engine/stateproxy.hpp +++ b/src/proc/engine/stateproxy.hpp @@ -28,6 +28,7 @@ #include "proc/state.hpp" +namespace proc { namespace engine { @@ -56,5 +57,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/tracking-heap-block-provider.cpp b/src/proc/engine/tracking-heap-block-provider.cpp index f9e951889..c9f96aa28 100644 --- a/src/proc/engine/tracking-heap-block-provider.cpp +++ b/src/proc/engine/tracking-heap-block-provider.cpp @@ -43,6 +43,7 @@ using lib::ScopedPtrVect; +namespace proc { namespace engine { namespace error = lumiera::error; @@ -322,4 +323,4 @@ namespace engine { -} // namespace engine +}} // namespace engine diff --git a/src/proc/engine/tracking-heap-block-provider.hpp b/src/proc/engine/tracking-heap-block-provider.hpp index a42fb65f8..d9aa53c26 100644 --- a/src/proc/engine/tracking-heap-block-provider.hpp +++ b/src/proc/engine/tracking-heap-block-provider.hpp @@ -52,6 +52,7 @@ #include <boost/scoped_array.hpp> +namespace proc { namespace engine { namespace error = lumiera::error; @@ -183,5 +184,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/type-handler.hpp b/src/proc/engine/type-handler.hpp index d7130d308..b02f6e637 100644 --- a/src/proc/engine/type-handler.hpp +++ b/src/proc/engine/type-handler.hpp @@ -49,6 +49,7 @@ #include <boost/functional/hash.hpp> +namespace proc { namespace engine { using lib::HashVal; @@ -56,7 +57,7 @@ namespace engine { using std::tr1::function; using std::tr1::placeholders::_1; - namespace error = lumiera::error; + namespace error = ::lumiera::error; namespace { // (optional) helpers to build an object embedded into a buffer... @@ -178,5 +179,5 @@ namespace engine { -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/common/external/libgavl.cpp b/src/proc/external/libgavl.cpp similarity index 94% rename from src/common/external/libgavl.cpp rename to src/proc/external/libgavl.cpp index b60caecea..637e7c559 100644 --- a/src/common/external/libgavl.cpp +++ b/src/proc/external/libgavl.cpp @@ -22,7 +22,7 @@ #include "proc/common.hpp" -#include "common/external/libgavl.hpp" +#include "proc/external/libgavl.hpp" #include "proc/control/stypemanager.hpp" extern "C" { @@ -30,8 +30,8 @@ extern "C" { } -namespace lib { - namespace external { +namespace proc { +namespace external { using control::STypeManager; using control::ON_STREAMTYPES_RESET; @@ -66,6 +66,4 @@ namespace lib { - } // namespace external - -} // namespace lib +}} // namespace proc::external diff --git a/src/common/external/libgavl.hpp b/src/proc/external/libgavl.hpp similarity index 92% rename from src/common/external/libgavl.hpp rename to src/proc/external/libgavl.hpp index ac6a00b03..632a76c66 100644 --- a/src/common/external/libgavl.hpp +++ b/src/proc/external/libgavl.hpp @@ -21,21 +21,20 @@ */ -#ifndef LIB_EXTERN_LIBGAVL_H -#define LIB_EXTERN_LIBGAVL_H +#ifndef PROC_EXTERNAL_LIBGAVL_H +#define PROC_EXTERNAL_LIBGAVL_H #include "proc/control/mediaimpllib.hpp" -namespace lib { - namespace external { +namespace proc { +namespace external { using lib::Symbol; - using lumiera::StreamType; typedef StreamType::ImplFacade ImplFacade; typedef StreamType::ImplFacade::TypeTag TypeTag; @@ -84,7 +83,5 @@ namespace lib { - } // namespace external - -} // namespace lib +}} // namespace proc::external #endif diff --git a/src/proc/facade.cpp b/src/proc/facade.cpp index de6d562ee..e1339d05e 100644 --- a/src/proc/facade.cpp +++ b/src/proc/facade.cpp @@ -32,6 +32,7 @@ namespace proc { using std::string; using lumiera::Subsys; + using lumiera::Option; class BuilderSubsysDescriptor @@ -40,14 +41,14 @@ namespace proc { operator string () const { return "Builder"; } bool - shouldStart (lumiera::Option&) + shouldStart (Option&) { TODO ("determine, if we need a Builder Thread"); return false; } bool - start (lumiera::Option&, Subsys::SigTerm termination) + start (Option&, Subsys::SigTerm termination) { UNIMPLEMENTED ("fire up a Builder in a separate Thread, and register shutdown hook"); return false; @@ -76,14 +77,14 @@ namespace proc { operator string () const { return "Session"; } bool - shouldStart (lumiera::Option&) + shouldStart (Option&) { TODO ("determine, if an existing Session should be loaded"); return false; } bool - start (lumiera::Option&, Subsys::SigTerm termination) + start (Option&, Subsys::SigTerm termination) { UNIMPLEMENTED ("load an existing session as denoted by the options and register shutdown hook"); return false; @@ -119,14 +120,14 @@ namespace proc { * @todo actually define cmdline options and parse/decide here! */ bool - shouldStart (lumiera::Option&) + shouldStart (Option&) { TODO ("extract options about specific output systems to be brought up"); return false; } bool - start (lumiera::Option&, Subsys::SigTerm termination) + start (Option&, Subsys::SigTerm termination) { this->completedSignal_ = termination; return play::OutputDirector::instance().connectUp(); diff --git a/src/proc/facade.hpp b/src/proc/facade.hpp index 18ef46454..0f9e227d8 100644 --- a/src/proc/facade.hpp +++ b/src/proc/facade.hpp @@ -20,6 +20,17 @@ */ +/** @file facade.hpp + ** Top level entrance point and facade for the Proc-Layer. + ** The middle layer of the application holds a session with the + ** high-level model, to be translated by the Builder into a node network, + ** which can be \em performed by the Engine to render output. + ** + ** @see common.hpp + ** + */ + + #ifndef PROC_INTERFACE_FACADE_H #define PROC_INTERFACE_FACADE_H @@ -29,12 +40,6 @@ -/** - * Lumiera Proc-Layer implementation root. - * The middle layer of the application holds a session with the - * high-level model, to be translated by the Builder into a node network, - * which can be \em performed by the Engine to render output. - */ namespace proc { diff --git a/src/proc/mobject/builder/applicable-builder-target-types.hpp b/src/proc/mobject/builder/applicable-builder-target-types.hpp index 57c2e66f9..58058f027 100644 --- a/src/proc/mobject/builder/applicable-builder-target-types.hpp +++ b/src/proc/mobject/builder/applicable-builder-target-types.hpp @@ -63,7 +63,7 @@ /////////////////////////////////TICKET #414 - +namespace proc { namespace mobject { namespace builder { @@ -92,5 +92,5 @@ namespace builder { -}} // namespace mobject::builder +}}} // namespace mobject::builder #endif diff --git a/src/proc/mobject/builder/assembler.cpp b/src/proc/mobject/builder/assembler.cpp index b4d05bb07..3ea5a0bd7 100644 --- a/src/proc/mobject/builder/assembler.cpp +++ b/src/proc/mobject/builder/assembler.cpp @@ -23,20 +23,17 @@ #include "proc/mobject/builder/assembler.hpp" -namespace mobject +namespace proc { +namespace mobject { +namespace builder { + + + + engine::RenderEngine & + Assembler::build () { - namespace builder - { + } - engine::RenderEngine & - Assembler::build () - { - } - - - - } // namespace mobject::builder - -} // namespace mobject +}}} // namespace proc::mobject::builder diff --git a/src/proc/mobject/builder/assembler.hpp b/src/proc/mobject/builder/assembler.hpp index f874b99bd..095052581 100644 --- a/src/proc/mobject/builder/assembler.hpp +++ b/src/proc/mobject/builder/assembler.hpp @@ -27,28 +27,24 @@ #include "proc/engine/renderengine.hpp" - -namespace mobject - { - namespace builder +namespace proc { +namespace mobject { +namespace builder { + + + /** + * This is the actual building facility: + * provided the correct tools and associations, + * it serves to build and connect the individual ProcNode objects + */ + class Assembler { - - - /** - * This is the actual building facility: - * provided the correct tools and associations, - * it serves to build and connect the individual ProcNode objects - */ - class Assembler - { - public: - engine::RenderEngine & build () ; - // TODO: allocation, GC??? - }; - - - - } // namespace mobject::builder - -} // namespace mobject + public: + engine::RenderEngine & build () ; + // TODO: allocation, GC??? + }; + + + +}}} // namespace proc::mobject::builder #endif diff --git a/src/proc/mobject/builder/buildertool.hpp b/src/proc/mobject/builder/buildertool.hpp index 9d51361d4..e707aabcc 100644 --- a/src/proc/mobject/builder/buildertool.hpp +++ b/src/proc/mobject/builder/buildertool.hpp @@ -61,13 +61,14 @@ #include "proc/mobject/explicitplacement.hpp" +namespace proc { namespace mobject { class Buildable; namespace builder { - using lumiera::P; + using lib::P; /** * Policy invoking an catch-all function for processing @@ -105,9 +106,9 @@ namespace mobject { * as we simply store a pointer within the BuilderTool instance. */ class BuilderTool - : public lumiera::visitor::Tool<void, InvokeCatchAllFunction> + : public ::lumiera::visitor::Tool<void, InvokeCatchAllFunction> { - lumiera::WrapperPtr currentWrapper_; + ::lumiera::WrapperPtr currentWrapper_; public: @@ -146,7 +147,7 @@ namespace mobject { } template<class TAR> - lumiera::P<TAR> + P<TAR> getPtr () { P<TAR>* pP = currentWrapper_.get<P<TAR>*>(); @@ -166,11 +167,11 @@ namespace mobject { class TYPELIST // list of all concrete Buildables to be treated > class Applicable - : public lumiera::visitor::Applicable<TOOLImpl, TYPELIST, BuilderTool> + : public ::lumiera::visitor::Applicable<TOOLImpl, TYPELIST, BuilderTool> { } ; - using lumiera::typelist::Types; // convenience for the users of "Applicable" + using ::lumiera::typelist::Types; // convenience for the users of "Applicable" }// namespace mobject::builder @@ -181,7 +182,7 @@ namespace mobject { /** * Marker Interface for classes visitable by Builder tools. */ - class Buildable : public lumiera::visitor::Visitable<builder::BuilderTool> + class Buildable : public ::lumiera::visitor::Visitable<builder::BuilderTool> { }; @@ -206,5 +207,5 @@ namespace mobject { } -}} // namespace mobject::builder +}}} // namespace proc::mobject::builder #endif diff --git a/src/proc/mobject/builder/common.hpp b/src/proc/mobject/builder/common.hpp index ba09ec9ca..35d51c2a5 100644 --- a/src/proc/mobject/builder/common.hpp +++ b/src/proc/mobject/builder/common.hpp @@ -27,11 +27,11 @@ #include "lib/error.hpp" #include "include/logging.h" - +namespace proc { namespace mobject { namespace builder { -}} // namespace mobject::builder +}}} // namespace mobject::builder #endif diff --git a/src/proc/mobject/builder/conmanager.cpp b/src/proc/mobject/builder/conmanager.cpp index b13c19b67..082512d90 100644 --- a/src/proc/mobject/builder/conmanager.cpp +++ b/src/proc/mobject/builder/conmanager.cpp @@ -24,21 +24,20 @@ #include "proc/mobject/builder/conmanager.hpp" #include "proc/control/stypemanager.hpp" +namespace proc { namespace mobject { - namespace builder { - - - - /** - * TODO !!!!!!!!!!!!!!!!!! - */ - int - ConManager::getConnection () - { - } +namespace builder { + + + + /** + * TODO !!!!!!!!!!!!!!!!!! + */ + int + ConManager::getConnection () + { + } - } // namespace mobject::builder - -} // namespace mobject +}}} // namespace proc::mobject::builder diff --git a/src/proc/mobject/builder/conmanager.hpp b/src/proc/mobject/builder/conmanager.hpp index db699d868..6e9c850c0 100644 --- a/src/proc/mobject/builder/conmanager.hpp +++ b/src/proc/mobject/builder/conmanager.hpp @@ -21,38 +21,37 @@ */ -#ifndef MOBJECT_BUILDER_CONMANAGER_H -#define MOBJECT_BUILDER_CONMANAGER_H +#ifndef PROC_MOBJECT_BUILDER_CONMANAGER_H +#define PROC_MOBJECT_BUILDER_CONMANAGER_H +namespace proc { namespace mobject { - namespace builder { - - - /** - * Connection Manager: used to build the connections between render engine nodes - * if these nodes need to cooperate besides the normal "data pull" operation. - * Esp. the Connection Manager knows how to wire up the effect's parameters - * with the corresponding ParamProviders (automation) in the Session. - * Questions regarding the possibility of a media stream connection are - * delegated internally to the STypeManager. - * \par - * The primary service of the connection manager is to accept a wiring request - * and handle the details of establishing the necessary connections. - */ - class ConManager - { - public: - /** - * TODO just a design sketch, work out signatures etc... - */ - int getConnection () ; - }; +namespace builder { + + + /** + * Connection Manager: used to build the connections between render engine nodes + * if these nodes need to cooperate besides the normal "data pull" operation. + * Esp. the Connection Manager knows how to wire up the effect's parameters + * with the corresponding ParamProviders (automation) in the Session. + * Questions regarding the possibility of a media stream connection are + * delegated internally to the STypeManager. + * \par + * The primary service of the connection manager is to accept a wiring request + * and handle the details of establishing the necessary connections. + */ + class ConManager + { + public: + /** + * TODO just a design sketch, work out signatures etc... + */ + int getConnection () ; + }; - } // namespace mobject::builder - -} // namespace mobject +}}} // namespace proc::mobject::builder #endif diff --git a/src/proc/mobject/builder/fixture-change-detector.hpp b/src/proc/mobject/builder/fixture-change-detector.hpp index e2da29419..6c1065396 100644 --- a/src/proc/mobject/builder/fixture-change-detector.hpp +++ b/src/proc/mobject/builder/fixture-change-detector.hpp @@ -51,6 +51,7 @@ //#include <map> +namespace proc { namespace mobject { namespace builder { @@ -77,5 +78,5 @@ namespace builder { -}} // namespace mobject::builder +}}} // namespace proc::mobject::builder #endif diff --git a/src/proc/mobject/builder/model-port-registry.cpp b/src/proc/mobject/builder/model-port-registry.cpp index c3c16d7eb..f19ef1b2a 100644 --- a/src/proc/mobject/builder/model-port-registry.cpp +++ b/src/proc/mobject/builder/model-port-registry.cpp @@ -43,6 +43,7 @@ #include "proc/mobject/model-port.hpp" #include "proc/mobject/builder/model-port-registry.hpp" +namespace proc { namespace mobject { namespace builder { @@ -79,7 +80,7 @@ namespace mobject { ModelPortRegistry::setActiveInstance (ModelPortRegistry& newRegistry) { LockRegistry global_lock; - ModelPortRegistry *previous = theGlobalRegistry.isValid()? + ModelPortRegistry *previous = theGlobalRegistry.isValid()? &( theGlobalRegistry()) : 0; INFO_IF (!previous, builder, "activating new ModelPort registry."); WARN_IF ( previous, builder, "switching ModelPort registry instance."); @@ -299,4 +300,4 @@ namespace mobject { -} // namespace mobject +}} // namespace proc::mobject diff --git a/src/proc/mobject/builder/model-port-registry.hpp b/src/proc/mobject/builder/model-port-registry.hpp index 078c1928b..694e5d783 100644 --- a/src/proc/mobject/builder/model-port-registry.hpp +++ b/src/proc/mobject/builder/model-port-registry.hpp @@ -57,6 +57,7 @@ #include <map> +namespace proc { namespace mobject { namespace builder { @@ -177,5 +178,5 @@ namespace builder { -}} // namespace mobject::builder +}}} // namespace proc::mobject::builder #endif diff --git a/src/proc/mobject/builder/mould.hpp b/src/proc/mobject/builder/mould.hpp index 5db2ece7f..e1bf5cb73 100644 --- a/src/proc/mobject/builder/mould.hpp +++ b/src/proc/mobject/builder/mould.hpp @@ -30,58 +30,57 @@ #include <vector> +namespace proc { namespace mobject { - namespace builder { +namespace builder { - using std::vector; - using asset::PProcPatt; - using lib::Symbol; - - - /** - * Interface: a workbench-like tool used by the builder - * for wiring up a specific building situation, followed by - * the application/execution of a single building step. Mould is - * conceived as the passive part, while usually the ProcPatt plays - * the role of the active counterpart. By means of the Mould interface, - * the specifics of a build situation are abstracted away, thus allowing - * the processing pattern to be defined as working on symbolic locations. - * Most commonly this is "current", denoting the render node just being built. - * <ul><li>PipeMould supports attaching an effect to a pipe</li> - * <li>combining pipes via a transition is done by a CombiningMould</li> - * <li>a SourceChainMould allows to start out from a source reader and build a clip</li> - * <li>wiring general connections is supported by the WiringMould</li> - * </ul> - * @see ToolFactory - * @see NodeCreatorTool - */ - class Mould - { - public: - vector<Mould> operate (); - - OperationPoint& getLocation (Symbol locationID); - }; - - - class PipeMould : public Mould - { - }; - - class CombiningMould : public Mould - { - }; - - class SourceChainMould : public Mould - { - }; - - class WiringMould : public Mould - { - }; - - - } // namespace mobject::builder - -} // namespace mobject + using std::vector; + using asset::PProcPatt; + using lib::Symbol; + + + /** + * Interface: a workbench-like tool used by the builder + * for wiring up a specific building situation, followed by + * the application/execution of a single building step. Mould is + * conceived as the passive part, while usually the ProcPatt plays + * the role of the active counterpart. By means of the Mould interface, + * the specifics of a build situation are abstracted away, thus allowing + * the processing pattern to be defined as working on symbolic locations. + * Most commonly this is "current", denoting the render node just being built. + * <ul><li>PipeMould supports attaching an effect to a pipe</li> + * <li>combining pipes via a transition is done by a CombiningMould</li> + * <li>a SourceChainMould allows to start out from a source reader and build a clip</li> + * <li>wiring general connections is supported by the WiringMould</li> + * </ul> + * @see ToolFactory + * @see NodeCreatorTool + */ + class Mould + { + public: + vector<Mould> operate (); + + OperationPoint& getLocation (Symbol locationID); + }; + + + class PipeMould : public Mould + { + }; + + class CombiningMould : public Mould + { + }; + + class SourceChainMould : public Mould + { + }; + + class WiringMould : public Mould + { + }; + + +}}} // namespace proc::mobject::builder #endif diff --git a/src/proc/mobject/builder/nodecreatortool.cpp b/src/proc/mobject/builder/nodecreatortool.cpp index 9dcb639b7..7f617f28e 100644 --- a/src/proc/mobject/builder/nodecreatortool.cpp +++ b/src/proc/mobject/builder/nodecreatortool.cpp @@ -24,52 +24,52 @@ #include "proc/mobject/builder/nodecreatortool.hpp" -using mobject::Buildable; -using mobject::session::Clip; -using mobject::session::Effect; -using mobject::session::Auto; +namespace proc { namespace mobject { - namespace builder { - - /////////////////////////////////TICKET #414 - - - ////TODO: do we ever get to treat a model root element?? - - - void - NodeCreatorTool::treat (Buildable& something) - { - } +namespace builder { + + using mobject::Buildable; + using mobject::session::Clip; + using mobject::session::Effect; + using mobject::session::Auto; + + /////////////////////////////////TICKET #414 + + + ////TODO: do we ever get to treat a model root element?? + + + void + NodeCreatorTool::treat (Buildable& something) + { + } - void - NodeCreatorTool::treat (Clip& clip) - { - } + void + NodeCreatorTool::treat (Clip& clip) + { + } - void - NodeCreatorTool::treat (Effect& effect) - { - } + void + NodeCreatorTool::treat (Effect& effect) + { + } - void - NodeCreatorTool::treat (Auto<double>& automation) - { - } - - - void - NodeCreatorTool::onUnknown (Buildable& target) - { - UNIMPLEMENTED ("catch-all when partitioning timeline"); ////////TODO: verify why this gets enforced here... - } + void + NodeCreatorTool::treat (Auto<double>& automation) + { + } + + + void + NodeCreatorTool::onUnknown (Buildable& target) + { + UNIMPLEMENTED ("catch-all when partitioning timeline"); ////////TODO: verify why this gets enforced here... + } - } // namespace mobject::builder - -} // namespace mobject +}}} // namespace proc::mobject::builder diff --git a/src/proc/mobject/builder/nodecreatortool.hpp b/src/proc/mobject/builder/nodecreatortool.hpp index fda4c95db..c808dce2e 100644 --- a/src/proc/mobject/builder/nodecreatortool.hpp +++ b/src/proc/mobject/builder/nodecreatortool.hpp @@ -31,6 +31,7 @@ +namespace proc { namespace mobject { namespace builder { @@ -71,5 +72,5 @@ namespace builder { -}} // namespace mobject::builder +}}} // namespace proc:mobject::builder #endif diff --git a/src/proc/mobject/builder/operationpoint.cpp b/src/proc/mobject/builder/operationpoint.cpp index e27ffc5b4..db1165ff1 100644 --- a/src/proc/mobject/builder/operationpoint.cpp +++ b/src/proc/mobject/builder/operationpoint.cpp @@ -26,11 +26,12 @@ #include "proc/asset/media.hpp" #include "proc/asset/proc.hpp" #include "proc/engine/nodefactory.hpp" -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" //#include "common/util.hpp" //#include <boost/ptr_container/ptr_vector.hpp> +namespace proc { namespace mobject { namespace builder { @@ -38,7 +39,6 @@ namespace builder { using engine::NodeFactory; using engine::PNode; - using lumiera::StreamType; struct RefPoint @@ -108,4 +108,4 @@ namespace builder { -}} // namespace mobject::builder +}}} // namespace proc::mobject::builder diff --git a/src/proc/mobject/builder/operationpoint.hpp b/src/proc/mobject/builder/operationpoint.hpp index 49b5855bb..fc534ffc1 100644 --- a/src/proc/mobject/builder/operationpoint.hpp +++ b/src/proc/mobject/builder/operationpoint.hpp @@ -32,6 +32,8 @@ #include <string> +namespace proc { + namespace asset { class Proc; } namespace asset { class Media; } namespace engine { class NodeFactory; } @@ -76,5 +78,5 @@ namespace builder { -}} // namespace mobject::builder +}}} // namespace proc::mobject::builder #endif diff --git a/src/proc/mobject/builder/renderstate.hpp b/src/proc/mobject/builder/renderstate.hpp index 3da0e094b..935aaab7e 100644 --- a/src/proc/mobject/builder/renderstate.hpp +++ b/src/proc/mobject/builder/renderstate.hpp @@ -28,28 +28,27 @@ +namespace proc { namespace mobject { - namespace builder { - - typedef proc_interface::State State; - - - /** - * Encapsulates the logic used to get a "render process". - * The provided StateProxy serves to hold any mutalbe state used - * in the render process, so the rest of the render engine - * can be stateless. - * @todo probably the state management will work different (6/08) - */ - class RenderState - { - public: - State& getRenderProcess () ; - }; +namespace builder { + + typedef proc_interface::State State; + + + /** + * Encapsulates the logic used to get a "render process". + * The provided StateProxy serves to hold any mutalbe state used + * in the render process, so the rest of the render engine + * can be stateless. + * @todo probably the state management will work different (6/08) + */ + class RenderState + { + public: + State& getRenderProcess () ; + }; - } // namespace mobject::session - -} // namespace mobject +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/builder/segmentation-tool.cpp b/src/proc/mobject/builder/segmentation-tool.cpp index 39b47380f..69263068c 100644 --- a/src/proc/mobject/builder/segmentation-tool.cpp +++ b/src/proc/mobject/builder/segmentation-tool.cpp @@ -29,6 +29,7 @@ using mobject::session::Clip; using mobject::session::Effect; +namespace proc { namespace mobject { namespace builder { @@ -76,4 +77,4 @@ namespace builder { } -}} // namespace mobject::builder +}}} // namespace proc::mobject::builder diff --git a/src/proc/mobject/builder/segmentation-tool.hpp b/src/proc/mobject/builder/segmentation-tool.hpp index 52bb1c5c1..720938cc5 100644 --- a/src/proc/mobject/builder/segmentation-tool.hpp +++ b/src/proc/mobject/builder/segmentation-tool.hpp @@ -36,6 +36,7 @@ using std::list; +namespace proc { namespace mobject { namespace builder { @@ -74,5 +75,5 @@ namespace builder { -}} // namespace mobject::builder +}}} // namespace proc::mobject::builder #endif diff --git a/src/proc/mobject/builder/segmentation.cpp b/src/proc/mobject/builder/segmentation.cpp index e7cd9cbf1..40228949f 100644 --- a/src/proc/mobject/builder/segmentation.cpp +++ b/src/proc/mobject/builder/segmentation.cpp @@ -38,6 +38,7 @@ #include "proc/mobject/builder/segmentation.hpp" #include "proc/mobject/builder/fixture-change-detector.hpp" +namespace proc { namespace mobject { namespace builder { @@ -82,4 +83,4 @@ namespace builder { -}}// namespace mobject::builder +}}}// namespace proc::mobject::builder diff --git a/src/proc/mobject/builder/segmentation.hpp b/src/proc/mobject/builder/segmentation.hpp index 028e47f26..0eed4ed78 100644 --- a/src/proc/mobject/builder/segmentation.hpp +++ b/src/proc/mobject/builder/segmentation.hpp @@ -45,6 +45,7 @@ //#include <map> +namespace proc { namespace mobject { namespace builder { @@ -83,5 +84,5 @@ namespace builder { -}} // namespace mobject::builder +}}} // namespace proc::mobject::builder #endif diff --git a/src/proc/mobject/builder/toolfactory.cpp b/src/proc/mobject/builder/toolfactory.cpp index ab844f574..1a679c864 100644 --- a/src/proc/mobject/builder/toolfactory.cpp +++ b/src/proc/mobject/builder/toolfactory.cpp @@ -26,6 +26,7 @@ //#include <boost/ptr_container/ptr_vector.hpp> +namespace proc { namespace mobject { namespace builder { @@ -91,4 +92,4 @@ namespace builder { -}} // namespace mobject::builder +}}} // namespace proc::mobject::builder diff --git a/src/proc/mobject/builder/toolfactory.hpp b/src/proc/mobject/builder/toolfactory.hpp index 201653fde..83ca8303b 100644 --- a/src/proc/mobject/builder/toolfactory.hpp +++ b/src/proc/mobject/builder/toolfactory.hpp @@ -35,6 +35,7 @@ +namespace proc { namespace mobject { namespace builder { @@ -69,5 +70,5 @@ namespace builder { -}} // namespace mobject::builder +}}} // namespace proc::mobject::builder #endif diff --git a/src/proc/mobject/builder/wiringrequest.hpp b/src/proc/mobject/builder/wiringrequest.hpp index 1ebb4515c..25f908994 100644 --- a/src/proc/mobject/builder/wiringrequest.hpp +++ b/src/proc/mobject/builder/wiringrequest.hpp @@ -26,29 +26,28 @@ +namespace proc { namespace mobject { - namespace builder { - - - /** - * A stateful value object denoting the wish to establish a link or connection - * between two entities. Used to organise the proper working of the build process. - * Wiring requests are first to be checked and can be deemed impossible to - * satisfy. Internally, wiring requests contain specific information about - * the objects to be connected. This information is exposed only to the - * ConManager, which is the facility actually wiring the connections. - */ - class WiringRequest - { - public: - /** - * TODO design sketch...... - */ - }; +namespace builder { + + + /** + * A statefull value object denoting the wish to establish a link or connection + * between two entities. Used to organise the proper working of the build process. + * Wiring requests are first to be checked and can be deemed impossible to + * satisfy. Internally, wiring requests contain specific information about + * the objects to be connected. This information is exposed only to the + * ConManager, which is the facility actually wiring the connections. + */ + class WiringRequest + { + public: + /** + * TODO design sketch...... + */ + }; - } // namespace mobject::builder - -} // namespace mobject +}}} // namespace proc::mobject::builder #endif diff --git a/src/proc/mobject/builderfacade.cpp b/src/proc/mobject/builderfacade.cpp index b2f1377d5..ec4c86682 100644 --- a/src/proc/mobject/builderfacade.cpp +++ b/src/proc/mobject/builderfacade.cpp @@ -24,6 +24,7 @@ #include "proc/mobject/builder/common.hpp" #include "proc/mobject/builderfacade.hpp" +namespace proc { namespace mobject { @@ -42,4 +43,4 @@ namespace mobject { -} // namespace mobject +}} // namespace mobject diff --git a/src/proc/mobject/builderfacade.hpp b/src/proc/mobject/builderfacade.hpp index 4a27d8b88..c58ce3692 100644 --- a/src/proc/mobject/builderfacade.hpp +++ b/src/proc/mobject/builderfacade.hpp @@ -28,7 +28,7 @@ #include "proc/engine/renderengine.hpp" - +namespace proc { namespace mobject { LUMIERA_ERROR_DECLARE (BUILDER_LIFECYCLE); ///< Builder activated while in non operational state @@ -54,5 +54,5 @@ namespace mobject { -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/explicitplacement.hpp b/src/proc/mobject/explicitplacement.hpp index 363e8beef..3d1e5c613 100644 --- a/src/proc/mobject/explicitplacement.hpp +++ b/src/proc/mobject/explicitplacement.hpp @@ -28,6 +28,7 @@ +namespace proc { namespace mobject { @@ -83,5 +84,5 @@ namespace mobject { -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/interpolator.cpp b/src/proc/mobject/interpolator.cpp index 026111b53..ca061f37e 100644 --- a/src/proc/mobject/interpolator.cpp +++ b/src/proc/mobject/interpolator.cpp @@ -23,11 +23,11 @@ #include "proc/mobject/interpolator.hpp" -namespace mobject - { - +namespace proc { +namespace mobject { + /** */ - - - -} // namespace mobject + + + +}} // namespace proc::mobject diff --git a/src/proc/mobject/interpolator.hpp b/src/proc/mobject/interpolator.hpp index 2244d7e58..ec70070f5 100644 --- a/src/proc/mobject/interpolator.hpp +++ b/src/proc/mobject/interpolator.hpp @@ -26,10 +26,10 @@ -namespace mobject - { - - +namespace proc { +namespace mobject { + + /** * Provides the implementation for getting the acutal value * of a time varying or automated effect/plugin parameter @@ -39,8 +39,8 @@ namespace mobject { /////////////// }; - - - -} // namespace mobject + + + +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/mobject-ref.cpp b/src/proc/mobject/mobject-ref.cpp index 4eb98dcfc..5c6197da1 100644 --- a/src/proc/mobject/mobject-ref.cpp +++ b/src/proc/mobject/mobject-ref.cpp @@ -38,9 +38,8 @@ #include "proc/mobject/mobject.hpp" #include "proc/mobject/placement.hpp" -//#include <boost/format.hpp> -//using boost::str; +namespace proc { namespace mobject { /** */ @@ -54,4 +53,4 @@ namespace mobject { -} // namespace mobject +}} // namespace proc::mobject diff --git a/src/proc/mobject/mobject-ref.hpp b/src/proc/mobject/mobject-ref.hpp index 4e8adaeb6..03f0f4e85 100644 --- a/src/proc/mobject/mobject-ref.hpp +++ b/src/proc/mobject/mobject-ref.hpp @@ -69,6 +69,7 @@ ///////////////////////////////////////////TODO: define an C-API representation here, make the header multilingual! +namespace proc { namespace mobject { @@ -105,8 +106,8 @@ namespace mobject { operator-> () const { if (!smPtr_) - throw lumiera::error::State("Lifecycle error: MObject ref not activated" - ,LUMIERA_ERROR_BOTTOM_MOBJECTREF); + throw error::State("Lifecycle error: MObject ref not activated" + ,LUMIERA_ERROR_BOTTOM_MOBJECTREF); ENSURE (INSTANCEOF (MO, smPtr_.get())); return smPtr_.operator-> (); @@ -116,8 +117,8 @@ namespace mobject { Placement<MO>& getPlacement() const { if (!isValid()) - throw lumiera::error::State("Accessing inactive MObject ref" - ,LUMIERA_ERROR_BOTTOM_MOBJECTREF); + throw error::State("Accessing inactive MObject ref" + ,LUMIERA_ERROR_BOTTOM_MOBJECTREF); ENSURE (INSTANCEOF (MO, smPtr_.get())); return *pRef_; @@ -163,8 +164,8 @@ namespace mobject { attach (Placement<MOX> const& newPlacement) { if (!isValid()) - throw lumiera::error::State("Attempt to attach a child to an inactive MObject ref" - , LUMIERA_ERROR_BOTTOM_MOBJECTREF); + throw error::State("Attempt to attach a child to an inactive MObject ref" + , LUMIERA_ERROR_BOTTOM_MOBJECTREF); MORef<MOX> newInstance; PlacementMO::ID thisScope = pRef_; return newInstance.activate ( @@ -388,5 +389,5 @@ namespace mobject { } -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/mobject.cpp b/src/proc/mobject/mobject.cpp index f844510fa..1a563391d 100644 --- a/src/proc/mobject/mobject.cpp +++ b/src/proc/mobject/mobject.cpp @@ -29,6 +29,7 @@ using lib::time::Time; using util::isnil; +namespace proc { namespace mobject { using ::NOBUG_FLAG(memory); @@ -60,4 +61,4 @@ namespace mobject { -} // namespace mobject +}} // namespace mobject diff --git a/src/proc/mobject/mobject.hpp b/src/proc/mobject/mobject.hpp index 78a98fde7..c860c52a0 100644 --- a/src/proc/mobject/mobject.hpp +++ b/src/proc/mobject/mobject.hpp @@ -28,6 +28,7 @@ #include "proc/mobject/builder/buildertool.hpp" #include "proc/mobject/placement.hpp" #include "proc/asset.hpp" //TODO finally not needed? +#include "lib/p.hpp" #include "lib/time/timevalue.hpp" #include <boost/noncopyable.hpp> @@ -41,10 +42,11 @@ using proc_interface::IDA; //TODO finally not needed? //using proc_interface::PAsset; //TODO: only temporarily using proc_interface::AssetManager; +namespace proc { namespace mobject { using std::string; - using lumiera::P; + using lib::P; //NOBUG_DECLARE_FLAG (mobjectmem); @@ -105,5 +107,5 @@ namespace mobject { typedef Placement<MObject> PMO; -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/model-port.hpp b/src/proc/mobject/model-port.hpp index 1ff5a0b3b..995d9dd45 100644 --- a/src/proc/mobject/model-port.hpp +++ b/src/proc/mobject/model-port.hpp @@ -68,8 +68,9 @@ #include "proc/asset/pipe.hpp" #include "lib/bool-checkable.hpp" -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" +namespace proc { namespace mobject { LUMIERA_ERROR_DECLARE (INVALID_MODEL_PORT); ///< Referral to unknown model port @@ -77,7 +78,6 @@ namespace mobject { using asset::ID; - using lumiera::StreamType; /** @@ -148,5 +148,5 @@ namespace mobject { -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/output-designation.cpp b/src/proc/mobject/output-designation.cpp index 3d628150b..aecd3c1fb 100644 --- a/src/proc/mobject/output-designation.cpp +++ b/src/proc/mobject/output-designation.cpp @@ -54,6 +54,7 @@ using lumiera::query::extractID; using lumiera::ConfigRules; using lumiera::Symbol; +namespace proc { namespace mobject { typedef OutputDesignation::PPipe PPipe; @@ -193,4 +194,4 @@ namespace mobject { -} // namespace mobject +}} // namespace mobject diff --git a/src/proc/mobject/output-designation.hpp b/src/proc/mobject/output-designation.hpp index bfd47b8a5..690e2a353 100644 --- a/src/proc/mobject/output-designation.hpp +++ b/src/proc/mobject/output-designation.hpp @@ -32,6 +32,7 @@ extern "C" { #include "lib/luid.h" } +namespace proc { namespace mobject { namespace mp = lumiera::typelist; @@ -116,5 +117,5 @@ namespace mobject { -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/output-mapping.hpp b/src/proc/mobject/output-mapping.hpp index 182c007e1..7d40ffdc3 100644 --- a/src/proc/mobject/output-mapping.hpp +++ b/src/proc/mobject/output-mapping.hpp @@ -63,6 +63,7 @@ +namespace proc { namespace mobject { namespace { // Helper to extract and rebind definition types @@ -400,5 +401,5 @@ namespace mobject { } -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/parameter.cpp b/src/proc/mobject/parameter.cpp index 854634f49..5a00644ce 100644 --- a/src/proc/mobject/parameter.cpp +++ b/src/proc/mobject/parameter.cpp @@ -25,6 +25,7 @@ #include "proc/mobject/parameter.hpp" #include "proc/mobject/paramprovider.hpp" +namespace proc { namespace mobject { @@ -37,4 +38,4 @@ namespace mobject { -} // namespace mobject +}} // namespace mobject diff --git a/src/proc/mobject/parameter.hpp b/src/proc/mobject/parameter.hpp index 7a0a852d5..fa256d0f6 100644 --- a/src/proc/mobject/parameter.hpp +++ b/src/proc/mobject/parameter.hpp @@ -27,6 +27,7 @@ +namespace proc { namespace mobject { template<class VAL> class ParamProvider; @@ -50,5 +51,5 @@ namespace mobject { -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/paramprovider.cpp b/src/proc/mobject/paramprovider.cpp index e0fae70a2..9bdeeb452 100644 --- a/src/proc/mobject/paramprovider.cpp +++ b/src/proc/mobject/paramprovider.cpp @@ -25,8 +25,8 @@ #include "proc/mobject/parameter.hpp" #include "proc/mobject/interpolator.hpp" -namespace mobject - { +namespace proc { +namespace mobject { template<class VAL> @@ -36,4 +36,4 @@ namespace mobject -} // namespace mobject +}} // namespace proc::mobject diff --git a/src/proc/mobject/paramprovider.hpp b/src/proc/mobject/paramprovider.hpp index cbf44a779..3be0b2206 100644 --- a/src/proc/mobject/paramprovider.hpp +++ b/src/proc/mobject/paramprovider.hpp @@ -26,8 +26,8 @@ -namespace mobject - { +namespace proc { +namespace mobject { template<class VAL> class Parameter; template<class VAL> class Interpolator; @@ -51,5 +51,5 @@ namespace mobject -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/placement-ref.hpp b/src/proc/mobject/placement-ref.hpp index 338f4ae60..0887296de 100644 --- a/src/proc/mobject/placement-ref.hpp +++ b/src/proc/mobject/placement-ref.hpp @@ -73,7 +73,11 @@ -namespace mobject { +namespace proc { + namespace error = ::lumiera::error; + + namespace mobject { + class MObject; @@ -209,7 +213,7 @@ namespace mobject { { return access(id_).isValid(); } - catch (lumiera::error::Invalid&) {} + catch (error::Invalid&) {} return false; } @@ -262,15 +266,15 @@ namespace mobject { access (_Id const& placementID) { if (!placementID) - throw lumiera::error::Logic ("Attempt to access a NIL PlacementRef" - ,LUMIERA_ERROR_BOTTOM_PLACEMENTREF); + throw error::Logic ("Attempt to access a NIL PlacementRef" + ,LUMIERA_ERROR_BOTTOM_PLACEMENTREF); Placement<MObject> & genericPlacement (session::SessionServiceFetch::resolveID (placementID)); // may throw REQUIRE (genericPlacement.isValid()); if (!(genericPlacement.template isCompatible<MX>())) - throw lumiera::error::Invalid("actual type of the resolved placement is incompatible" - , LUMIERA_ERROR_INVALID_PLACEMENTREF); + throw error::Invalid("actual type of the resolved placement is incompatible" + , LUMIERA_ERROR_INVALID_PLACEMENTREF); ////////////////////////TODO: 1. better message, including type? ////////////////////////TODO: 2. define a separate error-ID for the type mismatch! @@ -284,5 +288,5 @@ namespace mobject { -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/placement.cpp b/src/proc/mobject/placement.cpp index e0260ef13..6d9e730a1 100644 --- a/src/proc/mobject/placement.cpp +++ b/src/proc/mobject/placement.cpp @@ -31,6 +31,7 @@ using boost::format; using boost::str; +namespace proc { namespace mobject { @@ -81,4 +82,4 @@ namespace mobject { -} // namespace mobject +}} // namespace proc::mobject diff --git a/src/proc/mobject/placement.hpp b/src/proc/mobject/placement.hpp index 52d4bf805..641f9746f 100644 --- a/src/proc/mobject/placement.hpp +++ b/src/proc/mobject/placement.hpp @@ -77,6 +77,7 @@ #include <tr1/memory> +namespace proc { namespace mobject { namespace session{ class MObjectFactory; } @@ -283,5 +284,5 @@ namespace mobject { -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/session.hpp b/src/proc/mobject/session.hpp index 22dd59bdf..5b5c90d70 100644 --- a/src/proc/mobject/session.hpp +++ b/src/proc/mobject/session.hpp @@ -62,9 +62,10 @@ +namespace proc { namespace asset { - class Timeline; typedef lumiera::P<Timeline> PTimeline; - class Sequence; typedef lumiera::P<Sequence> PSequence; + class Timeline; typedef P<Timeline> PTimeline; + class Sequence; typedef P<Sequence> PSequence; } namespace mobject { @@ -205,5 +206,5 @@ namespace mobject { } // namespace mobject::session -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/session/abstractmo.cpp b/src/proc/mobject/session/abstractmo.cpp index aba3d7b2f..454a9e8e9 100644 --- a/src/proc/mobject/session/abstractmo.cpp +++ b/src/proc/mobject/session/abstractmo.cpp @@ -26,9 +26,10 @@ #include <boost/format.hpp> -using boost::format; using util::isnil; +using boost::format; +namespace proc { namespace mobject { namespace session { @@ -58,4 +59,4 @@ namespace session { } -}} // namespace mobject::session +}}} // namespace mobject::session diff --git a/src/proc/mobject/session/abstractmo.hpp b/src/proc/mobject/session/abstractmo.hpp index 1140f3c39..a06e8d4ca 100644 --- a/src/proc/mobject/session/abstractmo.hpp +++ b/src/proc/mobject/session/abstractmo.hpp @@ -29,9 +29,12 @@ +namespace proc { namespace mobject { namespace session { + namespace error = ::lumiera::error; + /** * abstract base class of all MObjects for providing common services. @@ -67,8 +70,8 @@ namespace session { throwIfInvalid() const { if (!isValid()) - throw lumiera::error::Fatal ("Invalid MObject in model. Indicates a race " - "or similarly broken internal assumptions."); + throw error::Fatal ("Invalid MObject in model. Indicates a race " + "or similarly broken internal assumptions."); } string buildShortID (lib::Literal typeID, string suffix ="") const; @@ -76,5 +79,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/allocation.cpp b/src/proc/mobject/session/allocation.cpp index 03cbfd88d..5a8b50e61 100644 --- a/src/proc/mobject/session/allocation.cpp +++ b/src/proc/mobject/session/allocation.cpp @@ -23,22 +23,19 @@ #include "proc/mobject/session/allocation.hpp" -namespace mobject +namespace proc { +namespace mobject { +namespace session { + + /** @todo probably a placeholder and to be pushed down....*/ + void + Allocation::intersect (LocatingSolution& solution) const { - namespace session - { - - /** @todo probably a placeholder and to be pushed down....*/ - void - Allocation::intersect (LocatingSolution& solution) const - { - LocatingPin::intersect (solution); - - TODO ("work out how the Allocation types solve for the position..."); - } - - - - } // namespace mobject::session - -} // namespace mobject + LocatingPin::intersect (solution); + + TODO ("work out how the Allocation types solve for the position..."); + } + + + +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/allocation.hpp b/src/proc/mobject/session/allocation.hpp index a1a668b9d..03f3d302d 100644 --- a/src/proc/mobject/session/allocation.hpp +++ b/src/proc/mobject/session/allocation.hpp @@ -21,8 +21,8 @@ */ -#ifndef MOBJECT_SESSION_ALLOCATION_H -#define MOBJECT_SESSION_ALLOCATION_H +#ifndef PROC_MOBJECT_SESSION_ALLOCATION_H +#define PROC_MOBJECT_SESSION_ALLOCATION_H #include <string> @@ -32,35 +32,32 @@ using std::string; -namespace mobject - { - namespace session +namespace proc { +namespace mobject { +namespace session { + + + /** + * Interface (abstract): any objective, constraint or wish + * of placing a MObject in a specific way. + */ + class Allocation : public LocatingPin { - - - /** - * Interface (abstract): any objective, constraint or wish - * of placing a MObject in a specific way. - */ - class Allocation : public LocatingPin - { - protected: - /** human readable representation of the condition - * characterizing this allocaton, e.g. "t >= 10" - */ - string repr; - - virtual void intersect (LocatingSolution&) const; - - public: - const string& getRepr () const { return repr; } - - virtual Allocation* clone () const = 0; - }; - - - - } // namespace mobject::session - -} // namespace mobject + protected: + /** human readable representation of the condition + * characterising this allocation, e.g. "t >= 10" + */ + string repr; + + virtual void intersect (LocatingSolution&) const; + + public: + const string& getRepr () const { return repr; } + + virtual Allocation* clone () const = 0; + }; + + + +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/auto.cpp b/src/proc/mobject/session/auto.cpp index b46ea5cae..54032b633 100644 --- a/src/proc/mobject/session/auto.cpp +++ b/src/proc/mobject/session/auto.cpp @@ -23,22 +23,19 @@ #include "proc/mobject/session/auto.hpp" -namespace mobject +namespace proc { +namespace mobject { +namespace session { + + + template<class VAL> + VAL + Auto<VAL>::getValue () { - namespace session - { - - - template<class VAL> - VAL - Auto<VAL>::getValue () - { - return 0; //////////////TODO - } - - - - - } // namespace mobject::session - -} // namespace mobject + return 0; //////////////TODO + } + + + + +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/auto.hpp b/src/proc/mobject/session/auto.hpp index 24b82ba9c..152baa851 100644 --- a/src/proc/mobject/session/auto.hpp +++ b/src/proc/mobject/session/auto.hpp @@ -21,42 +21,39 @@ */ -#ifndef MOBJECT_SESSION_AUTO_H -#define MOBJECT_SESSION_AUTO_H +#ifndef PROC_MOBJECT_SESSION_AUTO_H +#define PROC_MOBJECT_SESSION_AUTO_H #include "proc/mobject/session/meta.hpp" #include "proc/mobject/paramprovider.hpp" -namespace mobject - { - namespace session +namespace proc { +namespace mobject { +namespace session { + + + /** + * Automation data for some parameter + * (i.e. the parameter is a time varying function) + */ + template<class VAL> + class Auto : public Meta, public ParamProvider<VAL> { - - - /** - * Automation data for some parameter - * (i.e. the parameter is a time varying function) - */ - template<class VAL> - class Auto : public Meta, public ParamProvider<VAL> - { - string - initShortID() const - { - return buildShortID("Auto"); - } - - public: - //////////////////////////////TICKET #566 - - virtual VAL getValue () ; - }; + string + initShortID() const + { + return buildShortID("Auto"); + } + + public: + //////////////////////////////TICKET #566 + + virtual VAL getValue () ; + }; - } // namespace mobject::session - -} // namespace mobject +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/binding.cpp b/src/proc/mobject/session/binding.cpp index 9a763129a..5451f59d3 100644 --- a/src/proc/mobject/session/binding.cpp +++ b/src/proc/mobject/session/binding.cpp @@ -24,6 +24,7 @@ #include "proc/mobject/session/binding.hpp" #include "proc/asset/sequence.hpp" +namespace proc { namespace mobject { namespace session { @@ -49,4 +50,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/binding.hpp b/src/proc/mobject/session/binding.hpp index 0b790da9c..3ef96af36 100644 --- a/src/proc/mobject/session/binding.hpp +++ b/src/proc/mobject/session/binding.hpp @@ -21,17 +21,18 @@ */ -#ifndef MOBJECT_SESSION_BINDING_H -#define MOBJECT_SESSION_BINDING_H +#ifndef PROC_MOBJECT_SESSION_BINDING_H +#define PROC_MOBJECT_SESSION_BINDING_H #include "proc/mobject/session/meta.hpp" #include "proc/mobject/builder/buildertool.hpp" +namespace proc { namespace asset { class Sequence; - typedef lumiera::P<Sequence> PSequence; + typedef P<Sequence> PSequence; } @@ -71,5 +72,5 @@ namespace session { template class Placement<session::Binding, session::Meta>; typedef Placement<session::Binding, session::Meta> PBinding; -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/session/bus-mo.cpp b/src/proc/mobject/session/bus-mo.cpp index 1ea99c15c..ac06ee6c0 100644 --- a/src/proc/mobject/session/bus-mo.cpp +++ b/src/proc/mobject/session/bus-mo.cpp @@ -23,6 +23,7 @@ #include "proc/mobject/session/bus-mo.hpp" +namespace proc { namespace mobject { namespace session { @@ -47,4 +48,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/bus-mo.hpp b/src/proc/mobject/session/bus-mo.hpp index 30d5c3ae6..42509f1f6 100644 --- a/src/proc/mobject/session/bus-mo.hpp +++ b/src/proc/mobject/session/bus-mo.hpp @@ -21,13 +21,14 @@ */ -#ifndef MOBJECT_SESSION_BUS_MO_H -#define MOBJECT_SESSION_BUS_MO_H +#ifndef PROC_MOBJECT_SESSION_BUS_MO_H +#define PROC_MOBJECT_SESSION_BUS_MO_H #include "proc/mobject/session/meta.hpp" #include "proc/mobject/builder/buildertool.hpp" +namespace proc { namespace asset { class Pipe; @@ -71,5 +72,5 @@ namespace session { template class Placement<session::BusMO, session::Meta>; typedef Placement<session::BusMO, session::Meta> PBus; -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/session/clip.cpp b/src/proc/mobject/session/clip.cpp index 426cd8e57..bb5fdeb74 100644 --- a/src/proc/mobject/session/clip.cpp +++ b/src/proc/mobject/session/clip.cpp @@ -31,6 +31,7 @@ using lib::time::Mutation; using util::isnil; +namespace proc { namespace mobject { namespace session { @@ -82,5 +83,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/clip.hpp b/src/proc/mobject/session/clip.hpp index 52e134926..c6e2a3ba9 100644 --- a/src/proc/mobject/session/clip.hpp +++ b/src/proc/mobject/session/clip.hpp @@ -21,13 +21,14 @@ */ -#ifndef MOBJECT_SESSION_CLIP_H -#define MOBJECT_SESSION_CLIP_H +#ifndef PROC_MOBJECT_SESSION_CLIP_H +#define PROC_MOBJECT_SESSION_CLIP_H #include "proc/mobject/session/abstractmo.hpp" #include "lib/time/timevalue.hpp" +namespace proc { namespace asset { class Media; class Clip; @@ -101,10 +102,10 @@ namespace session { DEFINE_PROCESSABLE_BY (builder::BuilderTool); }; - + typedef Placement<Clip> PClipMO; -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/compoundclip.cpp b/src/proc/mobject/session/compoundclip.cpp deleted file mode 100644 index 6de47a052..000000000 --- a/src/proc/mobject/session/compoundclip.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - CompoundClip - compound of several clips (multichannel) - - Copyright (C) Lumiera.org - 2008, Hermann Vosseler <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -* *****************************************************/ - - -#include "proc/mobject/session/compoundclip.hpp" - -namespace mobject - { - - namespace session - { - - /** */ - - - - } // namespace mobject::session - -} // namespace mobject diff --git a/src/proc/mobject/session/compoundclip.hpp b/src/proc/mobject/session/compoundclip.hpp deleted file mode 100644 index 9e4f32f77..000000000 --- a/src/proc/mobject/session/compoundclip.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - COMPOUNDCLIP.hpp - compound of several clips (multichannel) - - Copyright (C) Lumiera.org - 2008, Hermann Vosseler <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - - -#ifndef MOBJECT_SESSION_COMPOUNDCLIP_H -#define MOBJECT_SESSION_COMPOUNDCLIP_H - -#include "proc/mobject/session/clip.hpp" - - - -namespace mobject - { - - namespace session - { - - - /** - * Clip MObject which is actually a compound of several elementary clips, - * e.g. the several streams found within multichannels media. - */ - class CompoundClip : public Clip - { - protected: - Clip* components; - - }; - - - } // namespace mobject::session - -} // namespace mobject -#endif diff --git a/src/proc/mobject/session/constraint.cpp b/src/proc/mobject/session/constraint.cpp index 4a1241cf3..1bedefbe1 100644 --- a/src/proc/mobject/session/constraint.cpp +++ b/src/proc/mobject/session/constraint.cpp @@ -23,15 +23,12 @@ #include "proc/mobject/session/constraint.hpp" -namespace mobject - { - namespace session - { - - /** */ - - - - } // namespace mobject::session - -} // namespace mobject +namespace proc { +namespace mobject { +namespace session { + + /** */ + + + +}}} // namespace mobject::session diff --git a/src/proc/mobject/session/constraint.hpp b/src/proc/mobject/session/constraint.hpp index eabddc617..827a48fc1 100644 --- a/src/proc/mobject/session/constraint.hpp +++ b/src/proc/mobject/session/constraint.hpp @@ -21,27 +21,24 @@ */ -#ifndef MOBJECT_SESSION_CONSTRAINT_H -#define MOBJECT_SESSION_CONSTRAINT_H +#ifndef PROC_MOBJECT_SESSION_CONSTRAINT_H +#define PROC_MOBJECT_SESSION_CONSTRAINT_H #include "proc/mobject/session/allocation.hpp" -namespace mobject - { - namespace session - { - - - /** - * LocatingPin representing an directive by the user - * that must not be violated - */ - class Constraint : public Allocation - {}; - - } // namespace mobject::session - -} // namespace mobject +namespace proc { +namespace mobject { +namespace session { + + + /** + * LocatingPin representing an directive by the user + * that must not be violated + */ + class Constraint : public Allocation + {}; + +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/defs-manager.cpp b/src/proc/mobject/session/defs-manager.cpp index 5dd350947..4c762d6c1 100644 --- a/src/proc/mobject/session/defs-manager.cpp +++ b/src/proc/mobject/session/defs-manager.cpp @@ -35,10 +35,10 @@ using lumiera::query::QueryHandler; using lumiera::query::LUMIERA_ERROR_CAPABILITY_QUERY; +namespace proc { namespace mobject { namespace session { - using lumiera::P; @@ -127,7 +127,7 @@ namespace session { return res; } -}} // namespace mobject::session +}}} // namespace mobject::session @@ -144,6 +144,7 @@ namespace session { #include "proc/asset/sequence.hpp" #include "proc/mobject/session/track.hpp" +namespace proc { namespace mobject { namespace session { @@ -170,4 +171,4 @@ namespace session { template bool DefsManager::forget (PPipe const&); -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/defs-manager.hpp b/src/proc/mobject/session/defs-manager.hpp index 0fbcb1ab5..fd473a625 100644 --- a/src/proc/mobject/session/defs-manager.hpp +++ b/src/proc/mobject/session/defs-manager.hpp @@ -21,8 +21,8 @@ */ -#ifndef MOBJECT_SESSION_DEFS_MANAGER_H -#define MOBJECT_SESSION_DEFS_MANAGER_H +#ifndef PROC_MOBJECT_SESSION_DEFS_MANAGER_H +#define PROC_MOBJECT_SESSION_DEFS_MANAGER_H #include "lib/p.hpp" @@ -33,11 +33,13 @@ +namespace proc { namespace mobject { namespace session { - using lumiera::P; + using lib::P; + using ::lumiera::Query; using boost::scoped_ptr; namespace impl { class DefsRegistry; } @@ -72,14 +74,14 @@ namespace session { * is considered \e misconfiguration. */ template<class TAR> - P<TAR> operator() (lumiera::Query<TAR> const&); + P<TAR> operator() (Query<TAR> const&); /** search through the registered defaults, never create anything. * @return object fulfilling the query, \c empty ptr if not found. */ template<class TAR> - P<TAR> search (lumiera::Query<TAR> const&); + P<TAR> search (Query<TAR> const&); /** retrieve an object fulfilling the query and register it as default. * The resolution is delegated to the ConfigQuery system (which may cause @@ -87,7 +89,7 @@ namespace session { * @return object fulfilling the query, \c empty ptr if no solution. */ template<class TAR> - P<TAR> create (lumiera::Query<TAR> const&); + P<TAR> create (Query<TAR> const&); /** register the given object as default, after ensuring it fulfils the * query. The latter may cause some properties of the object to be set, @@ -96,7 +98,7 @@ namespace session { * @note only a weak ref to the object is stored */ template<class TAR> - bool define (P<TAR> const&, lumiera::Query<TAR> const& =lumiera::Query<TAR>()); + bool define (P<TAR> const&, Query<TAR> const& =Query<TAR>()); /** remove the defaults registration of the given object, if there was such * @return false if nothing has been changed because the object wasn't registered @@ -124,5 +126,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/defs-registry.hpp b/src/proc/mobject/session/defs-registry.hpp index a6fc169cb..53bfa7a93 100644 --- a/src/proc/mobject/session/defs-registry.hpp +++ b/src/proc/mobject/session/defs-registry.hpp @@ -36,8 +36,8 @@ -#ifndef MOBJECT_SESSION_DEFS_REGISTRY_H -#define MOBJECT_SESSION_DEFS_REGISTRY_H +#ifndef PROC_MOBJECT_SESSION_DEFS_REGISTRY_H +#define PROC_MOBJECT_SESSION_DEFS_REGISTRY_H #include "lib/sync-classlock.hpp" @@ -54,10 +54,11 @@ #include <boost/lambda/lambda.hpp> +namespace proc { namespace mobject { namespace session { - using lumiera::P; + using lib::P; using lumiera::Query; using lib::ClassLock; using std::tr1::weak_ptr; @@ -340,5 +341,5 @@ namespace session { using impl::DefsRegistry; -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/effect.cpp b/src/proc/mobject/session/effect.cpp index d190274dd..fe78b195b 100644 --- a/src/proc/mobject/session/effect.cpp +++ b/src/proc/mobject/session/effect.cpp @@ -23,22 +23,19 @@ #include "proc/mobject/session/effect.hpp" -namespace mobject +namespace proc { +namespace mobject { +namespace session { + + /** + * @TODO: clarify asset->mobject relation and asset dependencies; Ticket #255 + */ + asset::Proc const& + Effect::getProcAsset() const { - namespace session - { - - /** - * @TODO: clarify asset->mobject relation and asset dependencies; Ticket #255 - */ - asset::Proc const& - Effect::getProcAsset() const - { - UNIMPLEMENTED ("how to access the processing asset assotiated to a given Effect-MObject"); - } - - - - } // namespace mobject::session - -} // namespace mobject + UNIMPLEMENTED ("how to access the processing asset assotiated to a given Effect-MObject"); + } + + + +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/effect.hpp b/src/proc/mobject/session/effect.hpp index 9c22e847e..c1a84b31b 100644 --- a/src/proc/mobject/session/effect.hpp +++ b/src/proc/mobject/session/effect.hpp @@ -21,8 +21,8 @@ */ -#ifndef MOBJECT_SESSION_EFFECT_H -#define MOBJECT_SESSION_EFFECT_H +#ifndef PROC_MOBJECT_SESSION_EFFECT_H +#define PROC_MOBJECT_SESSION_EFFECT_H #include "proc/mobject/session/abstractmo.hpp" @@ -33,7 +33,7 @@ using std::string; - +namespace proc { namespace mobject { namespace session { @@ -64,5 +64,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/element-query.hpp b/src/proc/mobject/session/element-query.hpp index fbb3fbe3a..d99b9669f 100644 --- a/src/proc/mobject/session/element-query.hpp +++ b/src/proc/mobject/session/element-query.hpp @@ -21,8 +21,8 @@ */ -#ifndef MOBJECT_SESSION_ELEMENT_QUERY_H -#define MOBJECT_SESSION_ELEMENT_QUERY_H +#ifndef PROC_MOBJECT_SESSION_ELEMENT_QUERY_H +#define PROC_MOBJECT_SESSION_ELEMENT_QUERY_H #include "proc/mobject/placement.hpp" @@ -35,6 +35,7 @@ +namespace proc { namespace mobject { namespace session { @@ -126,5 +127,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/fixedlocation.cpp b/src/proc/mobject/session/fixedlocation.cpp index 1cd7eb98c..59bd299b9 100644 --- a/src/proc/mobject/session/fixedlocation.cpp +++ b/src/proc/mobject/session/fixedlocation.cpp @@ -23,37 +23,35 @@ #include "proc/mobject/session/fixedlocation.hpp" -namespace mobject +namespace proc { +namespace mobject { +namespace session { + + + FixedLocation* + FixedLocation::clone () const + { + return new FixedLocation (*this); + } + + + /** */ + void + FixedLocation::intersect (LocatingSolution& solution) const { - namespace session - { - - FixedLocation* - FixedLocation::clone () const - { - return new FixedLocation (*this); - } + LocatingPin::intersect (solution); - - /** */ - void - FixedLocation::intersect (LocatingSolution& solution) const - { - LocatingPin::intersect (solution); - - TODO ("either set position or make overconstrained"); - if (solution.minTime <= this->time_) - solution.minTime = this->time_; - else - solution.impo = true; - if (solution.maxTime >= this->time_) - solution.maxTime = this->time_; - else - solution.impo = true; - } - - - - } // namespace mobject::session - -} // namespace mobject + TODO ("either set position or make overconstrained"); + if (solution.minTime <= this->time_) + solution.minTime = this->time_; + else + solution.impo = true; + if (solution.maxTime >= this->time_) + solution.maxTime = this->time_; + else + solution.impo = true; + } + + + +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/fixedlocation.hpp b/src/proc/mobject/session/fixedlocation.hpp index ab9b9cc6d..531669ded 100644 --- a/src/proc/mobject/session/fixedlocation.hpp +++ b/src/proc/mobject/session/fixedlocation.hpp @@ -21,20 +21,20 @@ */ -#ifndef MOBJECT_SESSION_FIXEDLOCATION_H -#define MOBJECT_SESSION_FIXEDLOCATION_H +#ifndef PROC_MOBJECT_SESSION_FIXEDLOCATION_H +#define PROC_MOBJECT_SESSION_FIXEDLOCATION_H #include "proc/mobject/session/locatingpin.hpp" -namespace mobject - { - class ExplicitPlacement; //TODO trac #100 +namespace proc { +namespace mobject { + + class ExplicitPlacement; //TODO trac #100 + + namespace session { - namespace session - { - /** * The most common case of positioning a MObject * in the Session: directly specifying a constant position. @@ -59,10 +59,8 @@ namespace mobject virtual FixedLocation* clone () const; }; - - - - } // namespace mobject::session - -} // namespace mobject + + + +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/fixture.cpp b/src/proc/mobject/session/fixture.cpp index 053bd3827..3f4aac79e 100644 --- a/src/proc/mobject/session/fixture.cpp +++ b/src/proc/mobject/session/fixture.cpp @@ -24,6 +24,7 @@ #include "proc/mobject/session/fixture.hpp" #include "include/logging.h" +namespace proc { namespace mobject { namespace session { @@ -54,8 +55,8 @@ namespace session { TODO ("actually do a self-verification of the Fixture"); return true; } - - - - -}} // namespace mobject::session + + + + +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/fixture.hpp b/src/proc/mobject/session/fixture.hpp index d6ecdc847..201d5a425 100644 --- a/src/proc/mobject/session/fixture.hpp +++ b/src/proc/mobject/session/fixture.hpp @@ -39,11 +39,12 @@ using std::tr1::shared_ptr; +namespace proc { namespace mobject { namespace session { - - - + + + class Fixture : boost::noncopyable { @@ -74,5 +75,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/generator-mo.cpp b/src/proc/mobject/session/generator-mo.cpp index 728e58b94..a57ff41c9 100644 --- a/src/proc/mobject/session/generator-mo.cpp +++ b/src/proc/mobject/session/generator-mo.cpp @@ -31,6 +31,7 @@ //using lib::time::Mutation; //using util::isnil; +namespace proc { namespace mobject { namespace session { @@ -41,5 +42,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace mobject::session diff --git a/src/proc/mobject/session/generator-mo.hpp b/src/proc/mobject/session/generator-mo.hpp index 876a497f3..c0c64a4f9 100644 --- a/src/proc/mobject/session/generator-mo.hpp +++ b/src/proc/mobject/session/generator-mo.hpp @@ -21,13 +21,14 @@ */ -#ifndef MOBJECT_SESSION_GENERATOR_MO_H -#define MOBJECT_SESSION_GENERATOR_MO_H +#ifndef PROC_MOBJECT_SESSION_GENERATOR_MO_H +#define PROC_MOBJECT_SESSION_GENERATOR_MO_H #include "proc/mobject/session/abstractmo.hpp" #include "lib/time/timevalue.hpp" +namespace proc { namespace asset { class Media; class Clip; @@ -87,5 +88,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/label.cpp b/src/proc/mobject/session/label.cpp index 58e3cd86f..3ffa028e6 100644 --- a/src/proc/mobject/session/label.cpp +++ b/src/proc/mobject/session/label.cpp @@ -26,6 +26,7 @@ using util::isnil; +namespace proc { namespace mobject { namespace session { @@ -39,4 +40,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/label.hpp b/src/proc/mobject/session/label.hpp index df790d74f..74c7e0bae 100644 --- a/src/proc/mobject/session/label.hpp +++ b/src/proc/mobject/session/label.hpp @@ -21,14 +21,15 @@ */ -#ifndef MOBJECT_SESSION_LABEL_H -#define MOBJECT_SESSION_LABEL_H +#ifndef PROC_MOBJECT_SESSION_LABEL_H +#define PROC_MOBJECT_SESSION_LABEL_H #include "proc/mobject/session/meta.hpp" #include "lib/symbol.hpp" +namespace proc { namespace mobject { namespace session { @@ -68,5 +69,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/lifecycle-advisor.hpp b/src/proc/mobject/session/lifecycle-advisor.hpp index 0e8364fae..7955ada11 100644 --- a/src/proc/mobject/session/lifecycle-advisor.hpp +++ b/src/proc/mobject/session/lifecycle-advisor.hpp @@ -46,8 +46,8 @@ */ -#ifndef MOBJECT_SESSION_LIFECYCLE_ADVISOR_H -#define MOBJECT_SESSION_LIFECYCLE_ADVISOR_H +#ifndef PROC_MOBJECT_SESSION_LIFECYCLE_ADVISOR_H +#define PROC_MOBJECT_SESSION_LIFECYCLE_ADVISOR_H #include "lib/error.hpp" #include "include/lifecycle.h" @@ -57,6 +57,7 @@ #include <boost/noncopyable.hpp> +namespace proc { namespace mobject { namespace session { @@ -180,5 +181,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace mobject::session #endif diff --git a/src/proc/mobject/session/locatingpin.cpp b/src/proc/mobject/session/locatingpin.cpp index 059e38290..93f6437e5 100644 --- a/src/proc/mobject/session/locatingpin.cpp +++ b/src/proc/mobject/session/locatingpin.cpp @@ -28,6 +28,7 @@ #include "proc/mobject/session/relativelocation.hpp" +namespace proc { namespace mobject { namespace session { @@ -198,4 +199,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/locatingpin.hpp b/src/proc/mobject/session/locatingpin.hpp index dc58ac944..52b0f2add 100644 --- a/src/proc/mobject/session/locatingpin.hpp +++ b/src/proc/mobject/session/locatingpin.hpp @@ -43,8 +43,8 @@ -#ifndef MOBJECT_SESSION_LOCATINGPIN_H -#define MOBJECT_SESSION_LOCATINGPIN_H +#ifndef PROC_MOBJECT_SESSION_LOCATINGPIN_H +#define PROC_MOBJECT_SESSION_LOCATINGPIN_H #include "proc/common.hpp" @@ -57,6 +57,7 @@ using boost::scoped_ptr; +namespace proc { namespace asset { class Pipe; } namespace mobject { @@ -187,7 +188,5 @@ protected: - } // namespace mobject::session - -} // namespace mobject +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/meta.cpp b/src/proc/mobject/session/meta.cpp index 88d87c443..275e585aa 100644 --- a/src/proc/mobject/session/meta.cpp +++ b/src/proc/mobject/session/meta.cpp @@ -23,15 +23,12 @@ #include "proc/mobject/session/meta.hpp" -namespace mobject - { - namespace session - { - - /** */ - - - - } // namespace mobject::session - -} // namespace mobject +namespace proc { +namespace mobject { +namespace session { + + /** */ + + + +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/meta.hpp b/src/proc/mobject/session/meta.hpp index 3015bb127..db49c9ed5 100644 --- a/src/proc/mobject/session/meta.hpp +++ b/src/proc/mobject/session/meta.hpp @@ -21,17 +21,17 @@ */ -#ifndef MOBJECT_SESSION_META_H -#define MOBJECT_SESSION_META_H +#ifndef PROC_MOBJECT_SESSION_META_H +#define PROC_MOBJECT_SESSION_META_H #include "proc/mobject/session/abstractmo.hpp" - +namespace proc { namespace mobject { namespace session { - - + + /** * Meta-MObject doesn't represent real Media Content, * but rather all sorts of Processing Instructions @@ -53,5 +53,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace mobject::session #endif diff --git a/src/proc/mobject/session/mobjectfactory.cpp b/src/proc/mobject/session/mobjectfactory.cpp index 8c47a3d8e..4890e3c3a 100644 --- a/src/proc/mobject/session/mobjectfactory.cpp +++ b/src/proc/mobject/session/mobjectfactory.cpp @@ -31,6 +31,7 @@ #include "proc/asset/clip.hpp" #include "proc/asset/effect.hpp" +namespace proc { namespace mobject { namespace session { ////////////////////////////////////////////////////////////////////////////////TICKET #414 @@ -107,4 +108,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace mobject::session diff --git a/src/proc/mobject/session/mobjectfactory.hpp b/src/proc/mobject/session/mobjectfactory.hpp index 82efd7c0c..217ddd33d 100644 --- a/src/proc/mobject/session/mobjectfactory.hpp +++ b/src/proc/mobject/session/mobjectfactory.hpp @@ -21,8 +21,8 @@ */ -#ifndef MOBJECT_SESSION_MOBJECTFACTORY_H -#define MOBJECT_SESSION_MOBJECTFACTORY_H +#ifndef PROC_MOBJECT_SESSION_MOBJECTFACTORY_H +#define PROC_MOBJECT_SESSION_MOBJECTFACTORY_H #include "proc/mobject/mobject.hpp" #include "proc/asset/entry-id.hpp" @@ -30,6 +30,7 @@ +namespace proc { namespace asset { class Clip; @@ -76,5 +77,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/placement-index-query-resolver.cpp b/src/proc/mobject/session/placement-index-query-resolver.cpp index 35fa5ecdc..4eda27cf0 100644 --- a/src/proc/mobject/session/placement-index-query-resolver.cpp +++ b/src/proc/mobject/session/placement-index-query-resolver.cpp @@ -33,6 +33,7 @@ #include <stack> +namespace proc { namespace mobject { namespace session { @@ -396,4 +397,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/placement-index-query-resolver.hpp b/src/proc/mobject/session/placement-index-query-resolver.hpp index bac26d96d..b451e7c7e 100644 --- a/src/proc/mobject/session/placement-index-query-resolver.hpp +++ b/src/proc/mobject/session/placement-index-query-resolver.hpp @@ -63,6 +63,7 @@ #include <tr1/functional> +namespace proc { namespace mobject { namespace session { @@ -113,5 +114,5 @@ namespace session { }; -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/placement-index.cpp b/src/proc/mobject/session/placement-index.cpp index fab3adda6..8554674dd 100644 --- a/src/proc/mobject/session/placement-index.cpp +++ b/src/proc/mobject/session/placement-index.cpp @@ -68,6 +68,7 @@ #include <string> +namespace proc { namespace mobject { namespace session { @@ -728,4 +729,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/placement-index.hpp b/src/proc/mobject/session/placement-index.hpp index 9dbc283ae..2d0fbea69 100644 --- a/src/proc/mobject/session/placement-index.hpp +++ b/src/proc/mobject/session/placement-index.hpp @@ -102,8 +102,8 @@ -#ifndef MOBJECT_PLACEMENT_INDEX_H -#define MOBJECT_PLACEMENT_INDEX_H +#ifndef PROC_MOBJECT_PLACEMENT_INDEX_H +#define PROC_MOBJECT_PLACEMENT_INDEX_H #include "lib/util.hpp" #include "lib/error.hpp" @@ -117,6 +117,7 @@ #include <vector> +namespace proc { namespace mobject { class MObject; @@ -326,5 +327,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/plug.cpp b/src/proc/mobject/session/plug.cpp index d642ed1e3..0e449c9fc 100644 --- a/src/proc/mobject/session/plug.cpp +++ b/src/proc/mobject/session/plug.cpp @@ -24,15 +24,12 @@ #include "proc/mobject/session/plug.hpp" #include "proc/asset/pipe.hpp" -namespace mobject - { - namespace session - { - - /** */ - - - - } // namespace mobject::session - -} // namespace mobject +namespace proc { +namespace mobject { +namespace session { + + /** */ + + + +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/plug.hpp b/src/proc/mobject/session/plug.hpp index fb0835c55..315a582c0 100644 --- a/src/proc/mobject/session/plug.hpp +++ b/src/proc/mobject/session/plug.hpp @@ -28,25 +28,22 @@ +namespace proc { namespace asset { class Pipe; } -namespace mobject - { - namespace session +namespace mobject { +namespace session { + + /** + * LocatingPin for requesting connection to some Pipe + */ + class Plug : public Wish { - - /** - * LocatingPin for requesting connection to some Pipe - */ - class Plug : public Wish - { - protected: - /** the Pipe this MObject wants to be conected to */ - asset::Pipe* outPipe; ////////////////////////////////TODO: shared_ptr - - }; - - } // namespace mobject::session - -} // namespace mobject + protected: + /** the Pipe this MObject wants to be conected to */ + asset::Pipe* outPipe; ////////////////////////////////TODO: shared_ptr + + }; + +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/query-focus-stack.hpp b/src/proc/mobject/session/query-focus-stack.hpp index 42dedc962..e6c2e79c9 100644 --- a/src/proc/mobject/session/query-focus-stack.hpp +++ b/src/proc/mobject/session/query-focus-stack.hpp @@ -32,6 +32,7 @@ using std::list; +namespace proc { namespace mobject { namespace session { @@ -192,5 +193,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace mobject::session #endif diff --git a/src/proc/mobject/session/query-focus.cpp b/src/proc/mobject/session/query-focus.cpp index 2972d2f35..5ab530589 100644 --- a/src/proc/mobject/session/query-focus.cpp +++ b/src/proc/mobject/session/query-focus.cpp @@ -30,6 +30,7 @@ using boost::format; using boost::str; +namespace proc { namespace mobject { namespace session { @@ -170,4 +171,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/query-focus.hpp b/src/proc/mobject/session/query-focus.hpp index 9fc74decc..e8fcfbc2a 100644 --- a/src/proc/mobject/session/query-focus.hpp +++ b/src/proc/mobject/session/query-focus.hpp @@ -32,6 +32,7 @@ #include <boost/intrusive_ptr.hpp> #include <string> +namespace proc { namespace mobject { namespace session { @@ -194,5 +195,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/query-resolver.cpp b/src/proc/mobject/session/query-resolver.cpp index 787c58f6d..eeeef53ad 100644 --- a/src/proc/mobject/session/query-resolver.cpp +++ b/src/proc/mobject/session/query-resolver.cpp @@ -24,6 +24,7 @@ #include "proc/mobject/session/query-resolver.hpp" #include "lib/multifact-arg.hpp" +namespace proc { namespace mobject { namespace session { @@ -122,4 +123,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/query-resolver.hpp b/src/proc/mobject/session/query-resolver.hpp index eedd6811a..18908d760 100644 --- a/src/proc/mobject/session/query-resolver.hpp +++ b/src/proc/mobject/session/query-resolver.hpp @@ -41,6 +41,7 @@ //using std::vector; //using std::string; +namespace proc { namespace mobject { namespace session { @@ -335,5 +336,5 @@ namespace session { } -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/relativelocation.cpp b/src/proc/mobject/session/relativelocation.cpp index 557d3b66d..1d40b67ca 100644 --- a/src/proc/mobject/session/relativelocation.cpp +++ b/src/proc/mobject/session/relativelocation.cpp @@ -24,27 +24,24 @@ #include "proc/mobject/session/relativelocation.hpp" #include "proc/mobject/mobject.hpp" -namespace mobject +namespace proc { +namespace mobject { +namespace session { + + RelativeLocation* + RelativeLocation::clone () const + { + return new RelativeLocation (*this); + } + + + void + RelativeLocation::intersect (LocatingSolution& solution) const { - namespace session - { - - RelativeLocation* - RelativeLocation::clone () const - { - return new RelativeLocation (*this); - } + LocatingPin::intersect (solution); - - void - RelativeLocation::intersect (LocatingSolution& solution) const - { - LocatingPin::intersect (solution); - - TODO ("either set position or make overconstrained"); - } - - - } // namespace mobject::session - -} // namespace mobject + TODO ("either set position or make overconstrained"); + } + + +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/relativelocation.hpp b/src/proc/mobject/session/relativelocation.hpp index bea3e134b..09eb45bfe 100644 --- a/src/proc/mobject/session/relativelocation.hpp +++ b/src/proc/mobject/session/relativelocation.hpp @@ -30,6 +30,7 @@ +namespace proc { namespace mobject { namespace session { @@ -79,5 +80,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/root.cpp b/src/proc/mobject/session/root.cpp index 5b6aa6290..32ac83494 100644 --- a/src/proc/mobject/session/root.cpp +++ b/src/proc/mobject/session/root.cpp @@ -25,6 +25,7 @@ #include "proc/mobject/session/defs-manager.hpp" +namespace proc { namespace mobject { namespace session { @@ -52,4 +53,4 @@ namespace session { /////////////////////////////////TODO more to come..... -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/root.hpp b/src/proc/mobject/session/root.hpp index c8dc18631..2169b9334 100644 --- a/src/proc/mobject/session/root.hpp +++ b/src/proc/mobject/session/root.hpp @@ -21,13 +21,14 @@ */ -#ifndef MOBJECT_SESSION_ROOT_H -#define MOBJECT_SESSION_ROOT_H +#ifndef PROC_MOBJECT_SESSION_ROOT_H +#define PROC_MOBJECT_SESSION_ROOT_H #include "proc/mobject/session/meta.hpp" #include "proc/mobject/builder/buildertool.hpp" +namespace proc { namespace mobject { namespace session { @@ -50,10 +51,10 @@ namespace session { class Root : public Meta { DefsManager& defaults_; - + ///////////TODO: timespan fields here or already in class Meta?? ///////////TODO: any idea about the purpose of root's "timespan"?? ///////TICKET #448 - + string initShortID() const { @@ -76,5 +77,5 @@ namespace session { template class Placement<session::Root, session::Meta>; typedef Placement<session::Root, session::Meta> PRoot; -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/session/scope-locator.hpp b/src/proc/mobject/session/scope-locator.hpp index a3c427722..183b8ebd4 100644 --- a/src/proc/mobject/session/scope-locator.hpp +++ b/src/proc/mobject/session/scope-locator.hpp @@ -21,8 +21,8 @@ */ -#ifndef MOBJECT_SESSION_SCOPE_LOCATOR_H -#define MOBJECT_SESSION_SCOPE_LOCATOR_H +#ifndef PROC_MOBJECT_SESSION_SCOPE_LOCATOR_H +#define PROC_MOBJECT_SESSION_SCOPE_LOCATOR_H #include "proc/mobject/session/scope.hpp" #include "proc/mobject/session/scope-query.hpp" @@ -33,6 +33,7 @@ #include <boost/scoped_ptr.hpp> +namespace proc { namespace mobject { namespace session { @@ -147,5 +148,5 @@ namespace session { } -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/scope-path.cpp b/src/proc/mobject/session/scope-path.cpp index 5c750d920..44ec04c2d 100644 --- a/src/proc/mobject/session/scope-path.cpp +++ b/src/proc/mobject/session/scope-path.cpp @@ -33,6 +33,7 @@ #include <algorithm> +namespace proc { namespace mobject { namespace session { @@ -340,4 +341,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace mobject::session diff --git a/src/proc/mobject/session/scope-path.hpp b/src/proc/mobject/session/scope-path.hpp index da23516d8..5546037f5 100644 --- a/src/proc/mobject/session/scope-path.hpp +++ b/src/proc/mobject/session/scope-path.hpp @@ -84,8 +84,8 @@ */ -#ifndef MOBJECT_SESSION_SCOPE_PATH_H -#define MOBJECT_SESSION_SCOPE_PATH_H +#ifndef PROC_MOBJECT_SESSION_SCOPE_PATH_H +#define PROC_MOBJECT_SESSION_SCOPE_PATH_H #include "proc/mobject/session/scope.hpp" #include "lib/bool-checkable.hpp" @@ -99,7 +99,7 @@ namespace lib { namespace iter{ - using mobject::session::Scope; + using proc::mobject::session::Scope; /** * this explicit specialisation allows to build a RangeIter @@ -118,6 +118,7 @@ namespace iter{ }; }} +namespace proc { namespace mobject { namespace session { @@ -297,5 +298,5 @@ namespace session { } -}} // namespace mobject::session +}}} // namespace mobject::session #endif diff --git a/src/proc/mobject/session/scope-query.hpp b/src/proc/mobject/session/scope-query.hpp index 166e2615a..1c153ba48 100644 --- a/src/proc/mobject/session/scope-query.hpp +++ b/src/proc/mobject/session/scope-query.hpp @@ -38,8 +38,8 @@ -#ifndef MOBJECT_SESSION_SCOPE_QUERY_H -#define MOBJECT_SESSION_SCOPE_QUERY_H +#ifndef PROC_MOBJECT_SESSION_SCOPE_QUERY_H +#define PROC_MOBJECT_SESSION_SCOPE_QUERY_H #include "proc/mobject/placement.hpp" @@ -48,6 +48,7 @@ #include <tr1/functional> +namespace proc { namespace mobject { namespace session { @@ -211,5 +212,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/scope.cpp b/src/proc/mobject/session/scope.cpp index 24b684a03..6b27c9a86 100644 --- a/src/proc/mobject/session/scope.cpp +++ b/src/proc/mobject/session/scope.cpp @@ -52,6 +52,7 @@ using std::vector; using lib::IterSource; using lib::iter_source::wrapIter; +namespace proc { namespace mobject { namespace session { @@ -253,4 +254,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/scope.hpp b/src/proc/mobject/session/scope.hpp index d3b463dc2..20651ceb2 100644 --- a/src/proc/mobject/session/scope.hpp +++ b/src/proc/mobject/session/scope.hpp @@ -21,8 +21,8 @@ */ -#ifndef MOBJECT_SESSION_SCOPE_H -#define MOBJECT_SESSION_SCOPE_H +#ifndef PROC_MOBJECT_SESSION_SCOPE_H +#define PROC_MOBJECT_SESSION_SCOPE_H #include "proc/mobject/placement.hpp" #include "proc/mobject/placement-ref.hpp" @@ -31,6 +31,7 @@ #include <string> +namespace proc { namespace mobject { namespace session { @@ -116,5 +117,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/segment.cpp b/src/proc/mobject/session/segment.cpp index 07320761d..87977a008 100644 --- a/src/proc/mobject/session/segment.cpp +++ b/src/proc/mobject/session/segment.cpp @@ -25,13 +25,12 @@ +namespace proc { namespace mobject { - namespace session { - - /** */ - - - - } // namespace mobject::session - -} // namespace mobject +namespace session { + + /** */ + + + +} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/segment.hpp b/src/proc/mobject/session/segment.hpp index 9c39533e2..900cb76b0 100644 --- a/src/proc/mobject/session/segment.hpp +++ b/src/proc/mobject/session/segment.hpp @@ -30,38 +30,35 @@ #include "proc/mobject/explicitplacement.hpp" #include "lib/time/timevalue.hpp" - using std::list; - +namespace proc { namespace mobject { - namespace session { - - - /** - * For the purpose of building and rendering, the fixture (for each timeline) - * is partitioned such that each segment is <i>structurally constant</i>. - * For each segment there is a RenderGraph (unit of the render engine) which - * is able to render all ExitNodes for this segment. - */ - class Segment - { - protected: - typedef lib::time::TimeSpan Span; - - /** begin of this timeline segment. */ - Span span_; - - /** relevant MObjects comprising this segment. */ - list<ExplicitPlacement> elements; - // TODO: actually necessary?? - // TODO: ownership?? - - }; - - - - } // namespace mobject::session - -} // namespace mobject +namespace session { + + + /** + * For the purpose of building and rendering, the fixture (for each timeline) + * is partitioned such that each segment is <i>structurally constant</i>. + * For each segment there is a RenderGraph (unit of the render engine) which + * is able to render all ExitNodes for this segment. + */ + class Segment + { + protected: + typedef lib::time::TimeSpan Span; + + /** begin of this timeline segment. */ + Span span_; + + /** relevant MObjects comprising this segment. */ + list<ExplicitPlacement> elements; + // TODO: actually necessary?? + // TODO: ownership?? + + }; + + + +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/segmentation.cpp b/src/proc/mobject/session/segmentation.cpp index b8c8fd660..16bb9c1e4 100644 --- a/src/proc/mobject/session/segmentation.cpp +++ b/src/proc/mobject/session/segmentation.cpp @@ -24,13 +24,12 @@ +namespace proc { namespace mobject { - namespace session { - - /** */ - - - - } // namespace mobject::session - -} // namespace mobject +namespace session { + + /** */ + + + +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/segmentation.hpp b/src/proc/mobject/session/segmentation.hpp index 4312a6205..75238b53d 100644 --- a/src/proc/mobject/session/segmentation.hpp +++ b/src/proc/mobject/session/segmentation.hpp @@ -32,29 +32,28 @@ using std::list; +namespace proc { namespace mobject { - namespace session { - - - /** - * For the purpose of building and rendering, the fixture (for each timeline) - * is partitioned such that each segment is <i>structurally constant</i>. - * The Segmentation defines and maintains this partitioning. Further, - * it is the general entry point for accessing the correct part of the engine - * responsible for a given timeline time point. - * @see SegmentationTool actually calculating the Segmentation - */ - class Segmentation - { - - /** segments of the engine in ordered sequence. */ - list<Segment> segments_; - - }; - - - - } // namespace mobject::session - -} // namespace mobject +namespace session { + + + /** + * For the purpose of building and rendering, the fixture (for each timeline) + * is partitioned such that each segment is <i>structurally constant</i>. + * The Segmentation defines and maintains this partitioning. Further, + * it is the general entry point for accessing the correct part of the engine + * responsible for a given timeline time point. + * @see SegmentationTool actually calculating the Segmentation + */ + class Segmentation + { + + /** segments of the engine in ordered sequence. */ + list<Segment> segments_; + + }; + + + +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/sess-manager-impl.cpp b/src/proc/mobject/session/sess-manager-impl.cpp index 9ce3c61ec..6999a2cb5 100644 --- a/src/proc/mobject/session/sess-manager-impl.cpp +++ b/src/proc/mobject/session/sess-manager-impl.cpp @@ -51,6 +51,7 @@ using boost::scoped_ptr; +namespace proc { namespace mobject { namespace session { @@ -286,4 +287,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/sess-manager-impl.hpp b/src/proc/mobject/session/sess-manager-impl.hpp index 403f835c8..dc340af39 100644 --- a/src/proc/mobject/session/sess-manager-impl.hpp +++ b/src/proc/mobject/session/sess-manager-impl.hpp @@ -28,6 +28,7 @@ #include "lib/sync.hpp" +namespace proc { namespace mobject { namespace session { @@ -69,5 +70,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/session-impl.cpp b/src/proc/mobject/session/session-impl.cpp index e091414b2..4c44e06e6 100644 --- a/src/proc/mobject/session/session-impl.cpp +++ b/src/proc/mobject/session/session-impl.cpp @@ -30,6 +30,7 @@ using namespace lumiera; +namespace proc { namespace mobject { namespace session { @@ -149,4 +150,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/session-impl.hpp b/src/proc/mobject/session/session-impl.hpp index b5416057e..4a24fadbd 100644 --- a/src/proc/mobject/session/session-impl.hpp +++ b/src/proc/mobject/session/session-impl.hpp @@ -43,8 +43,8 @@ */ -#ifndef MOBJECT_SESSION_SESSIONIMPL_H -#define MOBJECT_SESSION_SESSIONIMPL_H +#ifndef PROC_MOBJECT_SESSION_SESSIONIMPL_H +#define PROC_MOBJECT_SESSION_SESSIONIMPL_H #include "proc/mobject/session.hpp" #include "proc/mobject/session/fixture.hpp" @@ -63,6 +63,7 @@ +namespace proc { namespace mobject { namespace session { @@ -289,5 +290,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/session-interface-modules.hpp b/src/proc/mobject/session/session-interface-modules.hpp index daa05b18f..598095f71 100644 --- a/src/proc/mobject/session/session-interface-modules.hpp +++ b/src/proc/mobject/session/session-interface-modules.hpp @@ -67,8 +67,8 @@ */ -#ifndef MOBJECT_SESSION_INTERFACE_MODULES_H -#define MOBJECT_SESSION_INTERFACE_MODULES_H +#ifndef PROC_MOBJECT_SESSION_INTERFACE_MODULES_H +#define PROC_MOBJECT_SESSION_INTERFACE_MODULES_H #include "lib/element-tracker.hpp" #include "proc/asset/timeline.hpp" @@ -79,6 +79,7 @@ +namespace proc { namespace mobject { namespace session { @@ -129,5 +130,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/session-query.hpp b/src/proc/mobject/session/session-query.hpp index 15cf82d69..e52d06472 100644 --- a/src/proc/mobject/session/session-query.hpp +++ b/src/proc/mobject/session/session-query.hpp @@ -48,6 +48,7 @@ +namespace proc { namespace mobject { namespace session { @@ -79,5 +80,5 @@ namespace session { return bind (_compareTrackID, _1, trackID); } -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/session-service-defaults.hpp b/src/proc/mobject/session/session-service-defaults.hpp index 70cbd1b4a..80457864e 100644 --- a/src/proc/mobject/session/session-service-defaults.hpp +++ b/src/proc/mobject/session/session-service-defaults.hpp @@ -41,6 +41,7 @@ +namespace proc { namespace mobject { namespace session { @@ -55,5 +56,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/session-service-explore-scope.hpp b/src/proc/mobject/session/session-service-explore-scope.hpp index e3330fe27..10d483fba 100644 --- a/src/proc/mobject/session/session-service-explore-scope.hpp +++ b/src/proc/mobject/session/session-service-explore-scope.hpp @@ -52,11 +52,12 @@ +namespace proc { namespace mobject { namespace session { - + /** * Implementation-level service for issuing contents/discovery queries. * Actually, the implementation of this service is backed by the PlacementIndex @@ -76,5 +77,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/session-service-fetch.hpp b/src/proc/mobject/session/session-service-fetch.hpp index fd883dffb..93d6cb7c2 100644 --- a/src/proc/mobject/session/session-service-fetch.hpp +++ b/src/proc/mobject/session/session-service-fetch.hpp @@ -44,6 +44,7 @@ +namespace proc { namespace mobject { namespace session { @@ -69,5 +70,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/session-service-mock-index.hpp b/src/proc/mobject/session/session-service-mock-index.hpp index 44d4ab446..3c9d88d36 100644 --- a/src/proc/mobject/session/session-service-mock-index.hpp +++ b/src/proc/mobject/session/session-service-mock-index.hpp @@ -51,6 +51,7 @@ +namespace proc { namespace mobject { namespace session { @@ -74,5 +75,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/session-service-mutate.hpp b/src/proc/mobject/session/session-service-mutate.hpp index dbd063592..54db65c36 100644 --- a/src/proc/mobject/session/session-service-mutate.hpp +++ b/src/proc/mobject/session/session-service-mutate.hpp @@ -46,6 +46,7 @@ +namespace proc { namespace mobject { namespace session { @@ -70,5 +71,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/session-services.cpp b/src/proc/mobject/session/session-services.cpp index c75a6c9bd..5ab2470b1 100644 --- a/src/proc/mobject/session/session-services.cpp +++ b/src/proc/mobject/session/session-services.cpp @@ -36,6 +36,7 @@ using lib::Symbol; +namespace proc { namespace mobject { namespace session { @@ -178,4 +179,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/session-services.hpp b/src/proc/mobject/session/session-services.hpp index a44fddae8..ed95186c1 100644 --- a/src/proc/mobject/session/session-services.hpp +++ b/src/proc/mobject/session/session-services.hpp @@ -82,6 +82,7 @@ +namespace proc { namespace mobject { namespace session { @@ -136,5 +137,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/session.cpp b/src/proc/mobject/session/session.cpp index 891ba1349..322f3c150 100644 --- a/src/proc/mobject/session/session.cpp +++ b/src/proc/mobject/session/session.cpp @@ -46,6 +46,7 @@ using mobject::session::SessManager; using mobject::session::SessManagerImpl; using mobject::session::SessionImplAPI; +namespace proc { namespace mobject { @@ -138,4 +139,4 @@ namespace mobject { -} // namespace mobject +}} // namespace proc::mobject diff --git a/src/proc/mobject/session/simpleclip.cpp b/src/proc/mobject/session/simpleclip.cpp deleted file mode 100644 index e404532bf..000000000 --- a/src/proc/mobject/session/simpleclip.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - SimpleClip - Elementary clip (single media stream only) - - Copyright (C) Lumiera.org - 2008, Hermann Vosseler <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -* *****************************************************/ - - -#include "proc/mobject/session/simpleclip.hpp" - -namespace mobject - { - - namespace session - { - - /** */ - - - - } // namespace mobject::session - -} // namespace mobject diff --git a/src/proc/mobject/session/simpleclip.hpp b/src/proc/mobject/session/simpleclip.hpp deleted file mode 100644 index 6b6c23913..000000000 --- a/src/proc/mobject/session/simpleclip.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - SIMPLECLIP.hpp - Elementary clip (single media stream only) - - Copyright (C) Lumiera.org - 2008, Hermann Vosseler <Ichthyostega@web.de> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - - -#ifndef MOBJECT_SESSION_SIMPLECLIP_H -#define MOBJECT_SESSION_SIMPLECLIP_H - -#include "proc/mobject/session/clip.hpp" - - - -namespace mobject - { - - namespace session - { - - - /** - * Elementary clip consisting of only one media stream - */ - class SimpleClip : public Clip - { - }; - - - - } // namespace mobject::session - -} // namespace mobject -#endif diff --git a/src/proc/mobject/session/specific-contents-query.hpp b/src/proc/mobject/session/specific-contents-query.hpp index 4437441bc..84503c6b8 100644 --- a/src/proc/mobject/session/specific-contents-query.hpp +++ b/src/proc/mobject/session/specific-contents-query.hpp @@ -31,6 +31,7 @@ #include <tr1/functional> +namespace proc { namespace mobject { namespace session { @@ -170,5 +171,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/track.cpp b/src/proc/mobject/session/track.cpp index 78ca7a52a..27ab2f300 100644 --- a/src/proc/mobject/session/track.cpp +++ b/src/proc/mobject/session/track.cpp @@ -24,6 +24,7 @@ #include "proc/mobject/session/track.hpp" +namespace proc { namespace mobject { namespace session { @@ -47,4 +48,4 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/track.hpp b/src/proc/mobject/session/track.hpp index 998c51899..f11bf99a6 100644 --- a/src/proc/mobject/session/track.hpp +++ b/src/proc/mobject/session/track.hpp @@ -30,18 +30,19 @@ +namespace proc { namespace mobject { namespace session { //////////////////////////////////////////////////////TICKET #637 - - using lumiera::P; - using lib::time::Time; - - class Track; - - typedef P<Track> PTrack; - + + using lib::P; + using lib::time::Time; + + class Track; + + typedef P<Track> PTrack; + }} - + namespace asset { //////////////////////////////////////////////////////TICKET #637 typedef EntryID<mobject::session::Track> TrackID; @@ -51,8 +52,8 @@ namespace asset { //////////////////////////////////////////////////////TI namespace mobject { namespace session { - using asset::TrackID; - + using asset::TrackID; + //////////////////////////////////////////////////////TICKET #646 //////////////////////////////////////////////////////TICKET #715 @@ -109,13 +110,13 @@ namespace session { { return refID == id_.getSym(); } - - + + } // namespace mobject::session /** Placement<Track> defined to be subclass of Placement<Meta> */ template class Placement<session::Track, session::Meta>; typedef Placement<session::Track, session::Meta> PTrack; -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/session/wish.cpp b/src/proc/mobject/session/wish.cpp index 3704c77e6..9973feee2 100644 --- a/src/proc/mobject/session/wish.cpp +++ b/src/proc/mobject/session/wish.cpp @@ -23,15 +23,12 @@ #include "proc/mobject/session/wish.hpp" -namespace mobject - { - namespace session - { - - /** */ - - - - } // namespace mobject::session - -} // namespace mobject +namespace proc { +namespace mobject { +namespace session { + + /** */ + + + +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/wish.hpp b/src/proc/mobject/session/wish.hpp index ae5ffbeab..7040589f1 100644 --- a/src/proc/mobject/session/wish.hpp +++ b/src/proc/mobject/session/wish.hpp @@ -28,25 +28,22 @@ -namespace mobject - { - namespace session +namespace proc { +namespace mobject { +namespace session { + + + /** + * LocatingPin representing a low-priority directive by the user, + * to be fulfilled only if possible (and after satisfying the + * more important LocatingPins) + */ + class Wish : public Allocation { - - - /** - * LocatingPin representing a low-priority directive by the user, - * to be fulfilled only if possible (and after satisfying the - * more important LocatingPins) - */ - class Wish : public Allocation - { - - }; - - - - } // namespace mobject::session - -} // namespace mobject + + }; + + + +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/test-dummy-mobject.hpp b/src/proc/mobject/test-dummy-mobject.hpp index 325244691..3c2bce447 100644 --- a/src/proc/mobject/test-dummy-mobject.hpp +++ b/src/proc/mobject/test-dummy-mobject.hpp @@ -60,6 +60,7 @@ using std::cout; +namespace proc { namespace mobject { namespace test { @@ -162,5 +163,5 @@ namespace test { -}} // namespace mobject::test +}}} // namespace proc::mobject::test #endif diff --git a/src/proc/play/dummy-play-connection.cpp b/src/proc/play/dummy-play-connection.cpp index 72dab3d41..065cd0b40 100644 --- a/src/proc/play/dummy-play-connection.cpp +++ b/src/proc/play/dummy-play-connection.cpp @@ -33,26 +33,18 @@ -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 { // hidden local details of the service implementation.... - - } // (End) hidden service impl details - - - - - /** */ + namespace { // hidden local details of the service implementation.... - } // namespace play + } // (End) hidden service impl details -} // namespace proc + + + + /** */ + + +}} // namespace proc::play diff --git a/src/proc/play/output-director.hpp b/src/proc/play/output-director.hpp index 2783b097c..c88e4f13d 100644 --- a/src/proc/play/output-director.hpp +++ b/src/proc/play/output-director.hpp @@ -73,7 +73,7 @@ namespace play { : boost::noncopyable , public lib::Sync<> { - typedef lumiera::Subsys::SigTerm SigTerm; + typedef ::lumiera::Subsys::SigTerm SigTerm; scoped_ptr<PlayService> player_; diff --git a/src/proc/play/output-slot-connection.hpp b/src/proc/play/output-slot-connection.hpp index 34a574d9d..4ab5bdc8b 100644 --- a/src/proc/play/output-slot-connection.hpp +++ b/src/proc/play/output-slot-connection.hpp @@ -59,8 +59,8 @@ namespace proc { namespace play { - using ::engine::BuffHandle; -//using ::engine::BufferProvider; + using proc::engine::BuffHandle; +//using proc::engine::BufferProvider; //using lib::time::Time; //using std::string; using lib::transform; diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index bd51a03b9..095bfb5e1 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -54,8 +54,8 @@ namespace proc { namespace play { - using ::engine::BuffHandle; - using ::engine::BufferProvider; + using proc::engine::BuffHandle; + using proc::engine::BufferProvider; using lib::time::TimeValue; //using std::string; diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index ae8b8d3a6..193dc252e 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -75,7 +75,7 @@ namespace play { // using lumiera::Display; // using lumiera::DummyPlayer; using util::isnil; - using mobject::ModelPort; + using proc::mobject::ModelPort; typedef proc::play::POutputManager POutputManager; typedef lib::IterSource<ModelPort>::iterator ModelPorts; diff --git a/src/proc/state.cpp b/src/proc/state.cpp index 939bd6721..15dc9ba3a 100644 --- a/src/proc/state.cpp +++ b/src/proc/state.cpp @@ -23,8 +23,7 @@ #include "proc/state.hpp" -namespace proc_interface - { +namespace proc_interface { ////////////////////////////////TODO: State is a high -level interface, so can we get rid of this implementation file? /** */ diff --git a/src/proc/state.hpp b/src/proc/state.hpp index 4cee7bc9a..07175c66a 100644 --- a/src/proc/state.hpp +++ b/src/proc/state.hpp @@ -32,10 +32,11 @@ #include <cstddef> +namespace proc { namespace engine { - using lumiera::FrameID; - using lumiera::NodeID; + using ::lumiera::FrameID; + using ::lumiera::NodeID; class StateAdapter; class BuffTableStorage; @@ -109,13 +110,13 @@ namespace engine { friend class engine::StateAdapter; }; -} // namespace engine +}} // namespace proc::engine namespace proc_interface { - using engine::State; + using proc::engine::State; } // namespace proc_interface diff --git a/src/lib/streamtype.cpp b/src/proc/streamtype.cpp similarity index 93% rename from src/lib/streamtype.cpp rename to src/proc/streamtype.cpp index 410227567..0213ba211 100644 --- a/src/lib/streamtype.cpp +++ b/src/proc/streamtype.cpp @@ -21,11 +21,10 @@ * *****************************************************/ -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" -namespace lumiera - { +namespace proc { /** @internal defined here non-inline place the vtable in this object file.*/ StreamType::ImplFacade::ImplFacade (Symbol libID) @@ -34,4 +33,4 @@ namespace lumiera -} // namespace lumiera +} // namespace proc diff --git a/src/lib/streamtype.hpp b/src/proc/streamtype.hpp similarity index 97% rename from src/lib/streamtype.hpp rename to src/proc/streamtype.hpp index e344b811d..3950914f8 100644 --- a/src/lib/streamtype.hpp +++ b/src/proc/streamtype.hpp @@ -31,8 +31,8 @@ */ -#ifndef LUMIERA_STREAMTYPE_HPP -#define LUMIERA_STREAMTYPE_HPP +#ifndef PROC_STREAMTYPE_HPP +#define PROC_STREAMTYPE_HPP #include "lib/symbol.hpp" @@ -42,7 +42,7 @@ #include <boost/noncopyable.hpp> -namespace lumiera { +namespace proc { using lib::Symbol; @@ -190,5 +190,5 @@ namespace lumiera { -} // namespace lumiera +} // namespace proc #endif diff --git a/tests/components/proc/asset/asset-diagnostics.hpp b/tests/components/proc/asset/asset-diagnostics.hpp index 76788d3f5..1429c622d 100644 --- a/tests/components/proc/asset/asset-diagnostics.hpp +++ b/tests/components/proc/asset/asset-diagnostics.hpp @@ -87,5 +87,5 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset #endif diff --git a/tests/components/proc/asset/basicpipetest.cpp b/tests/components/proc/asset/basicpipetest.cpp index 6084dba5e..5c5a59456 100644 --- a/tests/components/proc/asset/basicpipetest.cpp +++ b/tests/components/proc/asset/basicpipetest.cpp @@ -48,8 +48,7 @@ namespace test { using mobject::Session; using lumiera::Query; using lumiera::query::normaliseID; - using lumiera::StreamType; - + /*********************************************************************** diff --git a/tests/components/proc/asset/testasset.hpp b/tests/components/proc/asset/testasset.hpp index ff1044e83..f352d3cb8 100644 --- a/tests/components/proc/asset/testasset.hpp +++ b/tests/components/proc/asset/testasset.hpp @@ -74,5 +74,5 @@ namespace asset } // namespace test -} // namespace asset +}} // namespace proc::asset #endif diff --git a/tests/components/proc/asset/testclipasset.hpp b/tests/components/proc/asset/testclipasset.hpp index eb5167abc..a43dd4d7e 100644 --- a/tests/components/proc/asset/testclipasset.hpp +++ b/tests/components/proc/asset/testclipasset.hpp @@ -53,5 +53,5 @@ namespace asset -} // namespace asset +}} // namespace proc::asset #endif diff --git a/tests/components/proc/asset/typed-id-test.cpp b/tests/components/proc/asset/typed-id-test.cpp index 7835c162b..f5de32f52 100644 --- a/tests/components/proc/asset/typed-id-test.cpp +++ b/tests/components/proc/asset/typed-id-test.cpp @@ -47,7 +47,7 @@ using util::for_each; using util::isnil; //using lib::Literal; using lib::Symbol; -using lumiera::P; +using lib::P; //using std::string; using std::cout; using std::endl; diff --git a/tests/components/proc/control/command-basic-test.cpp b/tests/components/proc/control/command-basic-test.cpp index ef392906e..121a83b7a 100644 --- a/tests/components/proc/control/command-basic-test.cpp +++ b/tests/components/proc/control/command-basic-test.cpp @@ -34,7 +34,7 @@ using std::rand; namespace control { namespace test { - using lumiera::P; + using lib::P; using lib::time::Time; using lib::time::TimeVar; using lib::time::TimeValue; diff --git a/tests/components/proc/mobject/session/defs-manager-impl-test.cpp b/tests/components/proc/mobject/session/defs-manager-impl-test.cpp index 48ad7f98c..3c6835255 100644 --- a/tests/components/proc/mobject/session/defs-manager-impl-test.cpp +++ b/tests/components/proc/mobject/session/defs-manager-impl-test.cpp @@ -30,7 +30,7 @@ #include "common/configrules.hpp" #include "proc/assetmanager.hpp" #include "proc/mobject/session.hpp" -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" #include <boost/format.hpp> @@ -54,7 +54,6 @@ namespace test { using lumiera::ConfigRules; using lumiera::query::QueryHandler; - using lumiera::StreamType; diff --git a/tests/components/proc/mobject/session/defs-manager-test.cpp b/tests/components/proc/mobject/session/defs-manager-test.cpp index e2d618a1b..15431d354 100644 --- a/tests/components/proc/mobject/session/defs-manager-test.cpp +++ b/tests/components/proc/mobject/session/defs-manager-test.cpp @@ -31,7 +31,7 @@ #include "proc/asset/struct.hpp" #include "proc/assetmanager.hpp" #include "proc/mobject/session.hpp" -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" #include <boost/format.hpp> @@ -55,7 +55,6 @@ namespace test { using asset::Struct; using lumiera::Query; using lumiera::query::normaliseID; - using lumiera::StreamType; /** shortcut: run just a query diff --git a/tests/components/proc/mobject/session/defs-registry-impl-test.cpp b/tests/components/proc/mobject/session/defs-registry-impl-test.cpp index 8025bfbf0..ccf3f7817 100644 --- a/tests/components/proc/mobject/session/defs-registry-impl-test.cpp +++ b/tests/components/proc/mobject/session/defs-registry-impl-test.cpp @@ -35,10 +35,10 @@ #include <boost/format.hpp> #include <map> -using lumiera::P; using lumiera::Query; using lumiera::query::test::garbage_query; using util::isnil; +using lib::P; using boost::scoped_ptr; using boost::format; diff --git a/tests/lib/advice/advice-configuration-test.cpp b/tests/lib/advice/advice-configuration-test.cpp index 0ea2be33d..8e90f2aa3 100644 --- a/tests/lib/advice/advice-configuration-test.cpp +++ b/tests/lib/advice/advice-configuration-test.cpp @@ -45,7 +45,7 @@ //using util::isnil; //using lib::Literal; //using lib::Symbol; -//using lumiera::P; +//using lib::P; //using std::string; //using std::cout; //using std::endl; diff --git a/tests/lib/advice/advice-multiplicity-test.cpp b/tests/lib/advice/advice-multiplicity-test.cpp index c95da30e3..00b789ab6 100644 --- a/tests/lib/advice/advice-multiplicity-test.cpp +++ b/tests/lib/advice/advice-multiplicity-test.cpp @@ -45,7 +45,7 @@ //using util::isnil; //using lib::Literal; //using lib::Symbol; -//using lumiera::P; +//using lib::P; //using std::string; //using std::cout; //using std::endl; diff --git a/tests/lib/advice/advice-situations-test.cpp b/tests/lib/advice/advice-situations-test.cpp index 88de5debb..e39a4000c 100644 --- a/tests/lib/advice/advice-situations-test.cpp +++ b/tests/lib/advice/advice-situations-test.cpp @@ -45,7 +45,7 @@ //using util::isnil; //using lib::Literal; //using lib::Symbol; -//using lumiera::P; +//using liab::P; //using std::string; //using std::cout; //using std::endl; diff --git a/tests/lib/custom-shared-ptr-test.cpp b/tests/lib/custom-shared-ptr-test.cpp index 45bf5da25..9a8a2c1df 100644 --- a/tests/lib/custom-shared-ptr-test.cpp +++ b/tests/lib/custom-shared-ptr-test.cpp @@ -34,7 +34,6 @@ namespace lib { namespace test{ - using lumiera::P; using std::string; using std::tr1::shared_ptr; using std::tr1::weak_ptr; diff --git a/tests/lib/teststreamtypes.hpp b/tests/lib/teststreamtypes.hpp index fda471b74..11aec0fd6 100644 --- a/tests/lib/teststreamtypes.hpp +++ b/tests/lib/teststreamtypes.hpp @@ -27,7 +27,7 @@ //#include "lib/util.hpp" -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" #include "proc/control/stypemanager.hpp" extern "C" { From b9d1899486a03ce24719bfb72d5d9e73741cdcb8 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 2 Dec 2011 17:50:44 +0100 Subject: [PATCH 287/496] cleanup: rectify Proc-Layer namespaces (II) --- src/include/play-facade.h | 12 +- src/lib/advice/index.hpp | 3 +- src/lib/time/formats.hpp | 2 +- src/lib/typed-counter.hpp | 20 +- src/lib/wrapperptr.hpp | 8 +- src/proc/asset/inventory.hpp | 2 +- src/proc/asset/meta.cpp | 3 +- src/proc/asset/struct-scheme.hpp | 7 +- src/proc/control/argument-erasure.hpp | 3 +- src/proc/control/argument-tuple-accept.hpp | 3 +- src/proc/control/command-argument-holder.hpp | 3 +- src/proc/control/command-closure.hpp | 3 +- src/proc/control/command-def.hpp | 3 +- .../control/command-impl-clone-builder.hpp | 3 +- src/proc/control/command-impl.cpp | 3 +- src/proc/control/command-impl.hpp | 3 +- src/proc/control/command-invocation.hpp | 3 +- src/proc/control/command-mutation.hpp | 3 +- src/proc/control/command-registry.hpp | 3 +- src/proc/control/command-signature.hpp | 3 +- src/proc/control/command.cpp | 5 +- src/proc/control/command.hpp | 3 +- src/proc/control/handling-pattern.cpp | 5 +- src/proc/control/handling-pattern.hpp | 3 +- src/proc/control/handling-patterns.hpp | 3 +- src/proc/control/mediaimpllib.hpp | 9 +- src/proc/control/memento-tie.hpp | 3 +- src/proc/control/pathmanager.cpp | 3 +- src/proc/control/pathmanager.hpp | 3 +- src/proc/control/proc-dispatcher.cpp | 3 +- src/proc/control/proc-dispatcher.hpp | 3 +- src/proc/control/stypemanager.cpp | 5 +- src/proc/control/stypemanager.hpp | 9 +- src/proc/control/styperegistry.hpp | 10 +- src/proc/engine/buffhandle.hpp | 2 +- src/proc/engine/nodeoperation.hpp | 2 +- src/proc/engine/nodewiring-config.hpp | 12 +- src/proc/engine/nodewiring.cpp | 2 +- src/proc/engine/type-handler.hpp | 2 +- src/proc/mobject/builder/buildertool.hpp | 11 +- .../mobject/builder/segmentation-tool.cpp | 12 +- src/proc/mobject/mobject-ref.hpp | 2 + src/proc/mobject/placement-ref.hpp | 2 +- src/proc/mobject/session/abstractmo.hpp | 2 +- src/proc/mobject/session/bus-mo.hpp | 2 +- src/proc/mobject/session/defs-manager.hpp | 9 +- src/proc/mobject/session/placement-index.cpp | 4 +- src/proc/mobject/session/placement-index.hpp | 8 +- src/proc/mobject/session/query-resolver.hpp | 9 +- src/proc/mobject/session/scope-path.cpp | 1 - src/proc/mobject/session/segment.cpp | 2 +- src/proc/mobject/session/session-impl.cpp | 1 - src/proc/mobject/session/session.cpp | 12 +- src/proc/play/output-director.hpp | 2 +- src/proc/state.hpp | 4 +- src/proc/streamtype.hpp | 4 +- tests/40components.tests | 2 +- tests/41asset.tests | 10 +- tests/43session.tests | 42 +- .../components/backend/media-access-mock.cpp | 2 +- .../proc/asset/asset-category-test.cpp | 3 +- .../proc/asset/asset-diagnostics.hpp | 1 + tests/components/proc/asset/basicpipetest.cpp | 331 ++++++----- .../proc/asset/compoundmediatest.cpp | 135 +++-- .../proc/asset/create-asset-test.cpp | 5 +- .../components/proc/asset/deleteassettest.cpp | 61 +- .../proc/asset/dependent-assets-test.cpp | 352 ++++++------ tests/components/proc/asset/entry-id-test.cpp | 5 +- .../proc/asset/identityofassetstest.cpp | 5 +- tests/components/proc/asset/makecliptest.cpp | 97 ++-- .../proc/asset/mediastructurequerytest.cpp | 59 +- .../proc/asset/meta/time-grid-basics-test.cpp | 3 +- .../proc/asset/orderingofassetstest.cpp | 189 ++++--- tests/components/proc/asset/testasset.cpp | 144 +++-- tests/components/proc/asset/testasset.hpp | 71 ++- tests/components/proc/asset/testclipasset.hpp | 14 +- tests/components/proc/asset/typed-id-test.cpp | 12 +- .../control/argument-tuple-accept-test.cpp | 3 +- .../proc/control/command-argument-test.cpp | 195 +++---- .../proc/control/command-basic-test.cpp | 3 +- .../proc/control/command-binding-test.cpp | 3 +- .../control/command-clone-builder-test.cpp | 5 +- .../proc/control/command-equality-test.cpp | 3 +- .../proc/control/command-mutation-test.cpp | 3 +- .../proc/control/command-registry-test.cpp | 3 +- .../proc/control/command-use1-test.cpp | 3 +- .../proc/control/command-use2-test.cpp | 3 +- .../proc/control/command-use3-test.cpp | 3 +- .../control/handling-pattern-basics-test.cpp | 3 +- .../handling-pattern-standard-impl-test.cpp | 7 +- .../proc/control/memento-tie-test.cpp | 3 +- .../proc/control/test-dummy-commands.cpp | 3 +- .../proc/control/test-dummy-commands.hpp | 3 +- .../proc/engine/buff-table-test.cpp | 3 +- .../proc/engine/buffer-metadata-key-test.cpp | 3 +- .../proc/engine/buffer-metadata-test.cpp | 3 +- .../engine/buffer-provider-protocol-test.cpp | 5 +- .../proc/engine/node-basic-test.cpp | 3 +- .../proc/engine/node-fabrication-test.cpp | 3 +- .../proc/engine/node-operation-test.cpp | 3 +- .../proc/engine/node-source-test.cpp | 3 +- .../components/proc/engine/testframe-test.cpp | 3 +- tests/components/proc/engine/testframe.cpp | 3 +- tests/components/proc/engine/testframe.hpp | 5 +- .../tracking-heap-block-provider-test.cpp | 3 +- .../proc/mobject/builder/buildertooltest.cpp | 3 +- .../proc/mobject/builder/buildsegmenttest.cpp | 56 +- .../builder/fixture-change-detector-test.cpp | 3 +- .../builder/model-port-registry-test.cpp | 3 +- .../segmentation-datastructure-test.cpp | 3 +- .../mobject/controller/rendersegmenttest.cpp | 68 ++- .../proc/mobject/mobject-interface-test.cpp | 3 +- .../proc/mobject/mobject-ref-test.cpp | 3 +- .../proc/mobject/output-mapping-test.cpp | 5 +- .../proc/mobject/placement-basic-test.cpp | 3 +- .../proc/mobject/placement-hierarchy-test.cpp | 3 +- .../placement-object-identity-test.cpp | 3 +- .../proc/mobject/placement-ref-test.cpp | 7 +- .../proc/mobject/session/addcliptest.cpp | 66 +-- .../session/defs-manager-impl-test.cpp | 3 +- .../mobject/session/defs-manager-test.cpp | 3 +- .../session/defs-registry-impl-test.cpp | 13 +- .../proc/mobject/session/deletecliptest.cpp | 3 +- .../session/placement-index-query-test.cpp | 3 +- .../mobject/session/placement-index-test.cpp | 3 +- .../mobject/session/placement-scope-test.cpp | 3 +- .../session/query-focus-stack-test.cpp | 3 +- .../proc/mobject/session/query-focus-test.cpp | 3 +- .../mobject/session/query-resolver-test.cpp | 3 +- .../mobject/session/rebuildfixturetest.cpp | 3 +- .../proc/mobject/session/scope-path-test.cpp | 3 +- .../proc/mobject/session/scope-query-test.cpp | 3 +- .../session/session-element-query-test.cpp | 3 +- .../session/session-element-tracker-test.cpp | 5 +- .../session/session-modify-parts-test.cpp | 5 +- .../session/session-service-access-test.cpp | 5 +- .../session/session-structure-test.cpp | 3 +- .../mobject/session/sessionmanagertest.cpp | 3 +- .../mobject/session/test-scope-invalid.hpp | 3 +- .../proc/mobject/session/test-scopes.cpp | 5 +- .../proc/mobject/session/test-scopes.hpp | 3 +- .../proc/mobject/session/testclip.cpp | 3 +- .../proc/mobject/session/testclip.hpp | 3 +- .../proc/mobject/session/testroot.hpp | 3 +- .../proc/mobject/session/testsession1.hpp | 3 +- .../timeline-sequence-handling-test.cpp | 9 +- .../proc/play/diagnostic-output-slot.hpp | 6 +- .../proc/play/output-slot-protocol-test.cpp | 8 +- tests/lib/meta/config-flags-test.cpp | 525 +++++++++--------- .../lib/meta/iterable-classification-test.cpp | 4 +- tests/{common => lib}/ref-array-test.cpp | 0 tests/lib/streamtypebasicstest.cpp | 168 +++--- tests/lib/streamtypelifecycletest.cpp | 154 ++--- tests/lib/test/test-helper-test.cpp | 4 +- tests/lib/teststreamtypes.hpp | 100 ++-- tests/lib/time/time-control-test.cpp | 2 +- tests/lib/time/time-formats-test.cpp | 2 +- tests/lib/time/time-mutation-test.cpp | 2 +- tests/lib/time/time-parsing-test.cpp | 2 +- tests/lib/time/time-quantisation-test.cpp | 2 +- 160 files changed, 1740 insertions(+), 1669 deletions(-) rename tests/{common => lib}/ref-array-test.cpp (100%) diff --git a/src/include/play-facade.h b/src/include/play-facade.h index e2b03713b..c5c2c0f00 100644 --- a/src/include/play-facade.h +++ b/src/include/play-facade.h @@ -119,13 +119,13 @@ namespace lumiera { }; - typedef lib::IterSource<mobject::ModelPort>::iterator ModelPorts; - typedef lib::IterSource<mobject::OutputDesignation>::iterator Pipes; + typedef lib::IterSource<proc::mobject::ModelPort>::iterator ModelPorts; + typedef lib::IterSource<proc::mobject::OutputDesignation>::iterator Pipes; typedef proc::play::POutputManager Output; - typedef mobject::session::PClipMO Clip; - typedef mobject::PTrack Track; - typedef asset::PTimeline Timeline; - typedef asset::PViewer Viewer; + typedef proc::mobject::session::PClipMO Clip; + typedef proc::mobject::PTrack Track; + typedef proc::asset::PTimeline Timeline; + typedef proc::asset::PViewer Viewer; /** core operation: create a new playback process * outputting to the given viewer/display */ diff --git a/src/lib/advice/index.hpp b/src/lib/advice/index.hpp index 5f3fa249a..a98c6fc54 100644 --- a/src/lib/advice/index.hpp +++ b/src/lib/advice/index.hpp @@ -103,6 +103,8 @@ namespace lib { namespace advice { + namespace error = lumiera::error; + using std::tr1::placeholders::_1; using std::tr1::unordered_map; using lib::iter_stl::eachVal; @@ -118,7 +120,6 @@ namespace advice { using std::cout; using std::endl; - using namespace lumiera; diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index a2af01c6f..12ac1b9b8 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -187,7 +187,7 @@ namespace time { std::bitset<MAXID> flags_; template<class F> - size_t + IxID typeID() const { return TypedContext<Supported>::ID<F>::get(); diff --git a/src/lib/typed-counter.hpp b/src/lib/typed-counter.hpp index 8201e6eb3..82ee099bb 100644 --- a/src/lib/typed-counter.hpp +++ b/src/lib/typed-counter.hpp @@ -65,6 +65,8 @@ namespace lib { + typedef size_t IxID; //////////////////////TICKET #863 + using std::vector; @@ -80,11 +82,11 @@ namespace lib { template<class CX> class TypedContext { - static size_t lastGeneratedTypeID; + static IxID lastGeneratedTypeID; public: - static size_t - newTypeID (size_t& typeID) + static IxID + newTypeID (IxID& typeID) { ClassLock<TypedContext> synchronised(); if (!typeID) @@ -96,10 +98,10 @@ namespace lib { template<typename TY> class ID { - static size_t typeID; + static IxID typeID; public: - static size_t + static IxID get() { if (typeID) @@ -112,12 +114,12 @@ namespace lib { /** storage for the type-ID generation mechanism */ template<class CX> - size_t TypedContext<CX>::lastGeneratedTypeID (0); + IxID TypedContext<CX>::lastGeneratedTypeID (0); /** table holding all the generated type-IDs */ template<class CX> template<typename TY> - size_t TypedContext<CX>::ID<TY>::typeID (0); + IxID TypedContext<CX>::ID<TY>::typeID (0); @@ -131,10 +133,10 @@ namespace lib { mutable vector<long> counters_; template<typename TY> - size_t + IxID slot() const { - size_t typeID = TypedContext<TypedCounter>::ID<TY>::get(); + IxID typeID = TypedContext<TypedCounter>::ID<TY>::get(); if (size() < typeID) counters_.resize (typeID); diff --git a/src/lib/wrapperptr.hpp b/src/lib/wrapperptr.hpp index 5ee48f544..6248438f3 100644 --- a/src/lib/wrapperptr.hpp +++ b/src/lib/wrapperptr.hpp @@ -39,10 +39,10 @@ namespace proc { namespace mobject { class MObject; } - typedef ::lumiera::typelist::Types < mobject::Placement<mobject::MObject>* - , lib::P<asset::Asset>* - > ::List - WrapperTypes; + typedef lumiera::typelist::Types < mobject::Placement<mobject::MObject>* + , lib::P<asset::Asset>* + > ::List + WrapperTypes; } namespace lumiera { diff --git a/src/proc/asset/inventory.hpp b/src/proc/asset/inventory.hpp index 6858aeeb0..c3224ecb7 100644 --- a/src/proc/asset/inventory.hpp +++ b/src/proc/asset/inventory.hpp @@ -41,7 +41,7 @@ namespace asset { {}; - typedef lumiera::P<Inventory> PInv; + typedef P<Inventory> PInv; }} // namespace proc::asset diff --git a/src/proc/asset/meta.cpp b/src/proc/asset/meta.cpp index 75ff42c6f..1b0bbaa13 100644 --- a/src/proc/asset/meta.cpp +++ b/src/proc/asset/meta.cpp @@ -80,7 +80,7 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset @@ -95,6 +95,7 @@ namespace asset { //#include "proc/asset/sequence.hpp" +namespace proc { namespace asset { using meta::Descriptor; diff --git a/src/proc/asset/struct-scheme.hpp b/src/proc/asset/struct-scheme.hpp index d0354833a..b0a8fe20f 100644 --- a/src/proc/asset/struct-scheme.hpp +++ b/src/proc/asset/struct-scheme.hpp @@ -45,6 +45,10 @@ using boost::format; +namespace lumiera { + class StreamType; +} + namespace proc { namespace mobject { namespace session { @@ -53,9 +57,6 @@ namespace session { class Clip; }} -namespace lumiera { - class StreamType; -} namespace asset{ diff --git a/src/proc/control/argument-erasure.hpp b/src/proc/control/argument-erasure.hpp index 1a9d15b60..6d74bdf9a 100644 --- a/src/proc/control/argument-erasure.hpp +++ b/src/proc/control/argument-erasure.hpp @@ -29,6 +29,7 @@ +namespace proc { namespace control { @@ -84,5 +85,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/argument-tuple-accept.hpp b/src/proc/control/argument-tuple-accept.hpp index 5db90d0c8..7003c32f7 100644 --- a/src/proc/control/argument-tuple-accept.hpp +++ b/src/proc/control/argument-tuple-accept.hpp @@ -63,6 +63,7 @@ +namespace proc { namespace control { @@ -670,5 +671,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/command-argument-holder.hpp b/src/proc/control/command-argument-holder.hpp index d40fd0433..17b53a822 100644 --- a/src/proc/control/command-argument-holder.hpp +++ b/src/proc/control/command-argument-holder.hpp @@ -52,6 +52,7 @@ +namespace proc { namespace control { using lib::InPlaceBuffer; @@ -278,5 +279,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/command-closure.hpp b/src/proc/control/command-closure.hpp index eb59a1c54..291717bb2 100644 --- a/src/proc/control/command-closure.hpp +++ b/src/proc/control/command-closure.hpp @@ -88,6 +88,7 @@ +namespace proc { namespace control { using lumiera::typelist::FunctionSignature; @@ -302,5 +303,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/command-def.hpp b/src/proc/control/command-def.hpp index 2f87f89d6..959d98505 100644 --- a/src/proc/control/command-def.hpp +++ b/src/proc/control/command-def.hpp @@ -76,6 +76,7 @@ +namespace proc { namespace control { using std::tr1::shared_ptr; @@ -325,5 +326,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/command-impl-clone-builder.hpp b/src/proc/control/command-impl-clone-builder.hpp index cbc6ba378..549a8e30a 100644 --- a/src/proc/control/command-impl-clone-builder.hpp +++ b/src/proc/control/command-impl-clone-builder.hpp @@ -60,6 +60,7 @@ //#include <tr1/functional> +namespace proc { namespace control { using lib::TypedAllocationManager; @@ -187,5 +188,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/command-impl.cpp b/src/proc/control/command-impl.cpp index 36406ced5..3a8f66e36 100644 --- a/src/proc/control/command-impl.cpp +++ b/src/proc/control/command-impl.cpp @@ -39,6 +39,7 @@ //#include <boost/format.hpp> //using boost::str; +namespace proc { namespace control { @@ -55,4 +56,4 @@ namespace control { -} // namespace control +}} // namespace proc::control diff --git a/src/proc/control/command-impl.hpp b/src/proc/control/command-impl.hpp index a25d92fad..bec6be23d 100644 --- a/src/proc/control/command-impl.hpp +++ b/src/proc/control/command-impl.hpp @@ -55,6 +55,7 @@ #include <tr1/functional> +namespace proc { namespace control { using std::tr1::function; @@ -237,5 +238,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/command-invocation.hpp b/src/proc/control/command-invocation.hpp index e357fcd9f..41e1e521f 100644 --- a/src/proc/control/command-invocation.hpp +++ b/src/proc/control/command-invocation.hpp @@ -53,6 +53,7 @@ +namespace proc { namespace control { using namespace lumiera::typelist; @@ -220,5 +221,5 @@ namespace control { } -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/command-mutation.hpp b/src/proc/control/command-mutation.hpp index fd4df2760..b80259a09 100644 --- a/src/proc/control/command-mutation.hpp +++ b/src/proc/control/command-mutation.hpp @@ -55,6 +55,7 @@ +namespace proc { namespace control { using std::ostream; @@ -155,5 +156,5 @@ namespace control { }; -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/command-registry.hpp b/src/proc/control/command-registry.hpp index 833840528..c2231bd6e 100644 --- a/src/proc/control/command-registry.hpp +++ b/src/proc/control/command-registry.hpp @@ -78,6 +78,7 @@ +namespace proc { namespace control { using boost::hash; @@ -268,5 +269,5 @@ namespace control { }; -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/command-signature.hpp b/src/proc/control/command-signature.hpp index f37f82c85..b756cf0ec 100644 --- a/src/proc/control/command-signature.hpp +++ b/src/proc/control/command-signature.hpp @@ -56,6 +56,7 @@ +namespace proc { namespace control { using std::tr1::function; @@ -151,5 +152,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/command.cpp b/src/proc/control/command.cpp index 291edfb7e..6489a3fee 100644 --- a/src/proc/control/command.cpp +++ b/src/proc/control/command.cpp @@ -54,7 +54,6 @@ #include <sstream> #include <string> -using namespace lumiera; using std::ostringstream; using std::string; using boost::format; @@ -62,7 +61,9 @@ using boost::str; using util::cStr; +namespace proc { namespace control { + namespace error = lumiera::error; LUMIERA_ERROR_DEFINE (INVALID_COMMAND, "Unknown or insufficiently defined command"); LUMIERA_ERROR_DEFINE (DUPLICATE_COMMAND, "Attempt to redefine an already existing command definition"); @@ -412,4 +413,4 @@ namespace control { -} // namespace control +}} // namespace proc::control diff --git a/src/proc/control/command.hpp b/src/proc/control/command.hpp index 2ab26d0dd..aa4c6075b 100644 --- a/src/proc/control/command.hpp +++ b/src/proc/control/command.hpp @@ -68,6 +68,7 @@ +namespace proc { namespace control { using std::string; @@ -274,5 +275,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/handling-pattern.cpp b/src/proc/control/handling-pattern.cpp index b71cc6d4c..a5fb54d2f 100644 --- a/src/proc/control/handling-pattern.cpp +++ b/src/proc/control/handling-pattern.cpp @@ -33,12 +33,13 @@ using boost::str; using boost::format; -using namespace lumiera; using util::isnil; using util::cStr; +namespace proc { namespace control { + namespace error = lumiera::error; /** retrieve pre-configured pattern */ HandlingPattern const& @@ -124,4 +125,4 @@ namespace control { } -} // namespace control +}} // namespace proc::control diff --git a/src/proc/control/handling-pattern.hpp b/src/proc/control/handling-pattern.hpp index 84ae17237..331243480 100644 --- a/src/proc/control/handling-pattern.hpp +++ b/src/proc/control/handling-pattern.hpp @@ -52,6 +52,7 @@ +namespace proc { namespace control { using std::string; @@ -146,5 +147,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/handling-patterns.hpp b/src/proc/control/handling-patterns.hpp index 41a87fbeb..daaa58f8a 100644 --- a/src/proc/control/handling-patterns.hpp +++ b/src/proc/control/handling-patterns.hpp @@ -49,6 +49,7 @@ +namespace proc { namespace control { namespace { // concrete command handling patterns @@ -238,5 +239,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/mediaimpllib.hpp b/src/proc/control/mediaimpllib.hpp index 4a5fd683f..e01e3dcb2 100644 --- a/src/proc/control/mediaimpllib.hpp +++ b/src/proc/control/mediaimpllib.hpp @@ -29,6 +29,7 @@ +namespace proc { namespace control { using lib::Symbol; @@ -39,9 +40,9 @@ namespace control { protected: virtual ~MediaImplLib() {}; - typedef lumiera::StreamType::ImplFacade ImplFacade; - typedef lumiera::StreamType::ImplFacade::TypeTag TypeTag; - typedef lumiera::StreamType::ImplFacade::DataBuffer DataBuffer; + typedef StreamType::ImplFacade ImplFacade; + typedef StreamType::ImplFacade::TypeTag TypeTag; + typedef StreamType::ImplFacade::DataBuffer DataBuffer; public: virtual Symbol getLibID() const =0; @@ -50,5 +51,5 @@ namespace control { }; -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/memento-tie.hpp b/src/proc/control/memento-tie.hpp index af63d38d1..917fa1428 100644 --- a/src/proc/control/memento-tie.hpp +++ b/src/proc/control/memento-tie.hpp @@ -53,6 +53,7 @@ #include <string> +namespace proc { namespace control { using boost::equality_comparable; @@ -213,5 +214,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/pathmanager.cpp b/src/proc/control/pathmanager.cpp index 0e3375083..17870694a 100644 --- a/src/proc/control/pathmanager.cpp +++ b/src/proc/control/pathmanager.cpp @@ -23,6 +23,7 @@ #include "proc/control/pathmanager.hpp" +namespace proc { namespace control { @@ -30,4 +31,4 @@ namespace control { /** */ -} // namespace control +}} // namespace proc::control diff --git a/src/proc/control/pathmanager.hpp b/src/proc/control/pathmanager.hpp index 63ea761c2..1c0e11722 100644 --- a/src/proc/control/pathmanager.hpp +++ b/src/proc/control/pathmanager.hpp @@ -27,6 +27,7 @@ +namespace proc { namespace control { @@ -44,5 +45,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/proc-dispatcher.cpp b/src/proc/control/proc-dispatcher.cpp index b7bb53fc4..6d558678a 100644 --- a/src/proc/control/proc-dispatcher.cpp +++ b/src/proc/control/proc-dispatcher.cpp @@ -32,6 +32,7 @@ //#include <boost/format.hpp> //using boost::str; +namespace proc { namespace control { @@ -94,4 +95,4 @@ namespace control { -} // namespace control +}} // namespace proc::control diff --git a/src/proc/control/proc-dispatcher.hpp b/src/proc/control/proc-dispatcher.hpp index e955b66c5..14f76d09e 100644 --- a/src/proc/control/proc-dispatcher.hpp +++ b/src/proc/control/proc-dispatcher.hpp @@ -43,6 +43,7 @@ +namespace proc { namespace control { // using lib::Symbol; @@ -74,5 +75,5 @@ namespace control { -} // namespace control +}} // namespace proc::control #endif diff --git a/src/proc/control/stypemanager.cpp b/src/proc/control/stypemanager.cpp index 31b5fc8c4..5b386c99b 100644 --- a/src/proc/control/stypemanager.cpp +++ b/src/proc/control/stypemanager.cpp @@ -27,6 +27,7 @@ #include "include/lifecycle.h" +namespace proc { namespace control { using lib::Symbol; @@ -113,7 +114,7 @@ namespace control { UNIMPLEMENTED ("STypeManager basic functionality: wire up implementation facade (impl type) from given raw type of the library"); } -} // namespace control +}} // namespace proc::control // ==== C interface for registering setup of basic stream type configuration ======= @@ -121,5 +122,5 @@ namespace control { void lumiera_StreamType_registerInitFunction (void setupFun(void)) { - lumiera::LifecycleHook (control::ON_STREAMTYPES_RESET, setupFun); + lumiera::LifecycleHook (proc::control::ON_STREAMTYPES_RESET, setupFun); } diff --git a/src/proc/control/stypemanager.hpp b/src/proc/control/stypemanager.hpp index f3cbd3444..d59120b65 100644 --- a/src/proc/control/stypemanager.hpp +++ b/src/proc/control/stypemanager.hpp @@ -21,8 +21,8 @@ */ -#ifndef CONTROL_STYPEMANAGER_H -#define CONTROL_STYPEMANAGER_H +#ifndef PROC_CONTROL_STYPEMANAGER_H +#define PROC_CONTROL_STYPEMANAGER_H #include "proc/streamtype.hpp" @@ -31,6 +31,7 @@ #include <boost/scoped_ptr.hpp> +namespace proc { namespace control { using lib::Symbol; @@ -102,12 +103,12 @@ namespace control { } -} // namespace control +}} // namespace proc::control namespace proc_interface { - using control::STypeManager; + using proc::control::STypeManager; } // namespace proc_interface diff --git a/src/proc/control/styperegistry.hpp b/src/proc/control/styperegistry.hpp index 73bf97ba1..205260701 100644 --- a/src/proc/control/styperegistry.hpp +++ b/src/proc/control/styperegistry.hpp @@ -37,6 +37,7 @@ #define CONTROL_STYPEREGISTRY_H +#include "proc/control/stypemanager.hpp" //#include "lib/query.hpp" //#include "lib/util.hpp" //#include "lib/p.hpp" @@ -48,6 +49,7 @@ #include <boost/noncopyable.hpp> +namespace proc { namespace control { // using lib::P; @@ -63,7 +65,7 @@ namespace control { /** * @internal Helper for organising preconfigured default objects. - * Maintaines a collection of objects known or encountered as "default" + * Maintains a collection of objects known or encountered as "default" * for a given type. This collection is ordered by "degree of constriction", * which is implemented by counting the number of predicates in the query * used to define or identify each object. @@ -73,7 +75,8 @@ namespace control { * @todo as of 3/2008 the real query implementation is missing, and the * exact behaviour has to be defined. */ - class STypeManager::Registry : private boost::noncopyable + class STypeManager::Registry + : boost::noncopyable { public: @@ -81,6 +84,5 @@ namespace control { -} // namespace control - +}} // namespace proc::control #endif diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index 24816854d..ec8228ca7 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -59,7 +59,7 @@ namespace proc { namespace engine { - namespace error = ::lumiera::error; + namespace error = lumiera::error; using error::LUMIERA_ERROR_LIFECYCLE; typedef size_t HashVal; ////////////TICKET #722 diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp index e656e9813..f6ef36cf9 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -336,5 +336,5 @@ namespace config { -}} // namespace engine::config +}}} // namespace proc::engine::config #endif diff --git a/src/proc/engine/nodewiring-config.hpp b/src/proc/engine/nodewiring-config.hpp index 713c68828..5a3b7e7e8 100644 --- a/src/proc/engine/nodewiring-config.hpp +++ b/src/proc/engine/nodewiring-config.hpp @@ -52,8 +52,8 @@ #define ENGINE_NODEWIRING_CONFIG_H -#include "lib/util.hpp" #include "lib/meta/configflags.hpp" +#include "lib/util.hpp" #include <tr1/functional> #include <bitset> @@ -69,6 +69,8 @@ namespace config { using lumiera::typelist::CONFIG_FLAGS_MAX; + typedef size_t IxID; ///////////////////////////////TICKET #863 + /** * Helper for fabricating ProcNode Wiring configurations. @@ -125,7 +127,7 @@ namespace config { typedef std::tr1::shared_ptr<FacFunction> PFunc; - typedef std::map<size_t, PFunc> ConfigTable; + typedef std::map<IxID, PFunc> ConfigTable; ConfigTable possibleConfig_; ///< Table of factories @@ -148,7 +150,7 @@ namespace config { template<class CONF> void - visit (size_t code) + visit (IxID code) { PFunc pFactory (new FactoryHolder<Factory<CONF> > (ctor_param_)); factories_[code] = pFactory; @@ -170,7 +172,7 @@ namespace config { } FacFunction& - operator[] (size_t configFlags) ///< retrieve the factory corresponding to the given config + operator[] (IxID configFlags) ///< retrieve the factory corresponding to the given config { if (contains (possibleConfig_, configFlags)) return *possibleConfig_[configFlags]; @@ -182,5 +184,5 @@ namespace config { -}} // namespace engine::config +}}} // namespace proc::engine::config #endif diff --git a/src/proc/engine/nodewiring.cpp b/src/proc/engine/nodewiring.cpp index f216c0d14..a3a3b01c1 100644 --- a/src/proc/engine/nodewiring.cpp +++ b/src/proc/engine/nodewiring.cpp @@ -150,4 +150,4 @@ namespace engine { } -}} // namespace engine +}} // namespace proc::engine diff --git a/src/proc/engine/type-handler.hpp b/src/proc/engine/type-handler.hpp index b02f6e637..c3835d683 100644 --- a/src/proc/engine/type-handler.hpp +++ b/src/proc/engine/type-handler.hpp @@ -57,7 +57,7 @@ namespace engine { using std::tr1::function; using std::tr1::placeholders::_1; - namespace error = ::lumiera::error; + namespace error = lumiera::error; namespace { // (optional) helpers to build an object embedded into a buffer... diff --git a/src/proc/mobject/builder/buildertool.hpp b/src/proc/mobject/builder/buildertool.hpp index e707aabcc..8ba2b6ac3 100644 --- a/src/proc/mobject/builder/buildertool.hpp +++ b/src/proc/mobject/builder/buildertool.hpp @@ -106,9 +106,8 @@ namespace mobject { * as we simply store a pointer within the BuilderTool instance. */ class BuilderTool - : public ::lumiera::visitor::Tool<void, InvokeCatchAllFunction> - { - ::lumiera::WrapperPtr currentWrapper_; + : public lumiera::visitor::Tool<void, InvokeCatchAllFunction> + { lumiera::WrapperPtr currentWrapper_; public: @@ -167,11 +166,11 @@ namespace mobject { class TYPELIST // list of all concrete Buildables to be treated > class Applicable - : public ::lumiera::visitor::Applicable<TOOLImpl, TYPELIST, BuilderTool> + : public lumiera::visitor::Applicable<TOOLImpl, TYPELIST, BuilderTool> { } ; - using ::lumiera::typelist::Types; // convenience for the users of "Applicable" + using lumiera::typelist::Types; // convenience for the users of "Applicable" }// namespace mobject::builder @@ -182,7 +181,7 @@ namespace mobject { /** * Marker Interface for classes visitable by Builder tools. */ - class Buildable : public ::lumiera::visitor::Visitable<builder::BuilderTool> + class Buildable : public lumiera::visitor::Visitable<builder::BuilderTool> { }; diff --git a/src/proc/mobject/builder/segmentation-tool.cpp b/src/proc/mobject/builder/segmentation-tool.cpp index 69263068c..895a2fe53 100644 --- a/src/proc/mobject/builder/segmentation-tool.cpp +++ b/src/proc/mobject/builder/segmentation-tool.cpp @@ -24,19 +24,19 @@ #include "proc/mobject/builder/segmentation-tool.hpp" -using mobject::Buildable; -using mobject::session::Clip; -using mobject::session::Effect; - - namespace proc { namespace mobject { namespace builder { + using mobject::Buildable; + using session::Clip; + using session::Effect; + using session::Fixture; + /////////////////////////////////TICKET #414 - SegmentationTool::SegmentationTool(mobject::session::Fixture&) + SegmentationTool::SegmentationTool(Fixture&) { UNIMPLEMENTED ("create new SegmentationTool"); } diff --git a/src/proc/mobject/mobject-ref.hpp b/src/proc/mobject/mobject-ref.hpp index 03f0f4e85..457fe8f31 100644 --- a/src/proc/mobject/mobject-ref.hpp +++ b/src/proc/mobject/mobject-ref.hpp @@ -72,6 +72,8 @@ namespace proc { namespace mobject { + namespace error = lumiera::error; + class MObject; diff --git a/src/proc/mobject/placement-ref.hpp b/src/proc/mobject/placement-ref.hpp index 0887296de..bd859e52b 100644 --- a/src/proc/mobject/placement-ref.hpp +++ b/src/proc/mobject/placement-ref.hpp @@ -74,7 +74,7 @@ namespace proc { - namespace error = ::lumiera::error; + namespace error = lumiera::error; namespace mobject { diff --git a/src/proc/mobject/session/abstractmo.hpp b/src/proc/mobject/session/abstractmo.hpp index a06e8d4ca..7c6b9ef70 100644 --- a/src/proc/mobject/session/abstractmo.hpp +++ b/src/proc/mobject/session/abstractmo.hpp @@ -33,7 +33,7 @@ namespace proc { namespace mobject { namespace session { - namespace error = ::lumiera::error; + namespace error = lumiera::error; /** diff --git a/src/proc/mobject/session/bus-mo.hpp b/src/proc/mobject/session/bus-mo.hpp index 42509f1f6..eb8ef4bac 100644 --- a/src/proc/mobject/session/bus-mo.hpp +++ b/src/proc/mobject/session/bus-mo.hpp @@ -32,7 +32,7 @@ namespace proc { namespace asset { class Pipe; - typedef lumiera::P<Pipe> PPipe; + typedef P<Pipe> PPipe; } diff --git a/src/proc/mobject/session/defs-manager.hpp b/src/proc/mobject/session/defs-manager.hpp index fd473a625..f37c624be 100644 --- a/src/proc/mobject/session/defs-manager.hpp +++ b/src/proc/mobject/session/defs-manager.hpp @@ -39,7 +39,6 @@ namespace session { using lib::P; - using ::lumiera::Query; using boost::scoped_ptr; namespace impl { class DefsRegistry; } @@ -74,14 +73,14 @@ namespace session { * is considered \e misconfiguration. */ template<class TAR> - P<TAR> operator() (Query<TAR> const&); + P<TAR> operator() (lumiera::Query<TAR> const&); /** search through the registered defaults, never create anything. * @return object fulfilling the query, \c empty ptr if not found. */ template<class TAR> - P<TAR> search (Query<TAR> const&); + P<TAR> search (lumiera::Query<TAR> const&); /** retrieve an object fulfilling the query and register it as default. * The resolution is delegated to the ConfigQuery system (which may cause @@ -89,7 +88,7 @@ namespace session { * @return object fulfilling the query, \c empty ptr if no solution. */ template<class TAR> - P<TAR> create (Query<TAR> const&); + P<TAR> create (lumiera::Query<TAR> const&); /** register the given object as default, after ensuring it fulfils the * query. The latter may cause some properties of the object to be set, @@ -98,7 +97,7 @@ namespace session { * @note only a weak ref to the object is stored */ template<class TAR> - bool define (P<TAR> const&, Query<TAR> const& =Query<TAR>()); + bool define (P<TAR> const&, lumiera::Query<TAR> const& =lumiera::Query<TAR>()); /** remove the defaults registration of the given object, if there was such * @return false if nothing has been changed because the object wasn't registered diff --git a/src/proc/mobject/session/placement-index.cpp b/src/proc/mobject/session/placement-index.cpp index 8554674dd..8b12ece8f 100644 --- a/src/proc/mobject/session/placement-index.cpp +++ b/src/proc/mobject/session/placement-index.cpp @@ -90,8 +90,6 @@ namespace session { using util::for_each; using util::has_any; - using namespace lumiera; - LUMIERA_ERROR_DEFINE (NOT_IN_SESSION, "referring to a Placement not known to the current session"); LUMIERA_ERROR_DEFINE (PLACEMENT_TYPE, "requested Placement (pointee) type not compatible with data or context"); LUMIERA_ERROR_DEFINE (NONEMPTY_SCOPE, "Placement scope (still) contains other elements"); @@ -575,7 +573,7 @@ namespace session { struct SelfCheckFailure : error::Fatal { - SelfCheckFailure (Literal currentTest, string failure) + SelfCheckFailure (lib::Literal currentTest, string failure) : error::Fatal (string("Failed test: ")+currentTest+ " : "+failure ,LUMIERA_ERROR_INDEX_CORRUPTED) { } diff --git a/src/proc/mobject/session/placement-index.hpp b/src/proc/mobject/session/placement-index.hpp index 2d0fbea69..f6a416628 100644 --- a/src/proc/mobject/session/placement-index.hpp +++ b/src/proc/mobject/session/placement-index.hpp @@ -107,6 +107,7 @@ #include "lib/util.hpp" #include "lib/error.hpp" +#include "lib/symbol.hpp" #include "lib/itertools.hpp" #include "proc/mobject/placement.hpp" #include "proc/mobject/placement-ref.hpp" @@ -170,8 +171,9 @@ namespace session { typedef PlacementMO::ID _PID; - typedef std::tr1::unordered_multimap<_PID,_PID>::const_iterator ScopeIter; - typedef lib::RangeIter<ScopeIter> ScopeRangeIter; + typedef std::tr1::unordered_multimap<_PID,_PID>::const_iterator ScopeIter; + typedef lib::RangeIter<ScopeIter> ScopeRangeIter; + typedef lib::TransformIter<ScopeRangeIter, PlacementMO&> _ID_TableIterator; @@ -179,7 +181,7 @@ namespace session { typedef PlacementRef<MObject> PRef; typedef PlacementMO::ID const& ID; - typedef lib::TransformIter<ScopeRangeIter, PlacementMO&> iterator; + typedef _ID_TableIterator iterator; /* == query operations == */ diff --git a/src/proc/mobject/session/query-resolver.hpp b/src/proc/mobject/session/query-resolver.hpp index 18908d760..7fe5631dc 100644 --- a/src/proc/mobject/session/query-resolver.hpp +++ b/src/proc/mobject/session/query-resolver.hpp @@ -21,8 +21,8 @@ */ -#ifndef MOBJECT_SESSION_QUERY_RESOLVER_H -#define MOBJECT_SESSION_QUERY_RESOLVER_H +#ifndef PROC_MOBJECT_SESSION_QUERY_RESOLVER_H +#define PROC_MOBJECT_SESSION_QUERY_RESOLVER_H //#include "proc/mobject/mobject.hpp" //#include "proc/mobject/placement.hpp" @@ -45,6 +45,7 @@ namespace proc { namespace mobject { namespace session { + using lib::IxID; using util::unConst; using boost::noncopyable; using boost::scoped_ptr; @@ -89,7 +90,7 @@ namespace session { struct QueryID { Kind kind; - size_t type; + IxID type; }; QueryID const& @@ -159,7 +160,7 @@ namespace session { typedef lib::TypedContext<Goal::Result> ResultType; template<typename RES> - inline size_t + inline IxID getResultTypeID() ///< @return unique ID denoting result type RES { return ResultType::ID<RES>::get(); diff --git a/src/proc/mobject/session/scope-path.cpp b/src/proc/mobject/session/scope-path.cpp index 44ec04c2d..badf0b1db 100644 --- a/src/proc/mobject/session/scope-path.cpp +++ b/src/proc/mobject/session/scope-path.cpp @@ -42,7 +42,6 @@ namespace session { using util::isSameObject; using util::isnil; - using namespace lumiera; LUMIERA_ERROR_DEFINE (EMPTY_SCOPE_PATH, "Placement scope not locatable (empty model path)"); diff --git a/src/proc/mobject/session/segment.cpp b/src/proc/mobject/session/segment.cpp index 87977a008..9ef7e8a0d 100644 --- a/src/proc/mobject/session/segment.cpp +++ b/src/proc/mobject/session/segment.cpp @@ -33,4 +33,4 @@ namespace session { -} // namespace proc::mobject::session +}}} // namespace proc::mobject::session diff --git a/src/proc/mobject/session/session-impl.cpp b/src/proc/mobject/session/session-impl.cpp index 4c44e06e6..d1a73760e 100644 --- a/src/proc/mobject/session/session-impl.cpp +++ b/src/proc/mobject/session/session-impl.cpp @@ -28,7 +28,6 @@ #include "proc/mobject/mobject.hpp" #include "lib/error.hpp" -using namespace lumiera; namespace proc { namespace mobject { diff --git a/src/proc/mobject/session/session.cpp b/src/proc/mobject/session/session.cpp index 322f3c150..583f7f3e9 100644 --- a/src/proc/mobject/session/session.cpp +++ b/src/proc/mobject/session/session.cpp @@ -40,15 +40,17 @@ #include "lib/singleton.hpp" -using lib::Symbol; -using lib::Singleton; -using mobject::session::SessManager; -using mobject::session::SessManagerImpl; -using mobject::session::SessionImplAPI; namespace proc { namespace mobject { + using lib::Symbol; + using lib::Singleton; + using session::SessManager; + using session::SessManagerImpl; + using session::SessionImplAPI; + + /** temporary fix for init problems * @todo really solve the basic init of session manager TICKET #518 diff --git a/src/proc/play/output-director.hpp b/src/proc/play/output-director.hpp index c88e4f13d..2783b097c 100644 --- a/src/proc/play/output-director.hpp +++ b/src/proc/play/output-director.hpp @@ -73,7 +73,7 @@ namespace play { : boost::noncopyable , public lib::Sync<> { - typedef ::lumiera::Subsys::SigTerm SigTerm; + typedef lumiera::Subsys::SigTerm SigTerm; scoped_ptr<PlayService> player_; diff --git a/src/proc/state.hpp b/src/proc/state.hpp index 07175c66a..e2e7414f4 100644 --- a/src/proc/state.hpp +++ b/src/proc/state.hpp @@ -35,8 +35,8 @@ namespace proc { namespace engine { - using ::lumiera::FrameID; - using ::lumiera::NodeID; + using lumiera::FrameID; + using lumiera::NodeID; class StateAdapter; class BuffTableStorage; diff --git a/src/proc/streamtype.hpp b/src/proc/streamtype.hpp index 3950914f8..29dcffa5d 100644 --- a/src/proc/streamtype.hpp +++ b/src/proc/streamtype.hpp @@ -31,8 +31,8 @@ */ -#ifndef PROC_STREAMTYPE_HPP -#define PROC_STREAMTYPE_HPP +#ifndef PROC_STREAMTYPE_H +#define PROC_STREAMTYPE_H #include "lib/symbol.hpp" diff --git a/tests/40components.tests b/tests/40components.tests index 615fe5bfe..da70c1ce4 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -511,7 +511,7 @@ return: 0 END -PLANNED "RefArray_test" RefArray_test <<END +TEST "RefArray_test" RefArray_test <<END return: 0 END diff --git a/tests/41asset.tests b/tests/41asset.tests index bd2cb93a2..d63c42e0a 100644 --- a/tests/41asset.tests +++ b/tests/41asset.tests @@ -34,14 +34,14 @@ END TEST "plain entry-ID" EntryID_test <<END -out: ID<..asset.+Dummy.>-..asset.+Dummy.\.001 -out: ID<..asset.+Dummy.>-strange -out: ID<..asset.+Dummy.>-..asset.+Dummy.\.002 +out: ID<..proc.asset.+Dummy.>-..proc.asset.+Dummy.\.001 +out: ID<..proc.asset.+Dummy.>-strange +out: ID<..proc.asset.+Dummy.>-..proc.asset.+Dummy.\.002 out: ID<track>-track\.001 out: ID<track>-track\.002 out: ID<track>-special -out: sizeof\( ..asset.+EntryID.+mobject.+session.+Track.+ \) = -out: sizeof\( ..asset.+BareEntryID. \) = +out: sizeof\( ..proc.asset.+EntryID.+mobject.+session.+Track.+ \) = +out: sizeof\( ..proc.asset.+BareEntryID. \) = return: 0 END diff --git a/tests/43session.tests b/tests/43session.tests index 0ecfa5964..016864d6d 100644 --- a/tests/43session.tests +++ b/tests/43session.tests @@ -44,40 +44,40 @@ END TEST "external MObject references" MObjectRef_test <<END out: MRef-NIL out: sizeof\( .+MORef.+session.Clip.+ \) = (32|24) -out: Placement<.+Clip.> ............... use-cnt=4 +out: Placement<.+Clip.> .......... use-cnt=4 out: Asset\(VIDEO:lumi.test-1 v1\) -out: Placement<.+Clip.> ............... use-cnt=4 +out: Placement<.+Clip.> .......... use-cnt=4 out: MRef-NIL out: sizeof\( .+MORef.+session.Clip.+ \) = (32|24) -out: Placement<.+Clip.> ............... use-cnt=4 +out: Placement<.+Clip.> .......... use-cnt=4 out: Asset\(VIDEO:lumi.test-2 v1\) -out: Placement<.+Clip.> ............... use-cnt=4 +out: Placement<.+Clip.> .......... use-cnt=4 out: MRef-NIL out: sizeof\( .+MORef.+session.Clip.+ \) = (32|24) -out: Placement<.+Clip.> ............... use-cnt=4 +out: Placement<.+Clip.> .......... use-cnt=4 out: Asset\(VIDEO:lumi.test-1 v1\) -out: Placement<.+Clip.> ............... use-cnt=4 +out: Placement<.+Clip.> .......... use-cnt=4 out: MRef-NIL out: sizeof\( .+MORef.+session.Clip.+ \) = (32|24) -out: Placement<.+Clip.> ............... use-cnt=4 +out: Placement<.+Clip.> .......... use-cnt=4 out: Asset\(VIDEO:lumi.test-2 v1\) -out: Placement<.+Clip.> ............... use-cnt=4 +out: Placement<.+Clip.> .......... use-cnt=4 out: MRef-NIL out: sizeof\( .+MORef.+session.Clip.+ \) = (32|24) out: Placement<.+Clip.>.+ use-cnt=4 out: Asset\(VIDEO:lumi.test-1 v1\) -out: Placement<.+Clip.> ............... use-cnt=4 +out: Placement<.+Clip.> .......... use-cnt=4 out: MRef-NIL out: sizeof\( .+MORef.+session.Clip.+ \) = (32|24) -out: Placement<.+Clip.> ............... use-cnt=4 +out: Placement<.+Clip.> .......... use-cnt=4 out: Asset\(VIDEO:lumi.test-1 v1\) -out: Placement<.+Clip.> ............... use-cnt=4 +out: Placement<.+Clip.> .......... use-cnt=4 out: MRef-NIL out: sizeof\( .+MORef.+session.Clip.+ \) = (32|24) -out: Placement<.+Clip.> ............... use-cnt=4 +out: Placement<.+Clip.> .......... use-cnt=4 out: Asset\(VIDEO:lumi.test-2 v1\) -out: Placement<.+Clip.> ............... use-cnt=4 -out: Placement<.+Clip.> ............... use-cnt=5 +out: Placement<.+Clip.> .......... use-cnt=4 +out: Placement<.+Clip.> .......... use-cnt=5 out: \(VIDEO:lumi.test-1 v1\) END @@ -105,7 +105,7 @@ END TEST "PlacementRef_test" PlacementRef_test <<END -out: Placement<.+TestSubMO21.> .......... use-cnt=2 +out: Placement<.+TestSubMO21.> ..... use-cnt=2 out: specialAPI() END @@ -127,13 +127,13 @@ END TEST "Querying the index" PlacementIndexQuery_test <<END out: explore contents depth-first... -out: Placement<.+TestSubMO21.> .......... use-cnt=1 -out: Placement<.+TestSubMO2.> ........... use-cnt=1 -out: Placement<.+TestSubMO1.> ........... use-cnt=1 -out: Placement<.+DummyMO.> ............... use-cnt=1 +out: Placement<.+TestSubMO21.> ..... use-cnt=1 +out: Placement<.+TestSubMO2.> ...... use-cnt=1 +out: Placement<.+TestSubMO1.> ...... use-cnt=1 +out: Placement<.+DummyMO.> .......... use-cnt=1 out: path to root starting at Placement<.+TestSubMO1 -out: Placement<.+TestSubMO2.> ........... use-cnt=1 -out: Placement<.+session.Label.> .............. use-cnt=1 +out: Placement<.+TestSubMO2.> ...... use-cnt=1 +out: Placement<.+session.Label.> ......... use-cnt=1 END diff --git a/tests/components/backend/media-access-mock.cpp b/tests/components/backend/media-access-mock.cpp index 096ecf197..837b27af4 100644 --- a/tests/components/backend/media-access-mock.cpp +++ b/tests/components/backend/media-access-mock.cpp @@ -90,7 +90,7 @@ namespace test { }; int Response::_i_(0); const ChanDesc NULLResponse; - using mobject::session::test::LENGTH_TestClip; + using proc::mobject::session::test::LENGTH_TestClip; struct TestCases : map<string,Response> diff --git a/tests/components/proc/asset/asset-category-test.cpp b/tests/components/proc/asset/asset-category-test.cpp index 7bfb68f4e..98ac06b2e 100644 --- a/tests/components/proc/asset/asset-category-test.cpp +++ b/tests/components/proc/asset/asset-category-test.cpp @@ -36,6 +36,7 @@ using std::string; using std::cout; +namespace proc { namespace asset{ namespace test { @@ -138,4 +139,4 @@ namespace test { -}} // namespace asset::test +}}} // namespace proc::asset::test diff --git a/tests/components/proc/asset/asset-diagnostics.hpp b/tests/components/proc/asset/asset-diagnostics.hpp index 1429c622d..36ff66a33 100644 --- a/tests/components/proc/asset/asset-diagnostics.hpp +++ b/tests/components/proc/asset/asset-diagnostics.hpp @@ -50,6 +50,7 @@ using std::string; using std::cout; +namespace proc { namespace asset { inline void diff --git a/tests/components/proc/asset/basicpipetest.cpp b/tests/components/proc/asset/basicpipetest.cpp index 5c5a59456..316c24fde 100644 --- a/tests/components/proc/asset/basicpipetest.cpp +++ b/tests/components/proc/asset/basicpipetest.cpp @@ -42,174 +42,173 @@ using std::string; using std::cout; +namespace proc { namespace asset { namespace test { - using mobject::Session; - using lumiera::Query; - using lumiera::query::normaliseID; + using mobject::Session; + using lumiera::Query; + using lumiera::query::normaliseID; + + + + /*********************************************************************** + * @test basic properties of Pipe (structural) Assets. + * <ul><li>created by referral</li> + * <li>access existing pipe by referral</li> + * <li>create with full properties</li> + * <li>access ProcPatt</li> + * <li>check dependency</li> + * </ul> + */ + class BasicPipe_test : public Test + { + virtual void run(Arg arg) + { + string pipeID = isnil(arg)? "Black Hole" : arg[1]; + string streamID = 2>arg.size()? "teststream" : arg[2] ; + + createExplicit (pipeID,streamID); + create_or_ref (pipeID); + create_using_default (); + dependProcPatt (pipeID); + } - - - /*********************************************************************** - * @test basic properties of Pipe (structural) Assets. - * <ul><li>created by referral</li> - * <li>access existing pipe by referral</li> - * <li>create with full properties</li> - * <li>access ProcPatt</li> - * <li>check dependency</li> - * </ul> - */ - class BasicPipe_test : public Test - { - virtual void run(Arg arg) - { - string pipeID = isnil(arg)? "Black Hole" : arg[1]; - string streamID = 2>arg.size()? "teststream" : arg[2] ; - - createExplicit (pipeID,streamID); - create_or_ref (pipeID); - create_using_default (); - dependProcPatt (pipeID); - } - - - - - void createExplicit (string pID, string sID) - { - string pID_sane (pID); - normaliseID (pID_sane); - CHECK (pID_sane != pID); - - PPipe thePipe = asset::Struct::retrieve.newPipe (pID,sID); - - CHECK (thePipe); - CHECK (thePipe->getProcPatt()); - CHECK (thePipe->getPipeID() == pID_sane); - CHECK (thePipe->getStreamID() == StreamType::ID(sID)); - CHECK (thePipe->shortDesc == pID_sane); - - Asset::Ident idi = thePipe->ident; - CHECK (idi.org == "lumi"); - CHECK (contains (idi.name, thePipe->getPipeID())); - CHECK (contains (idi.name, thePipe->getStreamID())); - - Category cat (idi.category); - Category refcat (STRUCT,"pipes"); - CHECK ( cat.hasKind(STRUCT) ); - CHECK ( cat.isWithin(refcat) ); - } - - - void create_or_ref(string pID) - { - normaliseID (pID); - - PPipe pipe1 = Pipe::query ("pipe("+pID+")"); - CHECK (pipe1); - CHECK (pipe1->getPipeID() == pID); - - string pID2 = "another-" + pID; - PPipe pipe2 = Pipe::query ("pipe("+pID2+")"); - CHECK (pipe2); - CHECK (pipe2 != pipe1); - Category c1 = pipe1->ident.category; - Category c2 = pipe2->ident.category; - CHECK (c1 == c2); - - PPipe pipe3 = Pipe::query ("pipe("+pID2+")"); + + + + void createExplicit (string pID, string sID) + { + string pID_sane (pID); + normaliseID (pID_sane); + CHECK (pID_sane != pID); + + PPipe thePipe = asset::Struct::retrieve.newPipe (pID,sID); + + CHECK (thePipe); + CHECK (thePipe->getProcPatt()); + CHECK (thePipe->getPipeID() == pID_sane); + CHECK (thePipe->getStreamID() == StreamType::ID(sID)); + CHECK (thePipe->shortDesc == pID_sane); + + Asset::Ident idi = thePipe->ident; + CHECK (idi.org == "lumi"); + CHECK (contains (idi.name, thePipe->getPipeID())); + CHECK (contains (idi.name, thePipe->getStreamID())); + + Category cat (idi.category); + Category refcat (STRUCT,"pipes"); + CHECK ( cat.hasKind(STRUCT) ); + CHECK ( cat.isWithin(refcat) ); + } + + + void create_or_ref(string pID) + { + normaliseID (pID); + + PPipe pipe1 = Pipe::query ("pipe("+pID+")"); + CHECK (pipe1); + CHECK (pipe1->getPipeID() == pID); + + string pID2 = "another-" + pID; + PPipe pipe2 = Pipe::query ("pipe("+pID2+")"); + CHECK (pipe2); + CHECK (pipe2 != pipe1); + Category c1 = pipe1->ident.category; + Category c2 = pipe2->ident.category; + CHECK (c1 == c2); + + PPipe pipe3 = Pipe::query ("pipe("+pID2+")"); //////////////////////////////////////////////////////////////TODO: that's broken; creating a new one instead to find the existing one, as it should be - CHECK (pipe3 == pipe2); - } - - - void create_using_default() - { - PPipe pipe1 = Pipe::query (""); // "the default pipe" - PPipe pipe2; - CHECK (pipe1); - CHECK (pipe1 == Session::current->defaults (Query<Pipe>())); - CHECK (pipe1->ident.category.hasKind(VIDEO)); - CHECK (pipe1->getProcPatt()); - PProcPatt propa = Session::current->defaults (Query<const ProcPatt>("pipe(default)")); - CHECK (propa == pipe1->getProcPatt()); - - // several variants to query for "the default pipe" - pipe2 = Session::current->defaults(Query<Pipe> ()); - CHECK (pipe2 == pipe1); - pipe2 = asset::Struct::retrieve (Query<Pipe> ()); - CHECK (pipe2 == pipe1); - pipe2 = asset::Struct::retrieve (Query<Pipe> ("pipe(default)")); - CHECK (pipe2 == pipe1); - - string sID = pipe1->getStreamID(); // sort of a "default stream type" - PPipe pipe3 = Pipe::query ("stream("+sID+")"); - CHECK (pipe3); - CHECK (pipe3->getStreamID() == StreamType::ID(sID)); - CHECK (pipe3->getProcPatt() == Session::current->defaults (Query<const ProcPatt>("stream("+sID+")"))); - } - - - void dependProcPatt(string pID) - { - typedef P<Pipe> PPipe; /////TODO: transition to P<> - typedef P<const ProcPatt> PProcPatt; - - PPipe thePipe = Pipe::query ("pipe("+pID+")"); - CHECK (thePipe); - PProcPatt thePatt = thePipe->getProcPatt(); - CHECK (thePatt); - CHECK (dependencyCheck (thePipe, thePatt)); - - PProcPatt pattern2 = thePatt->newCopy("another"); - CHECK (thePatt != pattern2); - CHECK (!dependencyCheck (thePipe, pattern2)); - TODO ("add something to the new pattern, e.g. an effect"); - - // now querying for a pipe using this pattern (created on-the-fly) - // note: because the pattern is new, this new pipe will be used as - // default pipe for this pattern automatically - PPipe pipe2x = Pipe::query ("pattern(another)"); - CHECK (pattern2 == pipe2x->getProcPatt()); - CHECK (pipe2x == Session::current->defaults (Query<Pipe>("pattern(another)"))); - - thePipe->switchProcPatt(pattern2); - CHECK ( dependencyCheck (thePipe, pattern2)); - CHECK (!dependencyCheck (thePipe, thePatt)); - - AssetManager& aMang = AssetManager::instance(); - CHECK ( aMang.known (thePipe->getID())); - CHECK ( aMang.known (thePatt->getID())); - CHECK ( aMang.known (pattern2->getID())); - aMang.remove (pattern2->getID()); - CHECK ( aMang.known (thePatt->getID())); - CHECK (!aMang.known (pattern2->getID())); - CHECK (!aMang.known (thePipe->getID())); // has been unlinked too, because dependant on pattern2 - - CHECK (thePipe); - PProcPatt pattern3 = thePipe->getProcPatt(); /////TODO: transition to P<> - CHECK (thePipe->getProcPatt()); - CHECK ( pattern3 == pattern2); // but is still valid, as long as the ref is alive.... - - PPipe pipe3x = Pipe::query ("pattern(another)"); - pattern3 = pipe3x->getProcPatt(); /////TODO: transition to P<> - CHECK (pattern3 != pattern2); // because pattern2 is already unlinked... - CHECK (pipe3x == Session::current->defaults (Query<Pipe>("pattern(another)"))); - CHECK (pipe3x != pipe2x); // ..we got a new default pipe for "pattern(another)" too! - - - TRACE (asset_mem, "leaving BasicPipe_test::dependProcPatt()"); - // expect now pipe2x and pattern2 to be destroyed... - } - }; - - - /** Register this test class... */ - LAUNCHER (BasicPipe_test, "unit asset"); - - - - } // namespace test - -} // namespace asset + CHECK (pipe3 == pipe2); + } + + + void create_using_default() + { + PPipe pipe1 = Pipe::query (""); // "the default pipe" + PPipe pipe2; + CHECK (pipe1); + CHECK (pipe1 == Session::current->defaults (Query<Pipe>())); + CHECK (pipe1->ident.category.hasKind(VIDEO)); + CHECK (pipe1->getProcPatt()); + PProcPatt propa = Session::current->defaults (Query<const ProcPatt>("pipe(default)")); + CHECK (propa == pipe1->getProcPatt()); + + // several variants to query for "the default pipe" + pipe2 = Session::current->defaults(Query<Pipe> ()); + CHECK (pipe2 == pipe1); + pipe2 = asset::Struct::retrieve (Query<Pipe> ()); + CHECK (pipe2 == pipe1); + pipe2 = asset::Struct::retrieve (Query<Pipe> ("pipe(default)")); + CHECK (pipe2 == pipe1); + + string sID = pipe1->getStreamID(); // sort of a "default stream type" + PPipe pipe3 = Pipe::query ("stream("+sID+")"); + CHECK (pipe3); + CHECK (pipe3->getStreamID() == StreamType::ID(sID)); + CHECK (pipe3->getProcPatt() == Session::current->defaults (Query<const ProcPatt>("stream("+sID+")"))); + } + + + void dependProcPatt(string pID) + { + typedef P<Pipe> PPipe; /////TODO: transition to P<> + typedef P<const ProcPatt> PProcPatt; + + PPipe thePipe = Pipe::query ("pipe("+pID+")"); + CHECK (thePipe); + PProcPatt thePatt = thePipe->getProcPatt(); + CHECK (thePatt); + CHECK (dependencyCheck (thePipe, thePatt)); + + PProcPatt pattern2 = thePatt->newCopy("another"); + CHECK (thePatt != pattern2); + CHECK (!dependencyCheck (thePipe, pattern2)); + TODO ("add something to the new pattern, e.g. an effect"); + + // now querying for a pipe using this pattern (created on-the-fly) + // note: because the pattern is new, this new pipe will be used as + // default pipe for this pattern automatically + PPipe pipe2x = Pipe::query ("pattern(another)"); + CHECK (pattern2 == pipe2x->getProcPatt()); + CHECK (pipe2x == Session::current->defaults (Query<Pipe>("pattern(another)"))); + + thePipe->switchProcPatt(pattern2); + CHECK ( dependencyCheck (thePipe, pattern2)); + CHECK (!dependencyCheck (thePipe, thePatt)); + + AssetManager& aMang = AssetManager::instance(); + CHECK ( aMang.known (thePipe->getID())); + CHECK ( aMang.known (thePatt->getID())); + CHECK ( aMang.known (pattern2->getID())); + aMang.remove (pattern2->getID()); + CHECK ( aMang.known (thePatt->getID())); + CHECK (!aMang.known (pattern2->getID())); + CHECK (!aMang.known (thePipe->getID())); // has been unlinked too, because dependant on pattern2 + + CHECK (thePipe); + PProcPatt pattern3 = thePipe->getProcPatt(); /////TODO: transition to P<> + CHECK (thePipe->getProcPatt()); + CHECK ( pattern3 == pattern2); // but is still valid, as long as the ref is alive.... + + PPipe pipe3x = Pipe::query ("pattern(another)"); + pattern3 = pipe3x->getProcPatt(); /////TODO: transition to P<> + CHECK (pattern3 != pattern2); // because pattern2 is already unlinked... + CHECK (pipe3x == Session::current->defaults (Query<Pipe>("pattern(another)"))); + CHECK (pipe3x != pipe2x); // ..we got a new default pipe for "pattern(another)" too! + + + TRACE (asset_mem, "leaving BasicPipe_test::dependProcPatt()"); + // expect now pipe2x and pattern2 to be destroyed... + } + }; + + + /** Register this test class... */ + LAUNCHER (BasicPipe_test, "unit asset"); + + + +}}} // namespace proc::asset::test diff --git a/tests/components/proc/asset/compoundmediatest.cpp b/tests/components/proc/asset/compoundmediatest.cpp index f7bf248d0..d75274d80 100644 --- a/tests/components/proc/asset/compoundmediatest.cpp +++ b/tests/components/proc/asset/compoundmediatest.cpp @@ -35,73 +35,70 @@ using util::isnil; using std::string; -namespace asset - { - namespace test +namespace proc { +namespace asset{ +namespace test { + + + + + /*********************************************************************** + * Verifying the special operations available for compound media assets + * comprised of several elementary media assets. + */ + class CompoundMedia_test : public Test { - - - - - /*********************************************************************** - * Verifying the special operations available for compound media assets - * comprised of several elementary media assets. - */ - class CompoundMedia_test : public Test - { - virtual void run(Arg arg) - { - buildCompound(); - modifyCompound(); - verifyClipStructure(); - - if (!isnil (arg)) - dumpAssetManager(); - TRACE (asset_mem, "leaving CreateAsset_test::run()"); - } - - - - typedef shared_ptr<asset::Media> PM; - - /** @test building a compound media asset by using a special - * factory, normally intended for loading existing sessions. - */ - void buildCompound() - { - UNIMPLEMENTED ("create new compound media"); - } - - /** @test adding and removing elementary media. - */ - void modifyCompound() - { - UNIMPLEMENTED ("add and remove elementary media to compound media asset"); - } - - - /** @test create a (compound) clip from some compound media asset - * and verify the clip mirrors the media asset's structure - */ - void verifyClipStructure() - { - UNIMPLEMENTED ("create compound clip from compound media and verify structure"); - } - - - bool checkProperties (PM object, Asset::Ident identity, string filename) - { - return identity == object->ident - && filename == object->getFilename(); - } - }; - - - /** Register this test class... */ - LAUNCHER (CompoundMedia_test, "unit asset"); - - - - } // namespace test - -} // namespace asset + virtual void run(Arg arg) + { + buildCompound(); + modifyCompound(); + verifyClipStructure(); + + if (!isnil (arg)) + dumpAssetManager(); + TRACE (asset_mem, "leaving CreateAsset_test::run()"); + } + + + + typedef shared_ptr<asset::Media> PM; + + /** @test building a compound media asset by using a special + * factory, normally intended for loading existing sessions. + */ + void buildCompound() + { + UNIMPLEMENTED ("create new compound media"); + } + + /** @test adding and removing elementary media. + */ + void modifyCompound() + { + UNIMPLEMENTED ("add and remove elementary media to compound media asset"); + } + + + /** @test create a (compound) clip from some compound media asset + * and verify the clip mirrors the media asset's structure + */ + void verifyClipStructure() + { + UNIMPLEMENTED ("create compound clip from compound media and verify structure"); + } + + + bool checkProperties (PM object, Asset::Ident identity, string filename) + { + return identity == object->ident + && filename == object->getFilename(); + } + }; + + + /** Register this test class... */ + LAUNCHER (CompoundMedia_test, "unit asset"); + + + +}}} // namespace proc::asset::test diff --git a/tests/components/proc/asset/create-asset-test.cpp b/tests/components/proc/asset/create-asset-test.cpp index f03893198..07b68d428 100644 --- a/tests/components/proc/asset/create-asset-test.cpp +++ b/tests/components/proc/asset/create-asset-test.cpp @@ -37,7 +37,8 @@ using util::isnil; using std::string; -namespace asset { +namespace proc { +namespace asset{ namespace test { @@ -205,4 +206,4 @@ namespace test { -}} // namespace asset::test +}}} // namespace proc::asset::test diff --git a/tests/components/proc/asset/deleteassettest.cpp b/tests/components/proc/asset/deleteassettest.cpp index b47a93755..8b16d822f 100644 --- a/tests/components/proc/asset/deleteassettest.cpp +++ b/tests/components/proc/asset/deleteassettest.cpp @@ -32,36 +32,33 @@ using std::string; using std::cout; -namespace asset - { - namespace test +namespace proc { +namespace asset{ +namespace test { + + + + + /******************************************************************* + * @test deleting an Asset includes removing all dependent Assets + * and all MObjects relying on these. Especially this means + * breaking all links between the involved Objects, so the + * shared-ptrs can do the actual cleanup. + * @see asset::Asset#unlink + * @see mobject::MObject#unlink + */ + class DeleteAsset_test : public Test { - - - - - /******************************************************************* - * @test deleting an Asset includes removing all dependent Assets - * and all MObjects relying on these. Especially this means - * breaking all links between the involved Objects, so the - * shared-ptrs can do the actual cleanup. - * @see asset::Asset#unlink - * @see mobject::MObject#unlink - */ - class DeleteAsset_test : public Test - { - virtual void run(Arg) - { - UNIMPLEMENTED ("delete asset and update all dependencies"); - } - }; - - - /** Register this test class... */ - LAUNCHER (DeleteAsset_test, "function asset"); - - - - } // namespace test - -} // namespace asset + virtual void run(Arg) + { + UNIMPLEMENTED ("delete asset and update all dependencies"); + } + }; + + + /** Register this test class... */ + LAUNCHER (DeleteAsset_test, "function asset"); + + + +}}} // namespace proc::asset::test diff --git a/tests/components/proc/asset/dependent-assets-test.cpp b/tests/components/proc/asset/dependent-assets-test.cpp index 0644dbcda..d50ab6d98 100644 --- a/tests/components/proc/asset/dependent-assets-test.cpp +++ b/tests/components/proc/asset/dependent-assets-test.cpp @@ -35,181 +35,181 @@ using util::isnil; +namespace proc { namespace asset{ namespace test { - - - - - /******************************************************************* - * @test the handling of Assets dependent on other Assets and the - * enabling/disabling of Assets. - * @see asset::Asset - * @see asset::Clip - */ - class DependentAssets_test : public Test - { - virtual void run (Arg) - { - checkDependencyMechanics (); - checkUnlinking (); - checkEnablementPropagation (); - checkRealAssetDependencyRegistration (); - } - - typedef TestAsset<Asset> TA; - typedef TA::PA PTestA; - - - /** @test check operation of basic asset dependency support - */ - void checkDependencyMechanics () - { - PAsset a1 = TA::create(); - CHECK (isnil (a1->getParents())); - CHECK (isnil (a1->getDependant())); - - PTestA a2 = TA::create(a1); - CHECK (a1 == a2->getParents()[0]); // TestAsset registered a1 as parent - CHECK (a2 == a1->getDependant()[0]); - - PAsset a3 = TA::create(); - a2->set_depend(a3); - CHECK (a3 == a2->getParents()[1]); - CHECK (a2 == a3->getDependant()[0]); - CHECK (!contains (a1->getDependant(), a3)); - } - - - /** @test unlink operation removing inter asset links - */ - void checkUnlinking () - { - PTestA a1_ = TA::create(); - PAsset a1 (a1_); - PTestA a2_ = TA::create(a1); - PAsset a2 (a2_); - PAsset a3 = TA::create(a2); - CHECK (a1 == a2->getParents()[0]); - CHECK (a2 == a1->getDependant()[0]); - CHECK (a2 == a3->getParents()[0]); - CHECK (a3 == a2->getDependant()[0]); - - a2_->call_unlink(); - CHECK (isnil (a2->getDependant())); - CHECK (!contains (a1->getDependant(), a2)); // has been propagated up - CHECK (!isnil (a2->getParents())); - CHECK (contains (a3->getParents(), a2)); // but up-links remain intact - - a2_->call_unlink(a1->getID()); - a2_->set_depend(a1); - PAsset a4 = TA::create(a1); - CHECK (a1 == a2->getParents()[0]); - CHECK (a1 == a4->getParents()[0]); - CHECK (a2 == a1->getDependant()[0]); - CHECK (a4 == a1->getDependant()[1]); - - a1_->call_unlink(a4->getID()); - CHECK (!contains (a1->getDependant(), a4)); // selectively removed - CHECK ( contains (a1->getDependant(), a2)); - CHECK (a1 == a4->getParents()[0]); // no propagation - } - - - /** @test enabling and disabling an asset should - * propagate to dependant assets - */ - void checkEnablementPropagation () - { - PAsset a1 = TA::create(); - PTestA a2_= TA::create(a1); - PAsset a2 (a2_); - PAsset a3 = TA::create(); // not dependant - - CHECK (a1->isActive()); - CHECK (a2->isActive()); - CHECK (a3->isActive()); - - a1->enable(false); - CHECK (!a1->isActive()); - CHECK (!a2->isActive()); - CHECK (a3->isActive()); - - a2->enable(true); - CHECK (!a1->isActive()); - CHECK (!a2->isActive()); // ignored because parent is disabled - - a1->enable(true); - CHECK (a1->isActive()); - CHECK (a2->isActive()); - - a2->enable(false); - CHECK (a1->isActive()); - CHECK (!a2->isActive()); // disabling not propagated to parent - a2->enable(true); - CHECK (a1->isActive()); - CHECK (a2->isActive()); - - a3->enable(false); - CHECK (a1->isActive()); - CHECK (a2->isActive()); - CHECK (!a3->isActive()); // no dependency... - - a1->enable(false); - a3->enable(); - CHECK (!a1->isActive()); - CHECK (!a2->isActive()); - CHECK (a3->isActive()); - - a1->enable(); - a2_->set_depend(a3); // now add a new parent dependency - a3->enable(false); - CHECK (a1->isActive()); - CHECK (!a2->isActive()); // has been propagated via the new dependency - CHECK (!a3->isActive()); - - a2->enable(true); - CHECK (a1->isActive()); // no change because one of the parents is disbled - CHECK (!a2->isActive()); - CHECK (!a3->isActive()); - a1->enable(false); - CHECK (!a1->isActive()); - a3->enable(true); - CHECK (!a1->isActive()); // no propagation because the disabled other parent (a1) - CHECK (!a2->isActive()); - CHECK (a3->isActive()); - a1->enable(true); - CHECK (a1->isActive()); // but now propagation is possible - CHECK (a2->isActive()); - CHECK (a3->isActive()); - } - - - /** @test each real world asset subclass has to care - * for registering and deregistering any additional - * dependencies. Here we collect some more prominent - * examples (and hopefully don't fail to cover any - * important special cases...) - */ - void checkRealAssetDependencyRegistration () - { - Use4Test<backend::test::MediaAccessMock> within_this_scope; - - // -----Media and Clip-------------------------------- - typedef P<Media> PM; - typedef P<Clip> PC; - PM mm = asset::Media::create("test-1", VIDEO); - PC cc = mm->createClip()->findClipAsset(); - CHECK (dependencyCheck (cc,mm)); - - } - - }; - - - /** Register this test class... */ - LAUNCHER (DependentAssets_test, "unit function asset"); - - - -}} // namespace asset::test + + + + + /******************************************************************* + * @test the handling of Assets dependent on other Assets and the + * enabling/disabling of Assets. + * @see asset::Asset + * @see asset::Clip + */ + class DependentAssets_test : public Test + { + virtual void run (Arg) + { + checkDependencyMechanics (); + checkUnlinking (); + checkEnablementPropagation (); + checkRealAssetDependencyRegistration (); + } + + typedef TestAsset<Asset> TA; + typedef TA::PA PTestA; + + + /** @test check operation of basic asset dependency support + */ + void checkDependencyMechanics () + { + PAsset a1 = TA::create(); + CHECK (isnil (a1->getParents())); + CHECK (isnil (a1->getDependant())); + + PTestA a2 = TA::create(a1); + CHECK (a1 == a2->getParents()[0]); // TestAsset registered a1 as parent + CHECK (a2 == a1->getDependant()[0]); + + PAsset a3 = TA::create(); + a2->set_depend(a3); + CHECK (a3 == a2->getParents()[1]); + CHECK (a2 == a3->getDependant()[0]); + CHECK (!contains (a1->getDependant(), a3)); + } + + + /** @test unlink operation removing inter asset links + */ + void checkUnlinking () + { + PTestA a1_ = TA::create(); + PAsset a1 (a1_); + PTestA a2_ = TA::create(a1); + PAsset a2 (a2_); + PAsset a3 = TA::create(a2); + CHECK (a1 == a2->getParents()[0]); + CHECK (a2 == a1->getDependant()[0]); + CHECK (a2 == a3->getParents()[0]); + CHECK (a3 == a2->getDependant()[0]); + + a2_->call_unlink(); + CHECK (isnil (a2->getDependant())); + CHECK (!contains (a1->getDependant(), a2)); // has been propagated up + CHECK (!isnil (a2->getParents())); + CHECK (contains (a3->getParents(), a2)); // but up-links remain intact + + a2_->call_unlink(a1->getID()); + a2_->set_depend(a1); + PAsset a4 = TA::create(a1); + CHECK (a1 == a2->getParents()[0]); + CHECK (a1 == a4->getParents()[0]); + CHECK (a2 == a1->getDependant()[0]); + CHECK (a4 == a1->getDependant()[1]); + + a1_->call_unlink(a4->getID()); + CHECK (!contains (a1->getDependant(), a4)); // selectively removed + CHECK ( contains (a1->getDependant(), a2)); + CHECK (a1 == a4->getParents()[0]); // no propagation + } + + + /** @test enabling and disabling an asset should + * propagate to dependant assets + */ + void checkEnablementPropagation () + { + PAsset a1 = TA::create(); + PTestA a2_= TA::create(a1); + PAsset a2 (a2_); + PAsset a3 = TA::create(); // not dependant + + CHECK (a1->isActive()); + CHECK (a2->isActive()); + CHECK (a3->isActive()); + + a1->enable(false); + CHECK (!a1->isActive()); + CHECK (!a2->isActive()); + CHECK (a3->isActive()); + + a2->enable(true); + CHECK (!a1->isActive()); + CHECK (!a2->isActive()); // ignored because parent is disabled + + a1->enable(true); + CHECK (a1->isActive()); + CHECK (a2->isActive()); + + a2->enable(false); + CHECK (a1->isActive()); + CHECK (!a2->isActive()); // disabling not propagated to parent + a2->enable(true); + CHECK (a1->isActive()); + CHECK (a2->isActive()); + + a3->enable(false); + CHECK (a1->isActive()); + CHECK (a2->isActive()); + CHECK (!a3->isActive()); // no dependency... + + a1->enable(false); + a3->enable(); + CHECK (!a1->isActive()); + CHECK (!a2->isActive()); + CHECK (a3->isActive()); + + a1->enable(); + a2_->set_depend(a3); // now add a new parent dependency + a3->enable(false); + CHECK (a1->isActive()); + CHECK (!a2->isActive()); // has been propagated via the new dependency + CHECK (!a3->isActive()); + + a2->enable(true); + CHECK (a1->isActive()); // no change because one of the parents is disbled + CHECK (!a2->isActive()); + CHECK (!a3->isActive()); + a1->enable(false); + CHECK (!a1->isActive()); + a3->enable(true); + CHECK (!a1->isActive()); // no propagation because the disabled other parent (a1) + CHECK (!a2->isActive()); + CHECK (a3->isActive()); + a1->enable(true); + CHECK (a1->isActive()); // but now propagation is possible + CHECK (a2->isActive()); + CHECK (a3->isActive()); + } + + + /** @test each real world asset subclass has to care + * for registering and deregistering any additional + * dependencies. Here we collect some more prominent + * examples (and hopefully don't fail to cover any + * important special cases...) + */ + void checkRealAssetDependencyRegistration () + { + Use4Test<backend::test::MediaAccessMock> within_this_scope; + + // -----Media and Clip-------------------------------- + typedef P<Media> PM; + typedef P<Clip> PC; + PM mm = asset::Media::create("test-1", VIDEO); + PC cc = mm->createClip()->findClipAsset(); + CHECK (dependencyCheck (cc,mm)); + } + + }; + + + /** Register this test class... */ + LAUNCHER (DependentAssets_test, "unit function asset"); + + + +}}} // namespace proc::asset::test diff --git a/tests/components/proc/asset/entry-id-test.cpp b/tests/components/proc/asset/entry-id-test.cpp index c25e9f82c..757d38092 100644 --- a/tests/components/proc/asset/entry-id-test.cpp +++ b/tests/components/proc/asset/entry-id-test.cpp @@ -46,9 +46,12 @@ using std::endl; +namespace proc { namespace asset{ namespace test { + using lumiera::error::LUMIERA_ERROR_WRONG_TYPE; + namespace { // Test definitions... struct Dummy { }; @@ -280,4 +283,4 @@ namespace test { LAUNCHER (EntryID_test, "unit asset"); -}} // namespace asset::test +}}} // namespace proc::asset::test diff --git a/tests/components/proc/asset/identityofassetstest.cpp b/tests/components/proc/asset/identityofassetstest.cpp index 2e1b4739c..27e70c69c 100644 --- a/tests/components/proc/asset/identityofassetstest.cpp +++ b/tests/components/proc/asset/identityofassetstest.cpp @@ -37,7 +37,8 @@ using util::isnil; using std::string; -namespace asset { +namespace proc { +namespace asset{ namespace test { @@ -105,4 +106,4 @@ namespace test { -}} // namespace asset::test +}}} // namespace proc::asset::test diff --git a/tests/components/proc/asset/makecliptest.cpp b/tests/components/proc/asset/makecliptest.cpp index 99a7a3812..609df4c9e 100644 --- a/tests/components/proc/asset/makecliptest.cpp +++ b/tests/components/proc/asset/makecliptest.cpp @@ -39,54 +39,53 @@ using std::string; using std::tr1::static_pointer_cast; //TODO only temporarily; -namespace asset { - namespace test { - - //using mobject::NOBUG_FLAG(mobject_mem); - - - - /*********************************************************************** - * @test creating a Clip MObject and an associated Clip Asset from - * a given asset::Media. - * @see asset::Media#createClip - */ - class MakeClip_test : public Test - { - typedef P<asset::Media> PM; - typedef asset::Media::PClipMO PC; - - virtual void run (Arg) - { - Use4Test<backend::test::MediaAccessMock> within_this_scope; - - - PM mm = asset::Media::create("test-1", VIDEO); - PC cc = mm->createClip(); - PM cm = cc->getMedia(); - - CHECK (cm); - CHECK (!isnil (cc->getLength())); - CHECK (cm->ident.category.hasKind (VIDEO)); - CHECK (cm->getFilename() == mm->getFilename()); +namespace proc { +namespace asset{ +namespace test { + +//using mobject::NOBUG_FLAG(mobject_mem); + + + + /*********************************************************************** + * @test creating a Clip MObject and an associated Clip Asset from + * a given asset::Media. + * @see asset::Media#createClip + */ + class MakeClip_test : public Test + { + typedef P<asset::Media> PM; + typedef asset::Media::PClipMO PC; + + virtual void run (Arg) + { + Use4Test<backend::test::MediaAccessMock> within_this_scope; + + + PM mm = asset::Media::create("test-1", VIDEO); + PC cc = mm->createClip(); + PM cm = cc->getMedia(); + + CHECK (cm); + CHECK (!isnil (cc->getLength())); + CHECK (cm->ident.category.hasKind (VIDEO)); + CHECK (cm->getFilename() == mm->getFilename()); TODO ("implement Processing Pattern!!!"); // CHECK (cm->howtoProc() == mm->howtoProc()); - CHECK (cm->ident.org == mm->ident.org); - CHECK (dependencyCheck (cm,mm)); - - TRACE (asset_mem, "leaving MakeClip_test::run()"); - TRACE (mobject_mem, "leaving MakeClip_test::run()"); - } - - - }; - - - /** Register this test class... */ - LAUNCHER (MakeClip_test, "function asset"); - - - - } // namespace test - -} // namespace asset + CHECK (cm->ident.org == mm->ident.org); + CHECK (dependencyCheck (cm,mm)); + + TRACE (asset_mem, "leaving MakeClip_test::run()"); + TRACE (mobject_mem, "leaving MakeClip_test::run()"); + } + + + }; + + + /** Register this test class... */ + LAUNCHER (MakeClip_test, "function asset"); + + + +}}} // namespace proc::asset::test diff --git a/tests/components/proc/asset/mediastructurequerytest.cpp b/tests/components/proc/asset/mediastructurequerytest.cpp index 99f0aaab8..14f7aeb9f 100644 --- a/tests/components/proc/asset/mediastructurequerytest.cpp +++ b/tests/components/proc/asset/mediastructurequerytest.cpp @@ -34,35 +34,32 @@ using util::isnil; using std::string; -namespace asset - { - namespace test +namespace proc { +namespace asset{ +namespace test { + + + + + /*********************************************************************** + * This test documents the Interface used by MediaFactory when loading + * media files for querying Lumiera's backend layer for information + * on how the media file is structured. + */ + class MediaStructureQuery_test : public Test { - - - - - /*********************************************************************** - * This test documents the Interface used by MediaFactory when loading - * media files for querying Lumiera's backend layer for information - * on how the media file is structured. - */ - class MediaStructureQuery_test : public Test - { - virtual void run(Arg) - { - UNIMPLEMENTED ("querying media file structure from backend"); - } - - - }; - - - /** Register this test class... */ - LAUNCHER (MediaStructureQuery_test, "unit asset"); - - - - } // namespace test - -} // namespace asset + virtual void run(Arg) + { + UNIMPLEMENTED ("querying media file structure from backend"); + } + + + }; + + + /** Register this test class... */ + LAUNCHER (MediaStructureQuery_test, "unit asset"); + + + +}}} // namespace proc::asset::test diff --git a/tests/components/proc/asset/meta/time-grid-basics-test.cpp b/tests/components/proc/asset/meta/time-grid-basics-test.cpp index c40ac7cbb..ea98b04bc 100644 --- a/tests/components/proc/asset/meta/time-grid-basics-test.cpp +++ b/tests/components/proc/asset/meta/time-grid-basics-test.cpp @@ -40,6 +40,7 @@ using std::endl; +namespace proc { namespace asset{ namespace meta { namespace test { @@ -129,4 +130,4 @@ namespace test { LAUNCHER (TimeGridBasics_test, "unit asset"); -}}} // namespace asset::meta::test +}}}} // namespace proc::asset::meta::test diff --git a/tests/components/proc/asset/orderingofassetstest.cpp b/tests/components/proc/asset/orderingofassetstest.cpp index 130b55bfe..62cdede05 100644 --- a/tests/components/proc/asset/orderingofassetstest.cpp +++ b/tests/components/proc/asset/orderingofassetstest.cpp @@ -36,98 +36,97 @@ using util::isnil; using std::string; -namespace asset { - namespace test { - - - - - /****************************************************** - * @test validate the equality and order relations of - * Asset::Ident and Asset objects. - * @note a known problem is that only Asset smart ptrs - * are supported for comparison, not smartpointers - * of Asset subclasses. To solve this, we would - * either have to repeat the operator definitions, - * or resort to template metaprogramming tricks. - * Just providing templated comparison operators - * would generally override the behaviour of - * std::shared_ptr, which is not desirable. - * @see Asset::Ident#compare - */ - class OrderingOfAssets_test : public Test - { - virtual void run(Arg) - { - Use4Test<backend::test::MediaAccessMock> within_this_scope; - - - Asset::Ident key1("test-1", Category(AUDIO), "ichthyo", 5); - PAsset mm1 = asset::Media::create(key1, "Name-1"); - - Asset::Ident key2("test-1", Category(AUDIO), "ichthyo", 7); - PAsset mm2 = asset::Media::create(key2, "Name-2"); - - Asset::Ident key3("test-2", Category(AUDIO), "ichthyo", 5); - PAsset mm3 = asset::Media::create(key3, "Name-3"); - - Asset::Ident key4("test-2", Category(AUDIO), "stega", 5); - PAsset mm4 = asset::Media::create(key4, "Name-4"); - - Asset::Ident key5("test-1", Category(VIDEO), "ichthyo", 5); - PAsset mm5 = asset::Media::create(key5, "Name-5"); - - - // ordering of keys - CHECK (key1 == key2); - CHECK (key2 != key3); - CHECK (key3 != key4); - CHECK (key4 != key5); - CHECK (key1 != key5); - - CHECK ( 0 > key2.compare(key3)); - CHECK ( 0 < key3.compare(key2)); - - CHECK ( 0 > key3.compare(key4)); - CHECK ( 0 > key4.compare(key5)); - CHECK ( 0 > key1.compare(key5)); - CHECK ( 0 > key2.compare(key5)); - CHECK ( 0 > key3.compare(key5)); - CHECK ( 0 > key1.compare(key3)); - CHECK ( 0 > key1.compare(key4)); - CHECK ( 0 > key2.compare(key4)); - - - // ordering of Asset smart ptrs - CHECK (mm1 == mm2); - CHECK (mm2 != mm3); - CHECK (mm3 != mm4); - CHECK (mm4 != mm5); - CHECK (mm1 != mm5); - - CHECK (mm2 < mm3); - CHECK (mm2 <= mm3); - CHECK (mm3 > mm2); - CHECK (mm3 >= mm2); - - CHECK (mm3 < mm4); - CHECK (mm4 < mm5); - CHECK (mm1 < mm5); - CHECK (mm2 < mm5); - CHECK (mm3 < mm5); - CHECK (mm1 < mm3); - CHECK (mm1 < mm4); - CHECK (mm2 < mm4); - - } - }; - - - /** Register this test class... */ - LAUNCHER (OrderingOfAssets_test, "unit asset"); - - - - } // namespace test - -} // namespace asset +namespace proc { +namespace asset{ +namespace test { + + + + + /****************************************************** + * @test validate the equality and order relations of + * Asset::Ident and Asset objects. + * @note a known problem is that only Asset smart ptrs + * are supported for comparison, not smartpointers + * of Asset subclasses. To solve this, we would + * either have to repeat the operator definitions, + * or resort to template metaprogramming tricks. + * Just providing templated comparison operators + * would generally override the behaviour of + * std::shared_ptr, which is not desirable. + * @see Asset::Ident#compare + */ + class OrderingOfAssets_test : public Test + { + virtual void run(Arg) + { + Use4Test<backend::test::MediaAccessMock> within_this_scope; + + + Asset::Ident key1("test-1", Category(AUDIO), "ichthyo", 5); + PAsset mm1 = asset::Media::create(key1, "Name-1"); + + Asset::Ident key2("test-1", Category(AUDIO), "ichthyo", 7); + PAsset mm2 = asset::Media::create(key2, "Name-2"); + + Asset::Ident key3("test-2", Category(AUDIO), "ichthyo", 5); + PAsset mm3 = asset::Media::create(key3, "Name-3"); + + Asset::Ident key4("test-2", Category(AUDIO), "stega", 5); + PAsset mm4 = asset::Media::create(key4, "Name-4"); + + Asset::Ident key5("test-1", Category(VIDEO), "ichthyo", 5); + PAsset mm5 = asset::Media::create(key5, "Name-5"); + + + // ordering of keys + CHECK (key1 == key2); + CHECK (key2 != key3); + CHECK (key3 != key4); + CHECK (key4 != key5); + CHECK (key1 != key5); + + CHECK ( 0 > key2.compare(key3)); + CHECK ( 0 < key3.compare(key2)); + + CHECK ( 0 > key3.compare(key4)); + CHECK ( 0 > key4.compare(key5)); + CHECK ( 0 > key1.compare(key5)); + CHECK ( 0 > key2.compare(key5)); + CHECK ( 0 > key3.compare(key5)); + CHECK ( 0 > key1.compare(key3)); + CHECK ( 0 > key1.compare(key4)); + CHECK ( 0 > key2.compare(key4)); + + + // ordering of Asset smart ptrs + CHECK (mm1 == mm2); + CHECK (mm2 != mm3); + CHECK (mm3 != mm4); + CHECK (mm4 != mm5); + CHECK (mm1 != mm5); + + CHECK (mm2 < mm3); + CHECK (mm2 <= mm3); + CHECK (mm3 > mm2); + CHECK (mm3 >= mm2); + + CHECK (mm3 < mm4); + CHECK (mm4 < mm5); + CHECK (mm1 < mm5); + CHECK (mm2 < mm5); + CHECK (mm3 < mm5); + CHECK (mm1 < mm3); + CHECK (mm1 < mm4); + CHECK (mm2 < mm4); + + } + }; + + + /** Register this test class... */ + LAUNCHER (OrderingOfAssets_test, "unit asset"); + + + +}}} // namespace proc::asset::test diff --git a/tests/components/proc/asset/testasset.cpp b/tests/components/proc/asset/testasset.cpp index 12eba65ad..31aba2acf 100644 --- a/tests/components/proc/asset/testasset.cpp +++ b/tests/components/proc/asset/testasset.cpp @@ -26,71 +26,70 @@ using std::tr1::static_pointer_cast; -namespace asset - { - namespace test + +namespace proc { +namespace asset{ +namespace test { + + namespace { + uint counter (0); - namespace - { - uint counter (0); - - /** @internal helper generating continuosely - * different new asset identities - */ - Asset::Ident - make_new_ident () - { - return Asset::Ident ( str(format("TestAsset.%i") % counter) - , Category (META) - , "test" - , counter++ - ); - } - Asset::Ident - make_new_ident (PAsset& ref) - { - return Asset::Ident ( str(format("%s-TestAsset.%i") % ref->ident.name - % counter) - , ref->ident.category - , "test" - , counter++ - ); - } - } - - - template<class A> - TestAsset<A>::TestAsset () - : A(make_new_ident ()) - { }; - - - template<class A> - TestAsset<A>::TestAsset (PAsset& pRef) - : A(make_new_ident (pRef)) - { - this->defineDependency(pRef); - }; - - - /** @internal helper for the create()-Functions - * retrieving the smart ptr created automatically - * within AssetManager by the Asset base class ctor + /** @internal helper generating continuously + * different new asset identities */ - template<class A> - P<TestAsset<A> > - TestAsset<A>::ptrFromThis () + Asset::Ident + make_new_ident () { - return static_pointer_cast<TestAsset<A>,Asset> - (AssetManager::instance().getAsset (this->id)); - }; + return Asset::Ident ( str(format("TestAsset.%i") % counter) + , Category (META) + , "test" + , counter++ + ); + } + Asset::Ident + make_new_ident (PAsset& ref) + { + return Asset::Ident ( str(format("%s-TestAsset.%i") % ref->ident.name + % counter) + , ref->ident.category + , "test" + , counter++ + ); + } + } + + + template<class A> + TestAsset<A>::TestAsset () + : A(make_new_ident ()) + { }; + + + template<class A> + TestAsset<A>::TestAsset (PAsset& pRef) + : A(make_new_ident (pRef)) + { + this->defineDependency(pRef); + }; + + + /** @internal helper for the create()-Functions + * retrieving the smart ptr created automatically + * within AssetManager by the Asset base class ctor + */ + template<class A> + P<TestAsset<A> > + TestAsset<A>::ptrFromThis () + { + return static_pointer_cast<TestAsset<A>,Asset> + (AssetManager::instance().getAsset (this->id)); + }; - } // namespace test - -} // namespace asset +}}} // namespace proc::asset::test + @@ -99,25 +98,22 @@ namespace asset /* explicit template instantiations for some Asset Kinds */ /*********************************************************/ -#include "proc/asset/unknown.hpp" +#include "proc/asset/unknown.hpp" -namespace asset - { - namespace test - { +namespace proc { +namespace asset{ +namespace test { - template TestAsset<Asset>::TestAsset (); - template TestAsset<Unknown>::TestAsset (); - - template TestAsset<Asset>::TestAsset (PAsset& pRef); - template TestAsset<Unknown>::TestAsset (PAsset& pRef); - - template P<TestAsset<Asset> > TestAsset<Asset>::ptrFromThis (); - template P<TestAsset<Unknown> > TestAsset<Unknown>::ptrFromThis (); - + template TestAsset<Asset>::TestAsset (); + template TestAsset<Unknown>::TestAsset (); + + template TestAsset<Asset>::TestAsset (PAsset& pRef); + template TestAsset<Unknown>::TestAsset (PAsset& pRef); + + template P<TestAsset<Asset> > TestAsset<Asset>::ptrFromThis (); + template P<TestAsset<Unknown> > TestAsset<Unknown>::ptrFromThis (); - } // namespace test -} // namespace asset +}}} // namespace proc::asset::test diff --git a/tests/components/proc/asset/testasset.hpp b/tests/components/proc/asset/testasset.hpp index f352d3cb8..7de1006ab 100644 --- a/tests/components/proc/asset/testasset.hpp +++ b/tests/components/proc/asset/testasset.hpp @@ -34,45 +34,42 @@ using boost::format; using std::string; -namespace asset - { - namespace test +namespace proc { +namespace asset{ +namespace test { + + + /** + * Test(mock) asset subclass usable for hijacking a given + * asset class (template parameter) and subsequently accessing + * internal facillities for writing unit tests. Prerequisite + * for using this template is that the used asset base class + * has a (protected) ctor taking an Asset::Ident.... + */ + template<class A> + class TestAsset : public A { - - - /** - * Test(mock) asset subclass usable for hijacking a given - * asset class (template parameter) and subsequently accessing - * internal facillities for writing unit tests. Prerequisite - * for using this template is that the used asset base class - * has a (protected) ctor taking an Asset::Ident.... - */ - template<class A> - class TestAsset : public A - { - TestAsset () ; - TestAsset (PAsset&); ///< declared dependant on the given Asset - - static void deleter (TestAsset<A>* aa) { delete aa; } - - public: - typedef P<TestAsset<A> > PA; - - static PA create () { return (new TestAsset<A> )->ptrFromThis(); } - static PA create (PAsset& pRef) { return (new TestAsset<A> (pRef))->ptrFromThis(); } - - /* === interesting asset features we want to access for tests === */ - void call_unlink () { this->unlink (); } - void call_unlink (IDA target) { this->unlink (target); } - void set_depend (PAsset parent) { this->defineDependency (parent); } - - private: - PA ptrFromThis (); - }; + TestAsset () ; + TestAsset (PAsset&); ///< declared dependant on the given Asset + + static void deleter (TestAsset<A>* aa) { delete aa; } + + public: + typedef P<TestAsset<A> > PA; + + static PA create () { return (new TestAsset<A> )->ptrFromThis(); } + static PA create (PAsset& pRef) { return (new TestAsset<A> (pRef))->ptrFromThis(); } + + /* === interesting asset features we want to access for tests === */ + void call_unlink () { this->unlink (); } + void call_unlink (IDA target) { this->unlink (target); } + void set_depend (PAsset parent) { this->defineDependency (parent); } + + private: + PA ptrFromThis (); + }; - } // namespace test - -}} // namespace proc::asset +}}} // namespace proc::asset::test #endif diff --git a/tests/components/proc/asset/testclipasset.hpp b/tests/components/proc/asset/testclipasset.hpp index a43dd4d7e..31db981b4 100644 --- a/tests/components/proc/asset/testclipasset.hpp +++ b/tests/components/proc/asset/testclipasset.hpp @@ -36,8 +36,9 @@ using std::string; using std::cout; -namespace asset - { +namespace proc { +namespace asset{ + /** * An asset::Media instance Test Clip for checking * various Asset operations and for creating @@ -48,10 +49,9 @@ namespace asset { public: }; - - - - - + + + + }} // namespace proc::asset #endif diff --git a/tests/components/proc/asset/typed-id-test.cpp b/tests/components/proc/asset/typed-id-test.cpp index f5de32f52..23fb0af84 100644 --- a/tests/components/proc/asset/typed-id-test.cpp +++ b/tests/components/proc/asset/typed-id-test.cpp @@ -54,15 +54,16 @@ using std::endl; +namespace proc { namespace mobject { namespace session { namespace test { struct DummyEntity { }; ////////////////////////TODO of course the key idea is to mix in TypedID::link, to get an automatic registration and deregistartion - typedef lumiera::P<DummyEntity> PDum; + typedef P<DummyEntity> PDum; -}}} +}}}} namespace lumiera{ ///////TODO: shouldn't that be namespace lib? or proc? namespace query { @@ -73,16 +74,17 @@ namespace query { * Of course this is a test/dummy/demonstration. */ template<> - struct TypeHandlerXX<mobject::session::test::DummyEntity> + struct TypeHandlerXX<proc::mobject::session::test::DummyEntity> { static Symbol getID() { return "typed-id-test-dummy"; } }; - }} +}} +namespace proc { namespace asset{ namespace test { @@ -242,4 +244,4 @@ namespace test { LAUNCHER (TypedID_test, "unit asset"); -}} // namespace asset::test +}}} // namespace proc::asset::test diff --git a/tests/components/proc/control/argument-tuple-accept-test.cpp b/tests/components/proc/control/argument-tuple-accept-test.cpp index 38618bcba..b31ee44c1 100644 --- a/tests/components/proc/control/argument-tuple-accept-test.cpp +++ b/tests/components/proc/control/argument-tuple-accept-test.cpp @@ -35,6 +35,7 @@ using std::cout; using std::endl; +namespace proc { namespace control { namespace test { @@ -123,4 +124,4 @@ namespace test { LAUNCHER (ArgumentTupleAccept_test, "unit controller"); -}} // namespace control::test +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/command-argument-test.cpp b/tests/components/proc/control/command-argument-test.cpp index 084940255..a4413e7fa 100644 --- a/tests/components/proc/control/command-argument-test.cpp +++ b/tests/components/proc/control/command-argument-test.cpp @@ -51,22 +51,23 @@ using std::cout; using std::endl; +namespace proc { namespace control { namespace test { - + using lib::test::showSizeof; using lib::test::randTime; using namespace lumiera::typelist; - - - - - + + + + + namespace { // test helpers - + ostringstream protocol; ///< used to verify the test function calls - - + + /** * watching the instance creation * of some parameter values @@ -76,54 +77,54 @@ namespace test { { TY element_; static int instanceCnt; - + Tracker (TY init = TY()) : element_(init) { ++instanceCnt; } Tracker (Tracker const& otr) : element_(otr.element_) { ++instanceCnt; } ~Tracker() { --instanceCnt; } - + TY& operator* () { return element_; } - + operator string() const { return element_; } - + friend ostream& operator<< (ostream& out, const Tracker& tra) { return out << tra.element_; } - + friend bool operator== (Tracker const& tra1, Tracker const& tra2) { return tra1.element_ == tra2.element_; } }; - + template<typename TY> int Tracker<TY>::instanceCnt (0); - - + + /** Dummy custom memento datatype * @note memento needs to be equality comparable */ struct Sint5 { int i[5]; - + friend bool operator== (Sint5 const& i1, Sint5 const& i2) { return i1.i == i2.i; } }; - - - + + + /* === functions to implement test-"operation" & UNDO === */ - + void doIt (Tracker<TimeVar> time, Tracker<string> str, int rand) { @@ -131,40 +132,40 @@ namespace test { cout << "invoke operation..." << endl; protocol << fmt % *time % *str % rand; } - + Tracker<string> captureState (Tracker<TimeVar>, Tracker<string> xstr, int) { cout << "capture state..." << endl; return protocol.str() + *xstr; } - + void undoIt (Tracker<TimeVar> time, Tracker<string>, int, Tracker<string> memento) { cout << "undo... memento=" << memento << endl; protocol << "undoIt(time="<<time<<")----memento-:"<< *memento; } - - + + /// another dummy-UNDO function void dummyU (int,int,int) { } int dummyC (int u,int o) { return u + rand() % (o-u+1); } - - - + + + void showIt (CmdClosure& clo) { cout << clo << endl; } - + void checkSerialisation (CmdClosure& clo) { TODO ("implement serialisation/de-serialisation-Check"); cout << "would be serialised....." << clo << endl; - + // serialise, then de-serialise into a new instance and compare both } @@ -174,17 +175,17 @@ namespace test { { return 10 + rand() % 90; } - - + + } // test-helper implementation - - - - - - + + + + + + typedef lib::ScopedPtrVect<CmdClosure> ArgTuples; - + /*************************************************************************** * @test Check storage handling of the command parameters and state memento. * @@ -193,33 +194,33 @@ namespace test { */ class CommandArgument_test : public Test { - + virtual void run (Arg) { ArgTuples testTuples; Tracker<TimeVar>::instanceCnt = 0; Tracker<string>::instanceCnt = 0; - + createTuples (testTuples); checkArgumentComparison (); serialiseArgTuples (testTuples); testTuples.clear(); - + simulateCmdLifecycle(); - + // verify all dtors properly called... CHECK (0 == Tracker<TimeVar>::instanceCnt); CHECK (0 == Tracker<string>::instanceCnt); } - - + + typedef Tracker<TimeVar> TTime; typedef Tracker<string> Tstr; - - - - + + + + /** @test create various argument tuples and re-access their contents */ void createTuples (ArgTuples& tup) @@ -228,47 +229,47 @@ namespace test { typedef ArgumentHolder<void(int), void*> A2; typedef ArgumentHolder<void(int,TimeVar), int> A3; typedef ArgumentHolder<void(int,TimeVar), Sint5> A4; - + typedef ArgumentHolder<void(TTime,Tstr,int), Tstr> A5; - - + + A1* arg1 = new A1(); tup.manage (arg1); A2* arg2 = new A2(); tup.manage (arg2); A3* arg3 = new A3(); tup.manage (arg3); A4* arg4 = new A4(); tup.manage (arg4); A5* arg5 = new A5(); tup.manage (arg5); - + CHECK (isnil (*arg1)); CHECK (isnil (*arg2)); CHECK (isnil (*arg3)); CHECK (isnil (*arg4)); CHECK (isnil (*arg5)); - + for_each (tup, showIt); - + arg1->storeTuple (tuple::makeNullTuple()); arg2->storeTuple (tuple::make (rand() % 10)); arg3->storeTuple (tuple::make (rand() % 10, TimeVar(randTime()))); arg4->storeTuple (tuple::make (rand() % 10, TimeVar(randTime()))); - + arg5->storeTuple (tuple::make (TTime (randTime()), Tstr("glorious"), twoRandomDigits() )); - + CHECK (!arg5->canUndo()); - + arg5->tie(undoIt, captureState) .tieCaptureFunc() // bind capturing function to memento storage, (TTime(), Tstr("destruction"), 11); // then invoke the bound capturing mechanism - + CHECK (arg5->canUndo()); CHECK (*arg5->memento() == "destruction"); - + VERIFY_ERROR(MISSING_MEMENTO, arg4->memento().i[3] = 513 ); - + for_each (tup, showIt); } - - - + + + /** @test serialise and de-serialise each tuple and check validity * @todo unimplemented, waiting on Serialiser */ @@ -277,41 +278,41 @@ namespace test { { for_each (tup, checkSerialisation); } - - - + + + /** @test verify the comparison operators */ void checkArgumentComparison () { ArgumentHolder<void(int,int), int> one, two; CHECK (one == two); // empty, identically typed argument holders -->equal - + one.tie(dummyU,dummyC) .tieCaptureFunc()(1,9); CHECK (one != two); // now one contains captured UNDO state - + two.tie(dummyU,dummyC) .tieCaptureFunc()(1,9); two.memento() = one.memento(); // put the same UNDO state in both CHECK (one == two); // ...makes them equal again - + one.storeTuple (tuple::make (1,2)); CHECK (one != two); // verify argument tuple comparison CHECK (two != one); CHECK (!isnil (one)); CHECK ( isnil (two)); - + two.storeTuple (tuple::make (3,4)); CHECK (!isnil (two)); CHECK (one != two); CHECK (two != one); - + one.storeTuple (tuple::make (1,4)); CHECK (!isnil (one)); CHECK (one != two); CHECK (two != one); - + one.storeTuple (tuple::make (3,4)); CHECK (!isnil (one)); CHECK (one == two); @@ -321,9 +322,9 @@ namespace test { CHECK (one != two); CHECK (two != one); } - - - + + + /** @test simulate a complete command lifecycle with regards to the * storage handling of the command parameters and state memento. */ @@ -333,71 +334,71 @@ namespace test { typedef void SIG_do(Tracker<TimeVar>, Tracker<string>, int); typedef ArgumentHolder<SIG_do, Tracker<string> > Args; typedef MementoTie<SIG_do, Tracker<string> > MemHolder; - + Args args; CHECK (isnil (args)); cout << showSizeof(args) << endl; - + // store a set of parameter values, later to be used on invocation args.storeTuple ( tuple::make (TTime(randTime()), Tstr("Lumiera rocks"), twoRandomDigits() )); CHECK (!isnil (args)); cout << args << endl; - + CHECK (!args.canUndo()); VERIFY_ERROR(MISSING_MEMENTO, args.memento() ); - + MemHolder& memHolder = args.tie(undoIt,captureState); CHECK (!memHolder); // no stored memento.... CHECK (!args.canUndo()); - + function<SIG_do> doItFun = doIt; function<SIG_do> undoFun = memHolder.tieUndoFunc(); function<SIG_do> captFun = memHolder.tieCaptureFunc(); - + typedef function<void()> OpFun; - + // now close all the functions with the stored parameter values... OpFun bound_doItFun = std::tr1::bind (&CmdClosure::invoke, args, CmdFunctor(doItFun)); OpFun bound_undoFun = std::tr1::bind (&CmdClosure::invoke, args, CmdFunctor(undoFun)); OpFun bound_captFun = std::tr1::bind (&CmdClosure::invoke, args, CmdFunctor(captFun)); - + protocol.seekp(0); protocol << "START..."; - + bound_captFun(); cout << "captured state: " << args.memento() << endl; CHECK (memHolder); CHECK (!isnil (*args.memento())); CHECK (args.canUndo()); cout << args << endl; - + bound_doItFun(); cout << protocol.str() << endl; bound_undoFun(); cout << protocol.str() << endl; - + // Commands can serve as prototype to be copied.... Args argsCopy (args); bound_captFun(); protocol.seekp(0); protocol << "RESET..."; - + args.storeTuple ( tuple::make (TTime(TimeValue(123456)), Tstr("unbelievable"), twoRandomDigits() )); cout << "modified: " << args << endl; cout << "copied : " << argsCopy << endl; // holds still the old params & memento - + bound_undoFun(); cout << protocol.str() << endl; } - + }; - - - + + + /** Register this test class... */ LAUNCHER (CommandArgument_test, "unit controller"); - - -}} // namespace control::test + + +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/command-basic-test.cpp b/tests/components/proc/control/command-basic-test.cpp index 121a83b7a..1e6a3d20a 100644 --- a/tests/components/proc/control/command-basic-test.cpp +++ b/tests/components/proc/control/command-basic-test.cpp @@ -31,6 +31,7 @@ using std::rand; +namespace proc { namespace control { namespace test { @@ -120,4 +121,4 @@ namespace test { LAUNCHER (CommandBasic_test, "unit controller"); -}} // namespace control::test +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/command-binding-test.cpp b/tests/components/proc/control/command-binding-test.cpp index 0f500a3b7..7f42dc3bc 100644 --- a/tests/components/proc/control/command-binding-test.cpp +++ b/tests/components/proc/control/command-binding-test.cpp @@ -29,6 +29,7 @@ #include <iostream> +namespace proc { namespace control { namespace test { @@ -122,4 +123,4 @@ namespace test { LAUNCHER (CommandBinding_test, "function controller"); -}} // namespace control::test +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/command-clone-builder-test.cpp b/tests/components/proc/control/command-clone-builder-test.cpp index 802dfb361..eabc73a69 100644 --- a/tests/components/proc/control/command-clone-builder-test.cpp +++ b/tests/components/proc/control/command-clone-builder-test.cpp @@ -34,12 +34,13 @@ #include "proc/control/test-dummy-commands.hpp" +namespace proc { namespace control { namespace test { using namespace lumiera::typelist; - typedef lumiera::P<CommandImpl> PCmdImpl; + typedef lib::P<CommandImpl> PCmdImpl; namespace { // test config... @@ -173,4 +174,4 @@ namespace test { LAUNCHER (CommandCloneBuilder_test, "function controller"); -}} // namespace control::test +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/command-equality-test.cpp b/tests/components/proc/control/command-equality-test.cpp index c580f1eda..7d8380d0e 100644 --- a/tests/components/proc/control/command-equality-test.cpp +++ b/tests/components/proc/control/command-equality-test.cpp @@ -36,6 +36,7 @@ #include <iostream> #include <string> +namespace proc { namespace control { namespace test { @@ -317,4 +318,4 @@ namespace test { LAUNCHER (CommandEquality_test, "function controller"); -}} // namespace control::test +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/command-mutation-test.cpp b/tests/components/proc/control/command-mutation-test.cpp index fbbbaa716..6ba6f8867 100644 --- a/tests/components/proc/control/command-mutation-test.cpp +++ b/tests/components/proc/control/command-mutation-test.cpp @@ -41,6 +41,7 @@ using std::cout; using std::endl; +namespace proc { namespace control { namespace test { @@ -214,4 +215,4 @@ namespace test { LAUNCHER (CommandMutation_test, "unit controller"); -}} // namespace control::test +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/command-registry-test.cpp b/tests/components/proc/control/command-registry-test.cpp index f0958a48b..31719a197 100644 --- a/tests/components/proc/control/command-registry-test.cpp +++ b/tests/components/proc/control/command-registry-test.cpp @@ -33,6 +33,7 @@ #include <tr1/functional> +namespace proc { namespace control { namespace test { @@ -236,4 +237,4 @@ namespace test { LAUNCHER (CommandRegistry_test, "function controller"); -}} // namespace control::test +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/command-use1-test.cpp b/tests/components/proc/control/command-use1-test.cpp index 949fcfffd..eaa00b2a5 100644 --- a/tests/components/proc/control/command-use1-test.cpp +++ b/tests/components/proc/control/command-use1-test.cpp @@ -33,6 +33,7 @@ #include <iostream> +namespace proc { namespace control { namespace test { @@ -389,4 +390,4 @@ namespace test { LAUNCHER (CommandUse1_test, "function controller"); -}} // namespace control::test +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/command-use2-test.cpp b/tests/components/proc/control/command-use2-test.cpp index 8fec714fa..3753359b1 100644 --- a/tests/components/proc/control/command-use2-test.cpp +++ b/tests/components/proc/control/command-use2-test.cpp @@ -37,6 +37,7 @@ #include <string> +namespace proc { namespace control { namespace test { @@ -204,4 +205,4 @@ namespace test { LAUNCHER (CommandUse2_test, "function controller"); -}} // namespace control::test +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/command-use3-test.cpp b/tests/components/proc/control/command-use3-test.cpp index c2917b598..a5ca90d9b 100644 --- a/tests/components/proc/control/command-use3-test.cpp +++ b/tests/components/proc/control/command-use3-test.cpp @@ -36,6 +36,7 @@ //#include <string> +namespace proc { namespace control { namespace test { @@ -102,4 +103,4 @@ namespace test { LAUNCHER (CommandUse3_test, "function controller"); -}} // namespace control::test +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/handling-pattern-basics-test.cpp b/tests/components/proc/control/handling-pattern-basics-test.cpp index 87458b177..f63c81f00 100644 --- a/tests/components/proc/control/handling-pattern-basics-test.cpp +++ b/tests/components/proc/control/handling-pattern-basics-test.cpp @@ -34,6 +34,7 @@ #include <cstdlib> +namespace proc { namespace control { namespace test { @@ -166,4 +167,4 @@ namespace test { LAUNCHER (HandlingPatternBasics_test, "function controller"); -}} // namespace control::test +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/handling-pattern-standard-impl-test.cpp b/tests/components/proc/control/handling-pattern-standard-impl-test.cpp index 01ba9789a..eea9a3ccf 100644 --- a/tests/components/proc/control/handling-pattern-standard-impl-test.cpp +++ b/tests/components/proc/control/handling-pattern-standard-impl-test.cpp @@ -49,6 +49,7 @@ //#include <string> +namespace proc { namespace control { namespace test { @@ -119,6 +120,6 @@ namespace test { /** Register this test class... */ LAUNCHER (HandlingPatternStandardImpl_test, "function controller"); - - -}} // namespace control::test + + +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/memento-tie-test.cpp b/tests/components/proc/control/memento-tie-test.cpp index 684078d8b..ad1f587aa 100644 --- a/tests/components/proc/control/memento-tie-test.cpp +++ b/tests/components/proc/control/memento-tie-test.cpp @@ -33,6 +33,7 @@ using std::tr1::bind; using std::rand; +namespace proc { namespace control { namespace test { @@ -180,4 +181,4 @@ namespace test { LAUNCHER (MementoTie_test, "unit controller"); -}} // namespace control::test +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/test-dummy-commands.cpp b/tests/components/proc/control/test-dummy-commands.cpp index 81daa1020..64e4aaa67 100644 --- a/tests/components/proc/control/test-dummy-commands.cpp +++ b/tests/components/proc/control/test-dummy-commands.cpp @@ -25,6 +25,7 @@ +namespace proc { namespace control { namespace test { @@ -47,4 +48,4 @@ namespace test { } -}} // namespace control::test +}}} // namespace proc::control::test diff --git a/tests/components/proc/control/test-dummy-commands.hpp b/tests/components/proc/control/test-dummy-commands.hpp index f6dce546d..518c9ff3a 100644 --- a/tests/components/proc/control/test-dummy-commands.hpp +++ b/tests/components/proc/control/test-dummy-commands.hpp @@ -46,6 +46,7 @@ #include <string> +namespace proc { namespace control { namespace test { @@ -150,5 +151,5 @@ namespace test { } -}} // namespace control::test +}}} // namespace proc::control::test #endif diff --git a/tests/components/proc/engine/buff-table-test.cpp b/tests/components/proc/engine/buff-table-test.cpp index 73482d48d..42576dddf 100644 --- a/tests/components/proc/engine/buff-table-test.cpp +++ b/tests/components/proc/engine/buff-table-test.cpp @@ -36,6 +36,7 @@ using std::cout; using std::rand; +namespace proc { namespace engine{ namespace test { @@ -180,4 +181,4 @@ namespace test { -}} // namespace engine::test +}}} // namespace proc::engine::test diff --git a/tests/components/proc/engine/buffer-metadata-key-test.cpp b/tests/components/proc/engine/buffer-metadata-key-test.cpp index a0e5d8f61..768016628 100644 --- a/tests/components/proc/engine/buffer-metadata-key-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-key-test.cpp @@ -36,6 +36,7 @@ using util::isnil; using util::isSameObject; +namespace proc { namespace engine{ namespace metadata{ namespace test { @@ -400,4 +401,4 @@ namespace test { -}}} // namespace engine::metadata::test +}}}} // namespace proc::engine::metadata::test diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp index 2ed136a9b..5ced14c03 100644 --- a/tests/components/proc/engine/buffer-metadata-test.cpp +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -39,6 +39,7 @@ using util::isSameObject; using util::isnil; +namespace proc { namespace engine{ namespace test { @@ -400,4 +401,4 @@ namespace test { -}} // namespace engine::test +}}} // namespace proc::engine::test diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp index 1b128823f..91344b12b 100644 --- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp +++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp @@ -35,12 +35,13 @@ using util::isSameObject; using util::for_each; +namespace proc { namespace engine{ namespace test { using lib::test::Dummy; - using ::engine::BuffHandle; + using proc::engine::BuffHandle; using error::LUMIERA_ERROR_LOGIC; using error::LUMIERA_ERROR_LIFECYCLE; @@ -232,4 +233,4 @@ namespace test { -}} // namespace engine::test +}}} // namespace proc::engine::test diff --git a/tests/components/proc/engine/node-basic-test.cpp b/tests/components/proc/engine/node-basic-test.cpp index 810edf8a7..1c69d3443 100644 --- a/tests/components/proc/engine/node-basic-test.cpp +++ b/tests/components/proc/engine/node-basic-test.cpp @@ -38,6 +38,7 @@ //using std::cout; +namespace proc { namespace engine{ namespace test { @@ -105,4 +106,4 @@ namespace test { -}} // namespace engine::test +}}} // namespace proc::engine::test diff --git a/tests/components/proc/engine/node-fabrication-test.cpp b/tests/components/proc/engine/node-fabrication-test.cpp index ccc0ad798..98ee47e51 100644 --- a/tests/components/proc/engine/node-fabrication-test.cpp +++ b/tests/components/proc/engine/node-fabrication-test.cpp @@ -32,6 +32,7 @@ using std::string; using std::cout; +namespace proc { namespace engine{ namespace test { @@ -55,4 +56,4 @@ namespace test { -}} // namespace engine::test +}}} // namespace proc::engine::test diff --git a/tests/components/proc/engine/node-operation-test.cpp b/tests/components/proc/engine/node-operation-test.cpp index d6823cbaa..fd6425029 100644 --- a/tests/components/proc/engine/node-operation-test.cpp +++ b/tests/components/proc/engine/node-operation-test.cpp @@ -32,6 +32,7 @@ using std::string; using std::cout; +namespace proc { namespace engine{ namespace test { @@ -55,4 +56,4 @@ namespace test { -}} // namespace engine::test +}}} // namespace proc::engine::test diff --git a/tests/components/proc/engine/node-source-test.cpp b/tests/components/proc/engine/node-source-test.cpp index b8bba352b..171d17384 100644 --- a/tests/components/proc/engine/node-source-test.cpp +++ b/tests/components/proc/engine/node-source-test.cpp @@ -32,6 +32,7 @@ using std::string; using std::cout; +namespace proc { namespace engine{ namespace test { @@ -55,4 +56,4 @@ namespace test { -}} // namespace engine::test +}}} // namespace proc::engine::test diff --git a/tests/components/proc/engine/testframe-test.cpp b/tests/components/proc/engine/testframe-test.cpp index a7e086fa9..5b6f62990 100644 --- a/tests/components/proc/engine/testframe-test.cpp +++ b/tests/components/proc/engine/testframe-test.cpp @@ -35,6 +35,7 @@ using util::isSameObject; using boost::scoped_ptr; +namespace proc { namespace engine{ namespace test { @@ -205,4 +206,4 @@ namespace test { -}} // namespace engine::test +}}} // namespace proc::engine::test diff --git a/tests/components/proc/engine/testframe.cpp b/tests/components/proc/engine/testframe.cpp index 0a051b74e..7f46ffe44 100644 --- a/tests/components/proc/engine/testframe.cpp +++ b/tests/components/proc/engine/testframe.cpp @@ -33,6 +33,7 @@ +namespace proc { namespace engine { namespace test { @@ -289,4 +290,4 @@ namespace test { -}} // namespace engine::test +}}} // namespace proc::engine::test diff --git a/tests/components/proc/engine/testframe.hpp b/tests/components/proc/engine/testframe.hpp index 470b5e9ea..05cfecff8 100644 --- a/tests/components/proc/engine/testframe.hpp +++ b/tests/components/proc/engine/testframe.hpp @@ -29,6 +29,7 @@ #include <stdint.h> +namespace proc { namespace engine { namespace test { @@ -113,8 +114,8 @@ namespace test { /** discards all the TestFrame instances and * initialises an empty table of test frames */ void resetTestFrames(); - -}} // namespace engine::test + +}}} // namespace proc::engine::test #endif diff --git a/tests/components/proc/engine/tracking-heap-block-provider-test.cpp b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp index 62ff32412..311a647d3 100644 --- a/tests/components/proc/engine/tracking-heap-block-provider-test.cpp +++ b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp @@ -33,6 +33,7 @@ using std::rand; +namespace proc { namespace engine{ namespace test { @@ -219,4 +220,4 @@ namespace test { -}} // namespace engine::test +}}} // namespace proc::engine::test diff --git a/tests/components/proc/mobject/builder/buildertooltest.cpp b/tests/components/proc/mobject/builder/buildertooltest.cpp index 21d4cba66..988b76bc4 100644 --- a/tests/components/proc/mobject/builder/buildertooltest.cpp +++ b/tests/components/proc/mobject/builder/buildertooltest.cpp @@ -40,6 +40,7 @@ using std::cout; +namespace proc { namespace mobject { namespace builder { namespace test { @@ -145,4 +146,4 @@ namespace test { -}}} // namespace mobject::builder::test +}}}} // namespace proc::mobject::builder::test diff --git a/tests/components/proc/mobject/builder/buildsegmenttest.cpp b/tests/components/proc/mobject/builder/buildsegmenttest.cpp index 39679814d..ba074f761 100644 --- a/tests/components/proc/mobject/builder/buildsegmenttest.cpp +++ b/tests/components/proc/mobject/builder/buildsegmenttest.cpp @@ -32,36 +32,30 @@ using std::string; using std::cout; -namespace mobject - { - namespace builder +namespace proc { +namespace mobject { +namespace builder { +namespace test { + + + + + /******************************************************************* + * @test the builder core functionality: create a render pipeline + * for a given segment of the Session/Timeline. + */ + class BuildSegment_test : public Test { - namespace test - { - - - - - /******************************************************************* - * @test the builder core functionality: create a render pipeline - * for a given segment of the Session/Timeline. - */ - class BuildSegment_test : public Test + virtual void run(Arg) { - virtual void run(Arg) - { - UNIMPLEMENTED ("oh my"); - } - }; - - - /** Register this test class... */ - LAUNCHER (BuildSegment_test, "function builder"); - - - - } // namespace test - - } // namespace builder - -} // namespace mobject + UNIMPLEMENTED ("oh my"); + } + }; + + + /** Register this test class... */ + LAUNCHER (BuildSegment_test, "function builder"); + + + +}}}} // namespace proc::mobject::builder::test diff --git a/tests/components/proc/mobject/builder/fixture-change-detector-test.cpp b/tests/components/proc/mobject/builder/fixture-change-detector-test.cpp index f41a8fe20..c7910c21a 100644 --- a/tests/components/proc/mobject/builder/fixture-change-detector-test.cpp +++ b/tests/components/proc/mobject/builder/fixture-change-detector-test.cpp @@ -30,6 +30,7 @@ #include "lib/util.hpp" +namespace proc { namespace mobject { namespace builder { namespace test { @@ -110,4 +111,4 @@ namespace test { -}}} // namespace mobject::builder::test +}}}} // namespace proc::mobject::builder::test diff --git a/tests/components/proc/mobject/builder/model-port-registry-test.cpp b/tests/components/proc/mobject/builder/model-port-registry-test.cpp index d9fea6e99..a5ebb39e5 100644 --- a/tests/components/proc/mobject/builder/model-port-registry-test.cpp +++ b/tests/components/proc/mobject/builder/model-port-registry-test.cpp @@ -30,6 +30,7 @@ #include "lib/util.hpp" +namespace proc { namespace mobject { namespace builder { namespace test { @@ -272,4 +273,4 @@ namespace test { -}}} // namespace mobject::builder::test +}}}} // namespace proc::mobject::builder::test diff --git a/tests/components/proc/mobject/builder/segmentation-datastructure-test.cpp b/tests/components/proc/mobject/builder/segmentation-datastructure-test.cpp index 91cd1cd05..a39af76f2 100644 --- a/tests/components/proc/mobject/builder/segmentation-datastructure-test.cpp +++ b/tests/components/proc/mobject/builder/segmentation-datastructure-test.cpp @@ -30,6 +30,7 @@ #include "lib/util.hpp" +namespace proc { namespace mobject { namespace builder { namespace test { @@ -110,4 +111,4 @@ namespace test { -}}} // namespace mobject::builder::test +}}}} // namespace proc::mobject::builder::test diff --git a/tests/components/proc/mobject/controller/rendersegmenttest.cpp b/tests/components/proc/mobject/controller/rendersegmenttest.cpp index a74d1bdb4..6f0bfc538 100644 --- a/tests/components/proc/mobject/controller/rendersegmenttest.cpp +++ b/tests/components/proc/mobject/controller/rendersegmenttest.cpp @@ -32,42 +32,36 @@ using std::string; using std::cout; -namespace mobject - { - namespace controller +namespace proc { +namespace mobject { +namespace controller { +namespace test { + + + + + /******************************************************************** + * @test create a render process from a given segment of the Session. + * Basically this includes cooperation of all parts of the + * Lumiera Proc Layer. For a prepared test-Session we invoke the + * controller to create a render process. This includes building + * the render pipeline. Finally, we analyze all the created + * Structures. + * @note this test doesn't cover the actual rendering. + * @see proc_interface::ControllerFacade + */ + class RenderSegment_test : public Test { - namespace test - { - - - - - /******************************************************************** - * @test create a render process from a given segment of the Session. - * Basically this includes cooperation of all parts of the - * Lumiera Proc Layer. For a prepared test-Session we invoke the - * controller to create a render process. This includes building - * the render pipeline. Finally, we analyze all the created - * Structures. - * @note this test doesn't cover the actual rendering. - * @see proc_interface::ControllerFacade - */ - class RenderSegment_test : public Test + virtual void run(Arg arg) { - virtual void run(Arg arg) - { - UNIMPLEMENTED ("complete render process for a given test segment of the Session"); - } - }; - - - /** Register this test class... */ - LAUNCHER (RenderSegment_test, "function operate"); - - - - } // namespace test - - } // namespace controller - -} // namespace mobject + UNIMPLEMENTED ("complete render process for a given test segment of the Session"); + } + }; + + + /** Register this test class... */ + LAUNCHER (RenderSegment_test, "function operate"); + + + +}}}} // namespace proc::mobject::controller::test diff --git a/tests/components/proc/mobject/mobject-interface-test.cpp b/tests/components/proc/mobject/mobject-interface-test.cpp index 26e893648..3f0d5a8fe 100644 --- a/tests/components/proc/mobject/mobject-interface-test.cpp +++ b/tests/components/proc/mobject/mobject-interface-test.cpp @@ -44,6 +44,7 @@ +namespace proc { namespace mobject { namespace test { @@ -125,4 +126,4 @@ namespace test { LAUNCHER (MObjectInterface_test, "unit session"); -}} // namespace mobject::test +}}} // namespace proc::mobject::test diff --git a/tests/components/proc/mobject/mobject-ref-test.cpp b/tests/components/proc/mobject/mobject-ref-test.cpp index 3aa6fe976..1b9ae39e8 100644 --- a/tests/components/proc/mobject/mobject-ref-test.cpp +++ b/tests/components/proc/mobject/mobject-ref-test.cpp @@ -50,6 +50,7 @@ using std::cout; using std::endl; +namespace proc { namespace mobject { namespace test { @@ -379,4 +380,4 @@ namespace test { LAUNCHER (MObjectRef_test, "unit session"); -}} // namespace mobject::test +}}} // namespace proc::mobject::test diff --git a/tests/components/proc/mobject/output-mapping-test.cpp b/tests/components/proc/mobject/output-mapping-test.cpp index e8a6a7e03..5bd75c464 100644 --- a/tests/components/proc/mobject/output-mapping-test.cpp +++ b/tests/components/proc/mobject/output-mapping-test.cpp @@ -35,8 +35,9 @@ using util::isnil; using std::string; +namespace proc { namespace mobject { -namespace test { +namespace test { using asset::Pipe; using asset::PPipe; @@ -179,4 +180,4 @@ namespace test { -}} // namespace mobject::test +}}} // namespace proc::mobject::test diff --git a/tests/components/proc/mobject/placement-basic-test.cpp b/tests/components/proc/mobject/placement-basic-test.cpp index 5aeed4d15..1eccec30a 100644 --- a/tests/components/proc/mobject/placement-basic-test.cpp +++ b/tests/components/proc/mobject/placement-basic-test.cpp @@ -39,6 +39,7 @@ using std::string; using std::cout; +namespace proc { namespace mobject { namespace session { namespace test { @@ -93,4 +94,4 @@ namespace test { -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/placement-hierarchy-test.cpp b/tests/components/proc/mobject/placement-hierarchy-test.cpp index e3657229c..6dbcd39a4 100644 --- a/tests/components/proc/mobject/placement-hierarchy-test.cpp +++ b/tests/components/proc/mobject/placement-hierarchy-test.cpp @@ -43,6 +43,7 @@ using std::cout; using std::endl; +namespace proc { namespace mobject { namespace session { namespace test { @@ -173,4 +174,4 @@ namespace test { LAUNCHER (PlacementHierarchy_test, "unit session"); -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/placement-object-identity-test.cpp b/tests/components/proc/mobject/placement-object-identity-test.cpp index fd349ed5e..e31662d08 100644 --- a/tests/components/proc/mobject/placement-object-identity-test.cpp +++ b/tests/components/proc/mobject/placement-object-identity-test.cpp @@ -42,6 +42,7 @@ //using std::endl; +namespace proc { namespace mobject { namespace test { @@ -155,4 +156,4 @@ namespace test { LAUNCHER (PlacementObjectIdentity_test, "function session"); -}} // namespace mobject::test +}}} // namespace proc::mobject::test diff --git a/tests/components/proc/mobject/placement-ref-test.cpp b/tests/components/proc/mobject/placement-ref-test.cpp index 8d91c660b..f57d509e6 100644 --- a/tests/components/proc/mobject/placement-ref-test.cpp +++ b/tests/components/proc/mobject/placement-ref-test.cpp @@ -40,6 +40,7 @@ using std::cout; using std::endl; +namespace proc { namespace mobject { namespace session { namespace test { @@ -48,8 +49,8 @@ namespace test { typedef TestPlacement<TestSubMO21> PSub; typedef PlacementMO::ID P_ID; - - + + /*************************************************************************** * @test properties and behaviour of the reference-mechanism for Placements. * We create an mock placement index and install it to be used @@ -188,4 +189,4 @@ namespace test { LAUNCHER (PlacementRef_test, "unit session"); -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/addcliptest.cpp b/tests/components/proc/mobject/session/addcliptest.cpp index 35dd0a7d9..4e57950a3 100644 --- a/tests/components/proc/mobject/session/addcliptest.cpp +++ b/tests/components/proc/mobject/session/addcliptest.cpp @@ -36,44 +36,38 @@ using std::string; using std::cout; -namespace mobject - { - namespace session +namespace proc { +namespace mobject { +namespace session { +namespace test { + + + + + /******************************************************************* + * @test adding an test clip to the Model/Session. + * @see mobject::session::Clip + * @see mobject::Session + */ + class AddClip_test : public Test { - namespace test - { - - - - - /******************************************************************* - * @test adding an test clip to the Model/Session. - * @see mobject::session::Clip - * @see mobject::Session - */ - class AddClip_test : public Test + virtual void + run (Arg) { - virtual void - run (Arg) - { - PSess sess = Session::current; - PMO clip = TestClip::create(); - sess->attach (clip); - + PSess sess = Session::current; + PMO clip = TestClip::create(); + sess->attach (clip); + ///////////////////////////////////////////////////////////////////TICKET #499 // CHECK (sess->currEDL().contains (clip)); - // TODO: Clip-Asset and Placement magic?? - } - }; - - - /** Register this test class... */ - LAUNCHER (AddClip_test, "unit session"); - - - - } // namespace test + // TODO: Clip-Asset and Placement magic?? + } + }; - } // namespace session - -} // namespace mobject + + /** Register this test class... */ + LAUNCHER (AddClip_test, "unit session"); + + + +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/defs-manager-impl-test.cpp b/tests/components/proc/mobject/session/defs-manager-impl-test.cpp index 3c6835255..9afa64b21 100644 --- a/tests/components/proc/mobject/session/defs-manager-impl-test.cpp +++ b/tests/components/proc/mobject/session/defs-manager-impl-test.cpp @@ -39,6 +39,7 @@ using util::isnil; using std::string; +namespace proc { namespace mobject { namespace session { namespace test { @@ -170,4 +171,4 @@ lumiera::query::setFakeBypass("stream("+sID+")"); ////////////////////////////// -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/defs-manager-test.cpp b/tests/components/proc/mobject/session/defs-manager-test.cpp index 15431d354..720c12b6c 100644 --- a/tests/components/proc/mobject/session/defs-manager-test.cpp +++ b/tests/components/proc/mobject/session/defs-manager-test.cpp @@ -41,6 +41,7 @@ using std::string; +namespace proc { namespace mobject { namespace session { namespace test { @@ -198,4 +199,4 @@ namespace test { LAUNCHER (DefsManager_test, "function session"); -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/defs-registry-impl-test.cpp b/tests/components/proc/mobject/session/defs-registry-impl-test.cpp index ccf3f7817..756001376 100644 --- a/tests/components/proc/mobject/session/defs-registry-impl-test.cpp +++ b/tests/components/proc/mobject/session/defs-registry-impl-test.cpp @@ -48,6 +48,7 @@ using std::map; +namespace proc { namespace mobject { namespace session { namespace test { @@ -98,8 +99,8 @@ namespace test { { scoped_ptr<DefsRegistry> reg_; - typedef lumiera::P<Dummy<13> > O; - typedef lumiera::P<Dummy<23> > P; + typedef P<Dummy<13> > Obj; + typedef P<Dummy<23> > Prd; typedef Query<Dummy<13> > Q13; typedef Query<Dummy<23> > Q23; @@ -112,9 +113,9 @@ namespace test { lib::factory::RefcountFac<Dummy<13> > oFac; lib::factory::RefcountFac<Dummy<23> > pFac; - O o1, o2, o3; + Obj o1, o2, o3; Q13 q1, q2, q3, q4, q5; - map<Q23, P> ps; + map<Q23, Prd> ps; public: DefsRegistryImpl_test () @@ -157,7 +158,7 @@ namespace test { ps.clear(); for (int i=0; i<100; ++i) { - P px (pFac()); + Prd px (pFac()); Q23 qx (garbage_query()); ps[qx] = px; reg_->put (px, qx); @@ -289,4 +290,4 @@ namespace test { -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/deletecliptest.cpp b/tests/components/proc/mobject/session/deletecliptest.cpp index e803f165e..2738462e7 100644 --- a/tests/components/proc/mobject/session/deletecliptest.cpp +++ b/tests/components/proc/mobject/session/deletecliptest.cpp @@ -42,6 +42,7 @@ using proc_interface::IDA; using std::tr1::dynamic_pointer_cast; /// TODO only temp +namespace proc { namespace mobject { namespace session { namespace test { @@ -91,4 +92,4 @@ namespace test { -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/placement-index-query-test.cpp b/tests/components/proc/mobject/session/placement-index-query-test.cpp index efb049cbf..84f0f6680 100644 --- a/tests/components/proc/mobject/session/placement-index-query-test.cpp +++ b/tests/components/proc/mobject/session/placement-index-query-test.cpp @@ -34,6 +34,7 @@ +namespace proc { namespace mobject { namespace session { namespace test { @@ -117,4 +118,4 @@ namespace test { LAUNCHER (PlacementIndexQuery_test, "unit session"); -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/placement-index-test.cpp b/tests/components/proc/mobject/session/placement-index-test.cpp index c61f4b586..0cdb18d66 100644 --- a/tests/components/proc/mobject/session/placement-index-test.cpp +++ b/tests/components/proc/mobject/session/placement-index-test.cpp @@ -42,6 +42,7 @@ using std::cout; using std::endl; +namespace proc { namespace mobject { namespace session { namespace test { @@ -322,4 +323,4 @@ namespace test { LAUNCHER (PlacementIndex_test, "unit session"); -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/placement-scope-test.cpp b/tests/components/proc/mobject/session/placement-scope-test.cpp index 3d3ab194a..f507673a6 100644 --- a/tests/components/proc/mobject/session/placement-scope-test.cpp +++ b/tests/components/proc/mobject/session/placement-scope-test.cpp @@ -34,6 +34,7 @@ using util::isSameObject; +namespace proc { namespace mobject { namespace session { namespace test { @@ -196,4 +197,4 @@ namespace test { LAUNCHER (PlacementScope_test, "function session"); -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/query-focus-stack-test.cpp b/tests/components/proc/mobject/session/query-focus-stack-test.cpp index d5f733b58..161a4b194 100644 --- a/tests/components/proc/mobject/session/query-focus-stack-test.cpp +++ b/tests/components/proc/mobject/session/query-focus-stack-test.cpp @@ -31,6 +31,7 @@ +namespace proc { namespace mobject { namespace session { namespace test { @@ -246,4 +247,4 @@ namespace test { LAUNCHER (QueryFocusStack_test, "unit session"); -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/query-focus-test.cpp b/tests/components/proc/mobject/session/query-focus-test.cpp index ae12108b2..fa73cab30 100644 --- a/tests/components/proc/mobject/session/query-focus-test.cpp +++ b/tests/components/proc/mobject/session/query-focus-test.cpp @@ -32,6 +32,7 @@ +namespace proc { namespace mobject { namespace session { namespace test { @@ -180,4 +181,4 @@ namespace test { LAUNCHER (QueryFocus_test, "unit session"); -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/query-resolver-test.cpp b/tests/components/proc/mobject/session/query-resolver-test.cpp index 42cfe1045..df70b2cb7 100644 --- a/tests/components/proc/mobject/session/query-resolver-test.cpp +++ b/tests/components/proc/mobject/session/query-resolver-test.cpp @@ -31,6 +31,7 @@ +namespace proc { namespace mobject { namespace session { namespace test { @@ -222,4 +223,4 @@ namespace test { LAUNCHER (QueryResolver_test, "unit session"); -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/rebuildfixturetest.cpp b/tests/components/proc/mobject/session/rebuildfixturetest.cpp index 170582dd6..6a12a3bfe 100644 --- a/tests/components/proc/mobject/session/rebuildfixturetest.cpp +++ b/tests/components/proc/mobject/session/rebuildfixturetest.cpp @@ -37,6 +37,7 @@ using std::string; using std::cout; +namespace proc { namespace mobject { namespace session { namespace test { @@ -90,4 +91,4 @@ namespace test { -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/scope-path-test.cpp b/tests/components/proc/mobject/session/scope-path-test.cpp index 80b578c9a..01c9b95c6 100644 --- a/tests/components/proc/mobject/session/scope-path-test.cpp +++ b/tests/components/proc/mobject/session/scope-path-test.cpp @@ -33,6 +33,7 @@ #include <string> +namespace proc { namespace mobject { namespace session { namespace test { @@ -404,4 +405,4 @@ namespace test { LAUNCHER (ScopePath_test, "unit session"); -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/scope-query-test.cpp b/tests/components/proc/mobject/session/scope-query-test.cpp index bce023e3d..3dd98d7ba 100644 --- a/tests/components/proc/mobject/session/scope-query-test.cpp +++ b/tests/components/proc/mobject/session/scope-query-test.cpp @@ -35,6 +35,7 @@ +namespace proc { namespace mobject { namespace session { namespace test { @@ -146,4 +147,4 @@ namespace test { LAUNCHER (ScopeQuery_test, "unit session"); -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/session-element-query-test.cpp b/tests/components/proc/mobject/session/session-element-query-test.cpp index 08e742af9..03c2fa1ef 100644 --- a/tests/components/proc/mobject/session/session-element-query-test.cpp +++ b/tests/components/proc/mobject/session/session-element-query-test.cpp @@ -34,6 +34,7 @@ +namespace proc { namespace mobject { namespace session { namespace test { @@ -146,4 +147,4 @@ namespace test { LAUNCHER (SessionElementQuery_test, "function session"); -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/session-element-tracker-test.cpp b/tests/components/proc/mobject/session/session-element-tracker-test.cpp index d5ba6ad8b..96370928c 100644 --- a/tests/components/proc/mobject/session/session-element-tracker-test.cpp +++ b/tests/components/proc/mobject/session/session-element-tracker-test.cpp @@ -33,6 +33,7 @@ +namespace proc { namespace mobject { namespace session { namespace test { @@ -120,7 +121,7 @@ namespace test { checksum = 0; { typedef Dummy AutoRegisteringDummy; - typedef lumiera::P<AutoRegisteringDummy> PDummy; + typedef P<AutoRegisteringDummy> PDummy; typedef lib::ElementTracker<Dummy> DummyRegistry; DummyRegistry trackedDummies; @@ -222,4 +223,4 @@ namespace test { -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/session-modify-parts-test.cpp b/tests/components/proc/mobject/session/session-modify-parts-test.cpp index 6d04accdd..42ea5329e 100644 --- a/tests/components/proc/mobject/session/session-modify-parts-test.cpp +++ b/tests/components/proc/mobject/session/session-modify-parts-test.cpp @@ -46,6 +46,7 @@ using std::cout; using std::set; +namespace proc { namespace mobject { namespace session { namespace test { @@ -318,7 +319,7 @@ namespace test { { TODO ("verify the commands issued by this test"); ////////////////////////TICKET #567 } - + }; @@ -327,4 +328,4 @@ namespace test { -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/session-service-access-test.cpp b/tests/components/proc/mobject/session/session-service-access-test.cpp index 0cdd56479..613d06be3 100644 --- a/tests/components/proc/mobject/session/session-service-access-test.cpp +++ b/tests/components/proc/mobject/session/session-service-access-test.cpp @@ -30,6 +30,7 @@ #include <string> +namespace proc { namespace mobject { namespace session { namespace test { @@ -44,7 +45,7 @@ namespace test { namespace { // what follows is a simulated (simplified) version // of the complete Session + SessionManager setup..... - + using lumiera::typelist::Types; using lumiera::typelist::InstantiateChained; @@ -348,4 +349,4 @@ namespace test { -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/session-structure-test.cpp b/tests/components/proc/mobject/session/session-structure-test.cpp index 551a85bd0..448209662 100644 --- a/tests/components/proc/mobject/session/session-structure-test.cpp +++ b/tests/components/proc/mobject/session/session-structure-test.cpp @@ -36,6 +36,7 @@ using std::string; using std::cout; +namespace proc { namespace mobject { namespace session { namespace test { @@ -138,4 +139,4 @@ namespace test { -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/sessionmanagertest.cpp b/tests/components/proc/mobject/session/sessionmanagertest.cpp index f36e3871c..add0e8493 100644 --- a/tests/components/proc/mobject/session/sessionmanagertest.cpp +++ b/tests/components/proc/mobject/session/sessionmanagertest.cpp @@ -33,6 +33,7 @@ using std::string; using std::cout; +namespace proc { namespace mobject { namespace session { namespace test { @@ -138,4 +139,4 @@ namespace test { -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/test-scope-invalid.hpp b/tests/components/proc/mobject/session/test-scope-invalid.hpp index 23435d144..4aec3296d 100644 --- a/tests/components/proc/mobject/session/test-scope-invalid.hpp +++ b/tests/components/proc/mobject/session/test-scope-invalid.hpp @@ -30,6 +30,7 @@ +namespace proc { namespace mobject { namespace session { namespace test { @@ -59,5 +60,5 @@ namespace test { -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test #endif diff --git a/tests/components/proc/mobject/session/test-scopes.cpp b/tests/components/proc/mobject/session/test-scopes.cpp index 04b09ae27..9ec6d4576 100644 --- a/tests/components/proc/mobject/session/test-scopes.cpp +++ b/tests/components/proc/mobject/session/test-scopes.cpp @@ -27,6 +27,7 @@ #include "proc/mobject/session/scope-query.hpp" +namespace proc { namespace mobject { namespace session { namespace test { @@ -53,7 +54,7 @@ namespace test { PPIdx index (SessionServiceMockIndex::install()); PMO& root = index->getRoot(); - + typedef PMO::ID ID; ID i1 = index->insert (p1, root); ID i2 = index->insert (p2, i1 ); @@ -91,4 +92,4 @@ namespace test { } -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/test-scopes.hpp b/tests/components/proc/mobject/session/test-scopes.hpp index 8b9b3a2fe..875f9f3c5 100644 --- a/tests/components/proc/mobject/session/test-scopes.hpp +++ b/tests/components/proc/mobject/session/test-scopes.hpp @@ -33,6 +33,7 @@ #include <tr1/memory> +namespace proc { namespace mobject { namespace session { namespace test { @@ -72,5 +73,5 @@ namespace test { -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test #endif diff --git a/tests/components/proc/mobject/session/testclip.cpp b/tests/components/proc/mobject/session/testclip.cpp index 74ee791c5..1f9141f3a 100644 --- a/tests/components/proc/mobject/session/testclip.cpp +++ b/tests/components/proc/mobject/session/testclip.cpp @@ -29,6 +29,7 @@ #include "lib/singleton.hpp" #include "lib/time/timevalue.hpp" +namespace proc { namespace mobject { namespace session { namespace test { @@ -90,4 +91,4 @@ namespace test { const Duration LENGTH_TestClip(Time(0,25,0,0)); //////TODO hard wired as of (1/10). See MediaFactory::operator() in media.cpp / mediaaccessmock.cpp -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/mobject/session/testclip.hpp b/tests/components/proc/mobject/session/testclip.hpp index c62034250..7bf9b28d4 100644 --- a/tests/components/proc/mobject/session/testclip.hpp +++ b/tests/components/proc/mobject/session/testclip.hpp @@ -34,6 +34,7 @@ using std::tr1::shared_ptr; using std::string; +namespace proc { namespace mobject { namespace session { namespace test { @@ -81,5 +82,5 @@ namespace test { extern const lib::time::Duration LENGTH_TestClip; -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test #endif diff --git a/tests/components/proc/mobject/session/testroot.hpp b/tests/components/proc/mobject/session/testroot.hpp index 69af08447..bbbd2d8e8 100644 --- a/tests/components/proc/mobject/session/testroot.hpp +++ b/tests/components/proc/mobject/session/testroot.hpp @@ -32,6 +32,7 @@ using lib::Symbol; +namespace proc { namespace mobject { namespace session { namespace test { @@ -47,5 +48,5 @@ namespace test { -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test #endif diff --git a/tests/components/proc/mobject/session/testsession1.hpp b/tests/components/proc/mobject/session/testsession1.hpp index 4b67fcd08..0aec5e90c 100644 --- a/tests/components/proc/mobject/session/testsession1.hpp +++ b/tests/components/proc/mobject/session/testsession1.hpp @@ -37,6 +37,7 @@ using std::string; using std::cout; +namespace proc { namespace mobject { namespace session { @@ -68,5 +69,5 @@ namespace session { const string SESSION1_CLIP("TODO: some sensible way to refer to a clip"); -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp b/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp index 92efe65f5..72ba5b60f 100644 --- a/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp +++ b/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp @@ -41,6 +41,7 @@ using std::string; using std::cout; +namespace proc { namespace mobject { namespace session { namespace test { @@ -260,7 +261,7 @@ namespace test { CHECK (num_sequences == sess->sequences.size()); CHECK (!contains (sess->timelines, aTimeline)); CHECK (!contains (sess->sequences, aSequence)); - + CHECK (1 == aTimeline.use_count()); CHECK (1 == aSequence.use_count()); #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #499 @@ -282,7 +283,7 @@ namespace test { // indirectly cause a new sequence to come to life... RTrack newTrack = sess->getRoot().attach (someTrack); // attach new Placement<Track> to root scope CHECK (newTrack != someTrack); // it's a new placement - + CHECK (num_sequences + 1 == sess->sequences.size()); // this root-attachment created a new sequence by sideeffect PSequence aSequence = sess->sequences[num_sequences]; CHECK (newTrack == aSequence->getTracks()); @@ -302,7 +303,7 @@ namespace test { CHECK (!assetM.known (aSequence->getID())); CHECK (num_sequences == sess->sequences.size()); CHECK (!contains (sess->sequences, aSequence)); - + CHECK (someTrack); CHECK (newTrack); #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #499 @@ -315,4 +316,4 @@ namespace test { -}}} // namespace mobject::session::test +}}}} // namespace proc::mobject::session::test diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index 1c566d198..e3c45cac1 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -52,9 +52,9 @@ namespace proc { namespace play { //using std::string; - using ::engine::BufferDescriptor; - using ::engine::test::TestFrame; - using ::engine::TrackingHeapBlockProvider; + using proc::engine::BufferDescriptor; + using proc::engine::test::TestFrame; + using proc::engine::TrackingHeapBlockProvider; //using std::vector; using std::tr1::shared_ptr; diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp index ae0319b6d..ac92dd62e 100644 --- a/tests/components/proc/play/output-slot-protocol-test.cpp +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -37,13 +37,13 @@ namespace proc { namespace play { -namespace test { +namespace test { // using lib::AllocationCluster; // using mobject::session::PEffect; - using ::engine::BuffHandle; - using ::engine::test::testData; - using ::engine::test::TestFrame; + using proc::engine::BuffHandle; + using proc::engine::test::testData; + using proc::engine::test::TestFrame; namespace { // Test fixture diff --git a/tests/lib/meta/config-flags-test.cpp b/tests/lib/meta/config-flags-test.cpp index 757f84d39..8d1222e4b 100644 --- a/tests/lib/meta/config-flags-test.cpp +++ b/tests/lib/meta/config-flags-test.cpp @@ -58,280 +58,281 @@ using std::endl; namespace lumiera { namespace typelist{ namespace test { + + + namespace { // internal definitions + + enum Cases + { ONE = 1 + , TWO + , THR + , FOU + , NUM_Cases = FOU + + , NOT_SET = 0 + }; + + + + /* === Test data === */ + typedef Config<> Conf0; + typedef Config<ONE> Conf1; + typedef Config<TWO> Conf2; + typedef Config<THR> Conf3; + typedef Config<TWO,FOU> Conf4; + + typedef Flags<ONE,THR>::Tuple Flags1; + typedef Flags<TWO,FOU>::Tuple Flags2; + typedef Types<Flags1,Flags2> SomeFlagsets; + + typedef Flags<ONE,TWO,THR,FOU>::Tuple AllFlags; + typedef CombineFlags<AllFlags> AllFlagCombinations; + + + + /** a type which is only partially defined, for some configs. + * In ConfigFlags_test::check_filter() we use the metaprogramming machinery + * to figure out all possible configs for which \c struct Maybe is defined. + * (for this to work, the "defined" specialisations need to provide a + * typedef \c is_defined ) + */ + template<class CONF> struct Maybe; + + struct Indeed { typedef Yes_t is_defined; }; + template<> struct Maybe<Conf1> : Indeed { enum{ CODE = 10 }; }; + template<> struct Maybe<Conf3> : Indeed { enum{ CODE = 30 }; }; + + template<char Fl> + struct Maybe<Config<TWO,Fl> > + { + typedef Yes_t is_defined; - - namespace { // internal definitions - - enum Cases - { ONE = 1 - , TWO - , THR - , FOU - , NUM_Cases = FOU - - , NOT_SET = 0 - }; - - - - /* === Test data === */ - typedef Config<> Conf0; - typedef Config<ONE> Conf1; - typedef Config<TWO> Conf2; - typedef Config<THR> Conf3; - typedef Config<TWO,FOU> Conf4; - - typedef Flags<ONE,THR>::Tuple Flags1; - typedef Flags<TWO,FOU>::Tuple Flags2; - typedef Types<Flags1,Flags2> SomeFlagsets; - - typedef Flags<ONE,TWO,THR,FOU>::Tuple AllFlags; - typedef CombineFlags<AllFlags> AllFlagCombinations; - - - - /** a type which is only partially defined, for some configs. - * In ConfigFlags_test::check_filter() we use the metaprogramming machinery - * to figure out all possible configs for which \c struct Maybe is defined. - * (for this to work, the "defined" specialisations need to provide a - * typedef \c is_defined ) - */ - template<class CONF> struct Maybe; - - struct Indeed { typedef Yes_t is_defined; }; - template<> struct Maybe<Conf1> : Indeed { enum{ CODE = 10 }; }; - template<> struct Maybe<Conf3> : Indeed { enum{ CODE = 30 }; }; - - template<char Fl> - struct Maybe<Config<TWO,Fl> > - { - typedef Yes_t is_defined; - - enum{ CODE = 20 + Fl }; - }; - - - } // (End) internal defs - + enum{ CODE = 20 + Fl }; + }; + + + } // (End) internal defs + #define PRINT_DELIMITER(TITLE) \ - cout << "__________________________\n" \ - "__________________________ " \ - << STRINGIFY(TITLE) << "\n"; - - - - - - /************************************************************************** - * @test check the handling of types representing a specific configuration. - * Basically, this is a bitset like feature, but working on types - * instead of runtime values. The Goal is to detect automatically - * all possible defined specialisations of some template based on - * such configuration-tuples. This allows us to assemble the glue code - * for pulling data from processing nodes out of small building blocks - * in all possible configurations. - */ - class ConfigFlags_test : public Test +cout << "__________________________\n" \ + "__________________________ " \ + << STRINGIFY(TITLE) << "\n"; + + + + + + /************************************************************************** + * @test check the handling of types representing a specific configuration. + * Basically, this is a bitset like feature, but working on types + * instead of runtime values. The Goal is to detect automatically + * all possible defined specialisations of some template based on + * such configuration-tuples. This allows us to assemble the glue code + * for pulling data from processing nodes out of small building blocks + * in all possible configurations. + */ + class ConfigFlags_test : public Test + { + virtual void run(Arg) { - virtual void run(Arg) - { - check_testdata (); - check_flags(); - check_instantiation (); - check_filter (); - check_FlagInfo (); - check_ConfigSelector (); - } + check_testdata (); + check_flags(); + check_instantiation (); + check_filter (); + check_FlagInfo (); + check_ConfigSelector (); + } + + + void check_testdata () + { + DISPLAY (Conf0); + DISPLAY (Conf1); + DISPLAY (Conf2); + DISPLAY (Conf3); + DISPLAY (Conf4); + + DISPLAY (AllFlags); + } + + + /** @test conversion between list-of-flags and a config-type in both directions */ + void check_flags () + { + PRINT_DELIMITER (check_flags()); + + typedef Config<TWO,FOU> Flags1; + typedef Flags<TWO,FOU> Flags2; + DISPLAY (Flags1); + DISPLAY (Flags2); + // should denote the same type + Flags1::Flags flags1 = Flags2::Tuple(); + Flags2::Tuple flags2 = flags1; + CHECK (1==sizeof(flags1)); // pure marker-type without content + CHECK (1==sizeof(flags2)); + + typedef DefineConfigByFlags<Node<Flag<ONE>,NullType> >::Config SimpleConfig_defined_by_Typelist; + DISPLAY (SimpleConfig_defined_by_Typelist); + + typedef DefineConfigByFlags<AllFlags>::Config AnotherConfig_defined_by_Typelist; + DISPLAY (AnotherConfig_defined_by_Typelist); + } + + + /** @test creates a predicate template (metafunction) returning true + * iff the template \c Maybe is defined for the configuration in question + */ + void check_instantiation () + { + #define CAN_INSTANTIATE(NAME) \ + cout << "defined " \ + << STRINGIFY(NAME) \ + << "? ---> " \ + << Instantiation<Maybe>::Test<NAME>::value << "\n"; + + PRINT_DELIMITER (check_instantiation()); + + CAN_INSTANTIATE (Conf0); + CAN_INSTANTIATE (Conf1); + CAN_INSTANTIATE (Conf2); + CAN_INSTANTIATE (Conf3); + CAN_INSTANTIATE (Conf4); + + typedef Config<THR,THR> Trash; + CAN_INSTANTIATE (Trash); + } + + + /** @test given a list of flag-tuples, we first create config-types out of them + * and then filter out those configs for which \c template Maybe is defined + */ + void check_filter () + { + PRINT_DELIMITER (check_filter()); + + DISPLAY (SomeFlagsets); + + typedef Apply<SomeFlagsets::List, DefineConfigByFlags> Configs_defined_by_Flagsets; + DISPLAY (Configs_defined_by_Flagsets); + + typedef Filter<Configs_defined_by_Flagsets::List,Instantiation<Maybe>::Test> Filter_possible_Configs; + DISPLAY (Filter_possible_Configs); - void check_testdata () - { - DISPLAY (Conf0); - DISPLAY (Conf1); - DISPLAY (Conf2); - DISPLAY (Conf3); - DISPLAY (Conf4); - - DISPLAY (AllFlags); - } - - - /** @test conversion between list-of-flags and a config-type in both directions */ - void check_flags () - { - PRINT_DELIMITER (check_flags()); - - typedef Config<TWO,FOU> Flags1; - typedef Flags<TWO,FOU> Flags2; - DISPLAY (Flags1); - DISPLAY (Flags2); - // should denote the same type - Flags1::Flags flags1 = Flags2::Tuple(); - Flags2::Tuple flags2 = flags1; - CHECK (1==sizeof(flags1)); // pure marker-type without content - CHECK (1==sizeof(flags2)); - - typedef DefineConfigByFlags<Node<Flag<ONE>,NullType> >::Config SimpleConfig_defined_by_Typelist; - DISPLAY (SimpleConfig_defined_by_Typelist); - - typedef DefineConfigByFlags<AllFlags>::Config AnotherConfig_defined_by_Typelist; - DISPLAY (AnotherConfig_defined_by_Typelist); - } - - - /** @test creates a predicate template (metafunction) returning true - * iff the template \c Maybe is defined for the configuration in question - */ - void check_instantiation () - { - #define CAN_INSTANTIATE(NAME) \ - cout << "defined " \ - << STRINGIFY(NAME) \ - << "? ---> " \ - << Instantiation<Maybe>::Test<NAME>::value << "\n"; - - PRINT_DELIMITER (check_instantiation()); - - CAN_INSTANTIATE (Conf0); - CAN_INSTANTIATE (Conf1); - CAN_INSTANTIATE (Conf2); - CAN_INSTANTIATE (Conf3); - CAN_INSTANTIATE (Conf4); - - typedef Config<THR,THR> Trash; - CAN_INSTANTIATE (Trash); - } - - - /** @test given a list of flag-tuples, we first create config-types out of them - * and then filter out those configs for which \c template Maybe is defined - */ - void check_filter () - { - PRINT_DELIMITER (check_filter()); - - DISPLAY (SomeFlagsets); - - typedef Apply<SomeFlagsets::List, DefineConfigByFlags> Configs_defined_by_Flagsets; - DISPLAY (Configs_defined_by_Flagsets); - - typedef Filter<Configs_defined_by_Flagsets::List,Instantiation<Maybe>::Test> Filter_possible_Configs; - DISPLAY (Filter_possible_Configs); - - - DISPLAY (AllFlagCombinations); - typedef Apply<AllFlagCombinations::List, DefineConfigByFlags> ListAllConfigs; - DISPLAY (ListAllConfigs); - - typedef Filter<ListAllConfigs::List,Instantiation<Maybe>::Test> Filter_all_possible_Configs; - DISPLAY (Filter_all_possible_Configs); - } - - - - struct TestVisitor - { - string result; ///< metafunction result - - TestVisitor() : result ("TestVisitor application:\n") {} - - /* === visitation interface === */ - typedef string Ret; - - Ret done() {return result; } - - template<class CONF> - void - visit (ulong code) - { - result += str (format ("visit(code=%u) -->%s\n") - % code % Printer<CONF>::print() ); - } - }; - - /** @test FlagInfo metafunction, which takes as argument a list-of-flags - * as well as a list-of-lists-of-flags and especially allows to - * apply a visitor object to the latter - */ - void check_FlagInfo() - { - PRINT_DELIMITER (check_FlagInfo()); - - DISPLAY (Flags1); - cout << "max bit : " << FlagInfo<Flags1>::BITS <<"\n"; - cout << "binary code: " << FlagInfo<Flags1>::CODE <<"\n"; - - typedef Apply<SomeFlagsets::List, DefineConfigByFlags> SomeConfigs; - DISPLAY (SomeConfigs); - cout << "max bit in [SomeConfigs] : " << FlagInfo<SomeConfigs::List>::BITS <<"\n"; - - TestVisitor visitor; - cout << FlagInfo<SomeConfigs::List>::accept (visitor); - } - + DISPLAY (AllFlagCombinations); + typedef Apply<AllFlagCombinations::List, DefineConfigByFlags> ListAllConfigs; + DISPLAY (ListAllConfigs); + typedef Filter<ListAllConfigs::List,Instantiation<Maybe>::Test> Filter_all_possible_Configs; + DISPLAY (Filter_all_possible_Configs); + } + + + + struct TestVisitor + { + string result; ///< metafunction result + + TestVisitor() : result ("TestVisitor application:\n") {} + + /* === visitation interface === */ + typedef string Ret; + + Ret done() {return result; } + template<class CONF> - struct TestFactory + void + visit (ulong code) { - uint operator() () { return offset_ + Maybe<CONF>::CODE; } - TestFactory(long o) : offset_(o) {} - - private: - long offset_; - }; + result += str (format ("visit(code=%u) -->%s\n") + % code % Printer<CONF>::print() ); + } + }; + + /** @test FlagInfo metafunction, which takes as argument a list-of-flags + * as well as a list-of-lists-of-flags and especially allows to + * apply a visitor object to the latter + */ + void check_FlagInfo() + { + PRINT_DELIMITER (check_FlagInfo()); + + DISPLAY (Flags1); + cout << "max bit : " << FlagInfo<Flags1>::BITS <<"\n"; + cout << "binary code: " << FlagInfo<Flags1>::CODE <<"\n"; + + typedef Apply<SomeFlagsets::List, DefineConfigByFlags> SomeConfigs; + DISPLAY (SomeConfigs); + cout << "max bit in [SomeConfigs] : " << FlagInfo<SomeConfigs::List>::BITS <<"\n"; + + TestVisitor visitor; + cout << FlagInfo<SomeConfigs::List>::accept (visitor); + } + + + template<class CONF> + struct TestFactory + { + uint operator() () { return offset_ + Maybe<CONF>::CODE; } + TestFactory(long o) : offset_(o) {} + + private: + long offset_; + }; + + + /** @test use the ConfigSelector template to build a set of factories, + * based on a set of configurations. Then invoke the appropriate + * factory by specifying the configuration bit code + */ + void check_ConfigSelector() + { + PRINT_DELIMITER (check_ConfigSelector()); + + typedef Apply<AllFlagCombinations::List, DefineConfigByFlags> ListAllConfigs; + typedef Filter<ListAllConfigs::List,Instantiation<Maybe>::Test> Possible_Configs; + DISPLAY (Possible_Configs); + + using proc::engine::config::ConfigSelector; + typedef ConfigSelector< TestFactory // Factory template + , uint(void) // Factory function type + , long // common ctor argument + > TestFactorySelector; + + const long offset = 1000; // parameter fed to all TestFactory ctors + TestFactorySelector testConfigSelector (Possible_Configs::List(), offset); - /** @test use the ConfigSelector template to build a set of factories, - * based on a set of configurations. Then invoke the appropriate - * factory by specifying the configuration bit code - */ - void check_ConfigSelector() + #define INVOKE_CONFIG_SELECTOR(CODE) \ + cout << " Flag-code = " << CODE \ + << " ConfigSelector() ---> " \ + << testConfigSelector[CODE] () << "\n"; + + INVOKE_CONFIG_SELECTOR (2); + INVOKE_CONFIG_SELECTOR (12); + INVOKE_CONFIG_SELECTOR (20); + INVOKE_CONFIG_SELECTOR (4); + INVOKE_CONFIG_SELECTOR (8); + + try { - PRINT_DELIMITER (check_ConfigSelector()); - - typedef Apply<AllFlagCombinations::List, DefineConfigByFlags> ListAllConfigs; - typedef Filter<ListAllConfigs::List,Instantiation<Maybe>::Test> Possible_Configs; - DISPLAY (Possible_Configs); - - typedef engine::config::ConfigSelector< TestFactory // Factory template - , uint(void) // Factory function type - , long // common ctor argument - > TestFactorySelector; - - const long offset = 1000; // parameter fed to all TestFactory ctors - TestFactorySelector testConfigSelector (Possible_Configs::List(), offset); - - - #define INVOKE_CONFIG_SELECTOR(CODE) \ - cout << " Flag-code = " << CODE \ - << " ConfigSelector() ---> " \ - << testConfigSelector[CODE] () << "\n"; - - INVOKE_CONFIG_SELECTOR (2); - INVOKE_CONFIG_SELECTOR (12); - INVOKE_CONFIG_SELECTOR (20); - INVOKE_CONFIG_SELECTOR (4); - INVOKE_CONFIG_SELECTOR (8); - - try - { - INVOKE_CONFIG_SELECTOR (23); - NOTREACHED (); - } - catch (lumiera::error::Invalid& err) - { - cout << err.what() << "\n"; - lumiera_error (); // reset error flag - } + INVOKE_CONFIG_SELECTOR (23); + NOTREACHED (); } - - - }; - - - /** Register this test class... */ - LAUNCHER (ConfigFlags_test, "unit common"); - + catch (lumiera::error::Invalid& err) + { + cout << err.what() << "\n"; + lumiera_error (); // reset error flag + } + } + }; + + + /** Register this test class... */ + LAUNCHER (ConfigFlags_test, "unit common"); + + + }}} // namespace lumiera::typelist::test diff --git a/tests/lib/meta/iterable-classification-test.cpp b/tests/lib/meta/iterable-classification-test.cpp index 0066c33de..9a8a21aa1 100644 --- a/tests/lib/meta/iterable-classification-test.cpp +++ b/tests/lib/meta/iterable-classification-test.cpp @@ -45,8 +45,8 @@ namespace lib { namespace meta{ namespace test{ - using mobject::session::Effect; - using mobject::session::ScopeQuery; + using proc::mobject::session::Effect; + using proc::mobject::session::ScopeQuery; typedef lib::time::TimeVar Time; using std::cout; diff --git a/tests/common/ref-array-test.cpp b/tests/lib/ref-array-test.cpp similarity index 100% rename from tests/common/ref-array-test.cpp rename to tests/lib/ref-array-test.cpp diff --git a/tests/lib/streamtypebasicstest.cpp b/tests/lib/streamtypebasicstest.cpp index bf5f191b6..be18f6e59 100644 --- a/tests/lib/streamtypebasicstest.cpp +++ b/tests/lib/streamtypebasicstest.cpp @@ -33,89 +33,87 @@ using ::test::Test; using util::isnil; -namespace lumiera { - namespace test_format { - - using control::STypeManager; - typedef StreamType const& SType; - typedef StreamType::ImplFacade const& ImplType; - - - /******************************************************************* - * @test check the basic workings of the stream type handling. - * create some stream implementation data, build a - * StreamType::ImplFacade from this, and derive a prototype - * and a full StreamType based on this information. - */ - class StreamTypeBasics_test : public Test - { - virtual void - run (Arg arg) - { - ImplType iType = buildImplType (); - basicImplTypeProperties (iType); - - SType type = extend2fullType (iType); - basicStreamTypeProperties (type, iType); - } - - ImplType - buildImplType () - { - STypeManager& typeManager = STypeManager::instance(); - - gavl_video_format_t rawType = test_createRawType(); - ImplType iTy (typeManager.getImpl (GAVL, rawType)); - - UNIMPLEMENTED ("at least preliminary implementation of the MediaImplLib interface for lib GAVL"); - - TODO ("how to do a simple consistency check on the returned ImplFacade? can we re-create the GAVL frame type?"); - CHECK (GAVL==iTy.libraryID); - return iTy; - } - - void - basicImplTypeProperties (ImplType refType) - { - ImplType iTy2 = test_createImplType (); - CHECK (iTy2==refType); - CHECK (refType==iTy2); - TODO ("add equality comparable concept to the ImplType class"); - - CHECK (StreamType::VIDEO==refType.getKind()); - UNIMPLEMENTED ("get a lib descriptor"); - UNIMPLEMENTED ("check the lib of the type"); - UNIMPLEMENTED ("compare two types"); - } - - SType - extend2fullType (ImplType iTy) - { - return STypeManager::instance().getType(iTy); - } - - void - basicStreamTypeProperties (SType type, ImplType iTy) - { - CHECK (type.implType); - CHECK (iTy==(*type.implType)); /////////////TODO: really by ptr??? - CHECK (&iTy==type.implType); // actually using the same object (in the registry) - - CHECK (!isnil (type.prototype.id)); - CHECK (StreamType::VIDEO==type.prototype.kind); - CHECK (StreamType::VIDEO==type.implType->getKind()); - - CHECK (type.implType->canConvert(iTy)); // of course... they are actually the same - CHECK (iTy.canConvert(type)); // because it's based on the same impl type - - CHECK (StreamType::RAW==type.intentionTag); - } - }; - - LAUNCHER (StreamTypeBasics_test, "unit common"); - - - } // namespace test_format - -} // namespace lumiera +namespace proc { +namespace test_format { + + using control::STypeManager; + typedef StreamType const& SType; + typedef StreamType::ImplFacade const& ImplType; + + + /******************************************************************* + * @test check the basic workings of the stream type handling. + * create some stream implementation data, build a + * StreamType::ImplFacade from this, and derive a prototype + * and a full StreamType based on this information. + */ + class StreamTypeBasics_test : public Test + { + virtual void + run (Arg arg) + { + ImplType iType = buildImplType (); + basicImplTypeProperties (iType); + + SType type = extend2fullType (iType); + basicStreamTypeProperties (type, iType); + } + + ImplType + buildImplType () + { + STypeManager& typeManager = STypeManager::instance(); + + gavl_video_format_t rawType = test_createRawType(); + ImplType iTy (typeManager.getImpl (GAVL, rawType)); + + UNIMPLEMENTED ("at least preliminary implementation of the MediaImplLib interface for lib GAVL"); + + TODO ("how to do a simple consistency check on the returned ImplFacade? can we re-create the GAVL frame type?"); + CHECK (GAVL==iTy.libraryID); + return iTy; + } + + void + basicImplTypeProperties (ImplType refType) + { + ImplType iTy2 = test_createImplType (); + CHECK (iTy2==refType); + CHECK (refType==iTy2); + TODO ("add equality comparable concept to the ImplType class"); + + CHECK (StreamType::VIDEO==refType.getKind()); + UNIMPLEMENTED ("get a lib descriptor"); + UNIMPLEMENTED ("check the lib of the type"); + UNIMPLEMENTED ("compare two types"); + } + + SType + extend2fullType (ImplType iTy) + { + return STypeManager::instance().getType(iTy); + } + + void + basicStreamTypeProperties (SType type, ImplType iTy) + { + CHECK (type.implType); + CHECK (iTy==(*type.implType)); /////////////TODO: really by ptr??? + CHECK (&iTy==type.implType); // actually using the same object (in the registry) + + CHECK (!isnil (type.prototype.id)); + CHECK (StreamType::VIDEO==type.prototype.kind); + CHECK (StreamType::VIDEO==type.implType->getKind()); + + CHECK (type.implType->canConvert(iTy)); // of course... they are actually the same + CHECK (iTy.canConvert(type)); // because it's based on the same impl type + + CHECK (StreamType::RAW==type.intentionTag); + } + }; + + LAUNCHER (StreamTypeBasics_test, "unit common"); + + +}} // namespace proc::test_format diff --git a/tests/lib/streamtypelifecycletest.cpp b/tests/lib/streamtypelifecycletest.cpp index 1c1466407..7c0dc63b3 100644 --- a/tests/lib/streamtypelifecycletest.cpp +++ b/tests/lib/streamtypelifecycletest.cpp @@ -27,88 +27,88 @@ #include "proc/control/stypemanager.hpp" #include "proc/mobject/session.hpp" //#include "teststreamtypes.hpp" +#include "include/lifecycle.h" #include <iostream> using std::cout; -namespace lumiera { - namespace test_format { - - using mobject::Session; - using control::STypeManager; - using control::ON_STREAMTYPES_RESET; - - - //////////TODO define a dummy-type-info here - //////////TODO - - void - setup_basicDummyTypeInfo () - { - UNIMPLEMENTED ("setup basic dummy-type-info"); - } - - namespace // enrol this basic setup to be triggered when the type system is reset - { - LifecycleHook _schedule_at_reset (ON_STREAMTYPES_RESET, &setup_basicDummyTypeInfo); - } - - - - /******************************************************************* - * @test check the stream type registration lifecycle. - * Any internal or external component (plugin) can extend - * the Proc Layer's registry of media stream types. - * There is a basic pristine set of type information, which is - * restored automatically everytime the STypeManager is reset, - * which in turn happenes before loading a (new) Session. - */ - class StreamTypeLifecycle_test : public Test - { - virtual void - run (Arg arg) - { - check_pristineState (); - register_additional_TypeInfo (); - check_pristineState (); - } - - - /** @test this test defines a new (dummy) type info - * and schedules it for setop in the pristine state; - * check this info is actually present after resetting - * the stream type manager, while other additional info - * \em not scheduled in this manner is not present - * in this state - */ - void - check_pristineState () - { - Session::current.reset(); - TODO ("verify the test-dummy basic type info is present now..."); - TODO ("verify the additional type info is *not* present"); - } - - /** @test use the stream type manager to register additional - * type info and verify it is used in type resolution. - */ - void - register_additional_TypeInfo () - { - TODO ("verify the additional type info is *not* present"); - - STypeManager& typeManager = STypeManager::instance(); - TODO ("use the registration facility to add additional type info"); - TODO ("verify the additional type info to be present now..."); - } - }; - - LAUNCHER (StreamTypeLifecycle_test, "unit common"); - - - } // namespace test_format +namespace proc { +namespace test_format { -} // namespace lumiera + using mobject::Session; + using control::STypeManager; + using control::ON_STREAMTYPES_RESET; + using lumiera::LifecycleHook; + + + //////////TODO define a dummy-type-info here + //////////TODO + + void + setup_basicDummyTypeInfo () + { + UNIMPLEMENTED ("setup basic dummy-type-info"); + } + + namespace // enrol this basic setup to be triggered when the type system is reset + { + LifecycleHook _schedule_at_reset (ON_STREAMTYPES_RESET, &setup_basicDummyTypeInfo); + } + + + + /******************************************************************* + * @test check the stream type registration lifecycle. + * Any internal or external component (plugin) can extend + * the Proc Layer's registry of media stream types. + * There is a basic pristine set of type information, which is + * restored automatically everytime the STypeManager is reset, + * which in turn happenes before loading a (new) Session. + */ + class StreamTypeLifecycle_test : public Test + { + virtual void + run (Arg arg) + { + check_pristineState (); + register_additional_TypeInfo (); + check_pristineState (); + } + + + /** @test this test defines a new (dummy) type info + * and schedules it for setop in the pristine state; + * check this info is actually present after resetting + * the stream type manager, while other additional info + * \em not scheduled in this manner is not present + * in this state + */ + void + check_pristineState () + { + Session::current.reset(); + TODO ("verify the test-dummy basic type info is present now..."); + TODO ("verify the additional type info is *not* present"); + } + + /** @test use the stream type manager to register additional + * type info and verify it is used in type resolution. + */ + void + register_additional_TypeInfo () + { + TODO ("verify the additional type info is *not* present"); + + STypeManager& typeManager = STypeManager::instance(); + TODO ("use the registration facility to add additional type info"); + TODO ("verify the additional type info to be present now..."); + } + }; + + LAUNCHER (StreamTypeLifecycle_test, "unit common"); + + +}} // namespace proc::test_format diff --git a/tests/lib/test/test-helper-test.cpp b/tests/lib/test/test-helper-test.cpp index c8fdb8a4b..498331b78 100644 --- a/tests/lib/test/test-helper-test.cpp +++ b/tests/lib/test/test-helper-test.cpp @@ -83,7 +83,7 @@ namespace test{ { std::cout << "Displaying types and sizes....\n"; - typedef Wrmrmpft<Murpf> Wrmpf1; + typedef Wrmrmpft<Murpf> Wrmpf1; typedef Wrmrmpft<char[2]> Wrmpf2; typedef Wrmrmpft<char[3]> Wrmpf3; @@ -110,7 +110,7 @@ namespace test{ } - + void checkGarbageStr() diff --git a/tests/lib/teststreamtypes.hpp b/tests/lib/teststreamtypes.hpp index 11aec0fd6..db22fe9cb 100644 --- a/tests/lib/teststreamtypes.hpp +++ b/tests/lib/teststreamtypes.hpp @@ -35,62 +35,60 @@ extern "C" { } -namespace lumiera { - namespace test_format { +namespace proc { +namespace test_format { - namespace { // constants used to parametrize tests + namespace { // constants used to parametrise tests + + const int TEST_IMG_WIDTH = 40; + const int TEST_IMG_HEIGHT = 30; - const int TEST_IMG_WIDTH = 40; - const int TEST_IMG_HEIGHT = 30; - - const int TEST_FRAME_DUR = GAVL_TIME_SCALE / 25; - } - - Symbol GAVL = "GAVL"; + const int TEST_FRAME_DUR = GAVL_TIME_SCALE / 25; + } + + Symbol GAVL = "GAVL"; + + + + /** Helper: create an raw GAVL type descriptor + * usable for generating a Lumiera StreamType + */ + inline gavl_video_format_t + test_createRawType () + { + gavl_video_format_t type; + type.pixelformat = GAVL_RGB_24; + type.interlace_mode = GAVL_INTERLACE_NONE; + type.framerate_mode = GAVL_FRAMERATE_CONSTANT; + type.chroma_placement = GAVL_CHROMA_PLACEMENT_DEFAULT; + type.image_width = TEST_IMG_WIDTH; // Width of the image in pixels + type.image_height = TEST_IMG_WIDTH; // Height of the image in pixels + type.frame_width = TEST_IMG_WIDTH; // Width of the frame buffer in pixels, might be larger than image_width + type.frame_height = TEST_IMG_WIDTH; // Height of the frame buffer in pixels, might be larger than image_height - /** Helper: create an raw GAVL type descriptor - * usable for generating a Lumiera StreamType - */ - inline gavl_video_format_t - test_createRawType () - { - gavl_video_format_t type; - - type.pixelformat = GAVL_RGB_24; - type.interlace_mode = GAVL_INTERLACE_NONE; - type.framerate_mode = GAVL_FRAMERATE_CONSTANT; - type.chroma_placement = GAVL_CHROMA_PLACEMENT_DEFAULT; - - type.image_width = TEST_IMG_WIDTH; // Width of the image in pixels - type.image_height = TEST_IMG_WIDTH; // Height of the image in pixels - type.frame_width = TEST_IMG_WIDTH; // Width of the frame buffer in pixels, might be larger than image_width - type.frame_height = TEST_IMG_WIDTH; // Height of the frame buffer in pixels, might be larger than image_height - - type.pixel_width = 1; // Relative width of a pixel (pixel aspect ratio is pixel_width/pixel_height) - type.pixel_height = 1; // Relative height of a pixel (pixel aspect ratio is pixel_width/pixel_height) - - type.frame_duration = TEST_FRAME_DUR; // Duration of a frame in timescale tics. - type.timescale = GAVL_TIME_SCALE; // Timescale in tics per second (is defined to be 1000000 as of 9/2008) - - return type; - } + type.pixel_width = 1; // Relative width of a pixel (pixel aspect ratio is pixel_width/pixel_height) + type.pixel_height = 1; // Relative height of a pixel (pixel aspect ratio is pixel_width/pixel_height) + type.frame_duration = TEST_FRAME_DUR; // Duration of a frame in timescale tics. + type.timescale = GAVL_TIME_SCALE; // Timescale in tics per second (is defined to be 1000000 as of 9/2008) - /** Helper: create an implementation frame - * and build the corresponding streamtype - */ - inline StreamType::ImplFacade const& - test_createImplType () - { - gavl_video_format_t rawType = test_createRawType(); - return control::STypeManager::instance().getImpl (GAVL, rawType); - } - - - - } // namespace test_format - -} // namespace lumiera + return type; + } + + + /** Helper: create an implementation frame + * and build the corresponding streamtype + */ + inline StreamType::ImplFacade const& + test_createImplType () + { + gavl_video_format_t rawType = test_createRawType(); + return control::STypeManager::instance().getImpl (GAVL, rawType); + } + + + +}} // namespace proc::test_format #endif diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp index c40ff1d54..f949d49be 100644 --- a/tests/lib/time/time-control-test.cpp +++ b/tests/lib/time/time-control-test.cpp @@ -53,7 +53,7 @@ namespace test{ namespace error = lumiera::error; using lib::ScopedHolder; - using asset::meta::TimeGrid; + using proc::asset::meta::TimeGrid; using lumiera::typelist::Types; using lumiera::typelist::InstantiateChainedCombinations; using error::LUMIERA_ERROR_UNCONNECTED; diff --git a/tests/lib/time/time-formats-test.cpp b/tests/lib/time/time-formats-test.cpp index d85649201..0222a1eeb 100644 --- a/tests/lib/time/time-formats-test.cpp +++ b/tests/lib/time/time-formats-test.cpp @@ -47,7 +47,7 @@ namespace lib { namespace time{ namespace test{ - using asset::meta::TimeGrid; + using proc::asset::meta::TimeGrid; using format::Frames; using format::Smpte; diff --git a/tests/lib/time/time-mutation-test.cpp b/tests/lib/time/time-mutation-test.cpp index 7ad09d379..2a4837fcb 100644 --- a/tests/lib/time/time-mutation-test.cpp +++ b/tests/lib/time/time-mutation-test.cpp @@ -44,7 +44,7 @@ namespace lib { namespace time{ namespace test{ - using asset::meta::TimeGrid; + using proc::asset::meta::TimeGrid; namespace { inline string diff --git a/tests/lib/time/time-parsing-test.cpp b/tests/lib/time/time-parsing-test.cpp index 812c893d7..24ada115c 100644 --- a/tests/lib/time/time-parsing-test.cpp +++ b/tests/lib/time/time-parsing-test.cpp @@ -37,7 +37,7 @@ namespace lib { namespace time{ namespace test{ - using asset::meta::TimeGrid; + using proc::asset::meta::TimeGrid; using format::LUMIERA_ERROR_INVALID_TIMECODE; diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp index a22988f1b..32b57a355 100644 --- a/tests/lib/time/time-quantisation-test.cpp +++ b/tests/lib/time/time-quantisation-test.cpp @@ -47,7 +47,7 @@ namespace lib { namespace time{ namespace test{ - using asset::meta::TimeGrid; + using proc::asset::meta::TimeGrid; /******************************************************** From b2d6074097ccd7f76bf03d466ad1126aab44adc0 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 2 Dec 2011 21:34:29 +0100 Subject: [PATCH 288/496] cleanup test includes --- src/common/plugin.c | 3 +- src/common/plugin_dynlib.c | 2 +- {tests => src/lib/test}/test.h | 29 ++++++++---- tests/31plugin.tests | 17 +++++-- tests/SConscript | 11 +++-- tests/backend/test-filedescriptors.c | 2 +- tests/backend/test-filehandles.c | 2 +- tests/backend/test-fileheader.c | 2 +- tests/backend/test-filemmap.c | 2 +- tests/backend/test-resourcecollector.c | 2 +- tests/backend/test-threadpool.c | 2 +- tests/backend/test-threads.c | 2 +- tests/common/test-config.c | 2 +- tests/common/test-interfaces.c | 46 +++++++++++++++---- .../interface/say_hello.h} | 0 tests/lib/helloworldtest.cpp | 15 ++++-- tests/library/test-llist.c | 2 +- tests/library/test-locking.c | 2 +- tests/library/test-mpool.c | 2 +- tests/library/test-psplay.c | 2 +- tests/library/test-safeclib.c | 2 +- tests/library/test-slist.c | 2 +- tests/library/test-time.c | 2 +- tests/lumiera/DIR_INFO | 1 - .../example_plugin.c | 2 +- .../plugin/test-cpp-plugin/example_plugin.cpp | 34 +++++++------- 26 files changed, 123 insertions(+), 67 deletions(-) rename {tests => src/lib/test}/test.h (71%) rename tests/{common/hello_interface.h => include/interface/say_hello.h} (100%) delete mode 100644 tests/lumiera/DIR_INFO rename tests/plugin/{examplepluginc => test-c-plugin}/example_plugin.c (99%) diff --git a/src/common/plugin.c b/src/common/plugin.c index 373e405e7..a44592dc8 100644 --- a/src/common/plugin.c +++ b/src/common/plugin.c @@ -248,7 +248,7 @@ lumiera_plugin_discover (LumieraPlugin (*callback_load)(const char* plugin), LumieraPlugin lumiera_plugin_load (const char* plugin) { - TRACE (pluginloader_dbg); + TRACE (pluginloader_dbg, "plugin=%s", plugin); /* dispatch on ext, call the registered function */ const char* ext = strrchr (plugin, '.'); @@ -417,6 +417,7 @@ lumiera_plugin_delete_fn (PSplaynode node) LUMIERA_INTERFACE_CAST(lumieraorg__plugin, 0) self->plugin; lumiera_interfaceregistry_bulkremove_interfaces (handle->plugin_interfaces ()); } + TRACE (pluginloader_dbg, "unloading plugin/module %s", self->name); itr->lumiera_plugin_unload_fn (self); break; } diff --git a/src/common/plugin_dynlib.c b/src/common/plugin_dynlib.c index 7e8618628..5e9aa30a4 100644 --- a/src/common/plugin_dynlib.c +++ b/src/common/plugin_dynlib.c @@ -34,7 +34,7 @@ LumieraPlugin lumiera_plugin_load_DYNLIB (const char* name) { - TRACE (pluginloader_dbg); + TRACE (pluginloader_dbg, "load DYNLIB: %s", name); REQUIRE (name); LumieraPlugin self = lumiera_plugin_new (name); LumieraInterface plugin = NULL; diff --git a/tests/test.h b/src/lib/test/test.h similarity index 71% rename from tests/test.h rename to src/lib/test/test.h index 1fc00955c..8d45b984f 100644 --- a/tests/test.h +++ b/src/lib/test/test.h @@ -1,8 +1,8 @@ /* - test.h - macros for running tests + TEST.h - support macros for plain-C tests - Copyright (C) - 2008, 2009, 2010, Christian Thaeter <ct@pipapo.org> + Copyright (C) Lumiera.org + 2008, 2010 Christian Thaeter <ct@pipapo.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -17,10 +17,21 @@ 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 TEST_H -#define TEST_H +/** @file test.h + ** Helpers and support macros for defining test executables in C. + ** These macros provide some building blocks to assemble a \c main() function, + ** which checks a test name parameter and invokes the matching embedded code block. + ** + ** @see test-mpool.c C test example + ** @see HelloWorld_test C++ test example + */ + + +#ifndef LIB_TEST_TEST_H +#define LIB_TEST_TEST_H #include <stdio.h> #include <stdlib.h> @@ -51,10 +62,10 @@ main (int argc, const char** argv) \ fprintf (stderr, " "#name" (planned)\n"); \ else if (!++testcnt) -#define TESTS_END \ - if (!testcnt && argc !=1) \ - fprintf (stderr,"no such test: %s\n", argv[1]); \ - return ret; \ +#define TESTS_END \ + if (!testcnt && argc !=1) \ + fprintf (stderr,"no such test: %s\n", argv[1]); \ + return ret; \ } diff --git a/tests/31plugin.tests b/tests/31plugin.tests index ace85086a..9f59426d0 100644 --- a/tests/31plugin.tests +++ b/tests/31plugin.tests @@ -10,7 +10,8 @@ export LUMIERA_PLUGIN_PATH=modules export LUMIERA_CONFIG_PATH=./ TEST "discovering plugins" plugin_discover <<END -out: found plugin: modules/examplepluginc.lum +out: found plugin: modules/test-c-plugin.lum +out: found plugin: modules/test-cpp-plugin.lum return: 0 END @@ -20,7 +21,7 @@ out: plugin discovered after unload: \(nil\) return: 0 END -TEST "C plugin test" plugin_examplepluginc <<END +TEST "C plugin test" plugin_exampleplugin <<END out: Hallo Welt! out: Tschuess Welt! out: Hello World! @@ -28,7 +29,7 @@ out: Bye World! return: 0 END -TEST "C plugin test, nested" plugin_examplepluginc_nested <<END +TEST "C plugin test, nested" plugin_exampleplugin_nested <<END out: config path is: \./ out: plugin path is: modules out: Hallo Welt! @@ -38,5 +39,15 @@ out: Tschuess Welt! return: 0 END +TEST "C++ plugin test" plugin_exampleplugin_cpp <<END +out: opened 0x.+ global interfaces 0x... +out: Hallo Welt! +out: Tschüss schnöde Welt! +out: Hello World! +out: Bye Vale of Tears! +out: dying +return: 0 +END + unset LUMIERA_PLUGIN_PATH unset LUMIERA_CONFIG_PATH diff --git a/tests/SConscript b/tests/SConscript index 8a09b547a..dd3584595 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -13,11 +13,10 @@ from Buildhelper import globRootdirs from Buildhelper import createPlugins Import('env','artifacts','core') - -# temp fix to add test.h -- wouldn't it be better to put this header be into src/lib ? env = env.Clone() -env.Append(CPPPATH='#/.') # add Rootdir to Includepath, so test/test.h is found -# temp fix------------- +env.Append(CPPPATH='include') # additional headers for tests + + def testExecutable(env,tree, exeName=None, obj=None): """ declare all targets needed to create a standalone @@ -25,7 +24,7 @@ def testExecutable(env,tree, exeName=None, obj=None): @note this tree uses separate Environment/Includepath """ env = env.Clone() - env.Append(CPPPATH=tree) # add Subdir to Includepath + env.Append(CPPPATH=tree) # add subdir to Includepath tree = env.subst(tree) # expand Construction Vars if obj: obj = [path.join(tree,name) for name in obj] @@ -40,6 +39,8 @@ def testCollection(env,dir): """ treat a Directory containing a collection of standalone tests. Link each of them into an independent executable """ + env = env.Clone() + env.Append(CPPPATH=dir) # add subdir to Includepath srcpatt = ['test-*.c'] exeName = lambda p: path.basename(path.splitext(p)[0]) buildIt = lambda p: env.Program(exeName(p), [p] + core) diff --git a/tests/backend/test-filedescriptors.c b/tests/backend/test-filedescriptors.c index b10f5845e..b55b2f57c 100644 --- a/tests/backend/test-filedescriptors.c +++ b/tests/backend/test-filedescriptors.c @@ -24,7 +24,7 @@ #include "backend/backend.h" #include "backend/filedescriptor.h" -#include "tests/test.h" +#include "lib/test/test.h" TESTS_BEGIN diff --git a/tests/backend/test-filehandles.c b/tests/backend/test-filehandles.c index 7e13c8c7f..7f95b0b88 100644 --- a/tests/backend/test-filehandles.c +++ b/tests/backend/test-filehandles.c @@ -25,7 +25,7 @@ #include "backend/backend.h" #include "backend/filehandlecache.h" -#include "tests/test.h" +#include "lib/test/test.h" TESTS_BEGIN diff --git a/tests/backend/test-fileheader.c b/tests/backend/test-fileheader.c index b19ba5204..b1842537a 100644 --- a/tests/backend/test-fileheader.c +++ b/tests/backend/test-fileheader.c @@ -22,7 +22,7 @@ #include "backend/backend.h" #include "backend/fileheader.h" -#include "tests/test.h" +#include "lib/test/test.h" #include "backend/filehandlecache.h" extern LumieraFilehandlecache lumiera_fhcache; diff --git a/tests/backend/test-filemmap.c b/tests/backend/test-filemmap.c index bd1b4ce7a..388eb59b1 100644 --- a/tests/backend/test-filemmap.c +++ b/tests/backend/test-filemmap.c @@ -31,7 +31,7 @@ #include "backend/mmapings.h" #include "backend/mmap.h" -#include "tests/test.h" +#include "lib/test/test.h" #include <unistd.h> #include <string.h> diff --git a/tests/backend/test-resourcecollector.c b/tests/backend/test-resourcecollector.c index 6629ea2f8..4273e8404 100644 --- a/tests/backend/test-resourcecollector.c +++ b/tests/backend/test-resourcecollector.c @@ -21,7 +21,7 @@ #include "backend/resourcecollector.h" -#include "tests/test.h" +#include "lib/test/test.h" #include <unistd.h> #include <errno.h> diff --git a/tests/backend/test-threadpool.c b/tests/backend/test-threadpool.c index 4c0b1846b..5a07a090a 100644 --- a/tests/backend/test-threadpool.c +++ b/tests/backend/test-threadpool.c @@ -19,7 +19,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "tests/test.h" +#include "lib/test/test.h" #include "backend/threadpool.h" #include "include/logging.h" diff --git a/tests/backend/test-threads.c b/tests/backend/test-threads.c index a3b3697a3..42bbf6697 100644 --- a/tests/backend/test-threads.c +++ b/tests/backend/test-threads.c @@ -27,7 +27,7 @@ #include "include/logging.h" #include "lib/mutex.h" #include "backend/threads.h" -#include "tests/test.h" +#include "lib/test/test.h" #include <unistd.h> #include <errno.h> diff --git a/tests/common/test-config.c b/tests/common/test-config.c index 8ac469fe3..fc64caabb 100644 --- a/tests/common/test-config.c +++ b/tests/common/test-config.c @@ -24,7 +24,7 @@ #include "common/config.h" #include "common/configitem.h" -#include "tests/test.h" +#include "lib/test/test.h" TESTS_BEGIN diff --git a/tests/common/test-interfaces.c b/tests/common/test-interfaces.c index 19db8f8da..d1eb71e5d 100644 --- a/tests/common/test-interfaces.c +++ b/tests/common/test-interfaces.c @@ -25,8 +25,8 @@ #include "common/config.h" #include "common/config_interface.h" -#include "tests/test.h" -#include "tests/common/hello_interface.h" +#include "lib/test/test.h" +#include "interface/say_hello.h" /* define 2 example interfaces @@ -465,7 +465,9 @@ TEST (plugin_discover) if (lumiera_plugin_discover (lumiera_plugin_load, lumiera_plugin_register)) { - LumieraPlugin p = lumiera_plugin_lookup ("modules/examplepluginc.lum"); + LumieraPlugin p = lumiera_plugin_lookup ("modules/test-c-plugin.lum"); + printf ("found plugin: %s\n", lumiera_plugin_name (p)); + p = lumiera_plugin_lookup ("modules/test-cpp-plugin.lum"); printf ("found plugin: %s\n", lumiera_plugin_name (p)); lumiera_plugin_discover (lumiera_plugin_load, lumiera_plugin_register); } @@ -482,12 +484,12 @@ TEST (plugin_unload) lumiera_interfaceregistry_init (); lumiera_plugin_discover (lumiera_plugin_load, lumiera_plugin_register); - LumieraPlugin p = lumiera_plugin_lookup ("modules/examplepluginc.lum"); + LumieraPlugin p = lumiera_plugin_lookup ("modules/test-c-plugin.lum"); printf ("plugin discovered before unload: %p\n", p); - CHECK (p, "prerequisite: need to load examplepluginc.lum"); + CHECK (p, "prerequisite: need to load test-c-plugin.lum"); - lumiera_plugin_unload (lumiera_plugin_lookup ("modules/examplepluginc.lum")); - p = lumiera_plugin_lookup ("examplepluginc.lum"); + lumiera_plugin_unload (lumiera_plugin_lookup ("modules/test-c-plugin.lum")); + p = lumiera_plugin_lookup ("test-c-plugin.lum"); printf ("plugin discovered after unload: %p\n", p); CHECK (!p, "failed to unload plugin."); @@ -496,7 +498,7 @@ TEST (plugin_unload) } -TEST (plugin_examplepluginc) +TEST (plugin_exampleplugin) { lumiera_interfaceregistry_init (); lumiera_plugin_discover (lumiera_plugin_load, lumiera_plugin_register); @@ -522,7 +524,7 @@ TEST (plugin_examplepluginc) lumiera_config_destroy (); } -TEST (plugin_examplepluginc_nested) +TEST (plugin_exampleplugin_nested) { lumiera_interfaceregistry_init (); lumiera_plugin_discover (lumiera_plugin_load, lumiera_plugin_register); @@ -540,6 +542,32 @@ TEST (plugin_examplepluginc_nested) lumiera_config_destroy (); } + +TEST (plugin_exampleplugin_cpp) +{ + lumiera_interfaceregistry_init (); + lumiera_plugin_discover (lumiera_plugin_load, lumiera_plugin_register); + + + LUMIERA_INTERFACE_HANDLE(lumieraorg_testhello, 0) german = + LUMIERA_INTERFACE_OPEN (lumieraorg_testhello, 0, 0, lumieraorg_hello_german_cpp); + + LUMIERA_INTERFACE_HANDLE(lumieraorg_testhello, 0) english = + LUMIERA_INTERFACE_OPEN (lumieraorg_testhello, 0, 0, lumieraorg_hello_english_cpp); + + german->hello (); + german->goodbye ("schnöde Welt!"); + + english->hello (); + english->goodbye ("Vale of Tears!"); + + LUMIERA_INTERFACE_CLOSE (german); + LUMIERA_INTERFACE_CLOSE (english); + + lumiera_interfaceregistry_destroy (); + lumiera_config_destroy (); +} + TESTS_END diff --git a/tests/common/hello_interface.h b/tests/include/interface/say_hello.h similarity index 100% rename from tests/common/hello_interface.h rename to tests/include/interface/say_hello.h diff --git a/tests/lib/helloworldtest.cpp b/tests/lib/helloworldtest.cpp index 529b536ed..c54c87440 100644 --- a/tests/lib/helloworldtest.cpp +++ b/tests/lib/helloworldtest.cpp @@ -35,7 +35,7 @@ namespace lumiera{ namespace test { /****************************************** - * Hellooooooo the world is a test + * Hellooooooo the world is just a test * @test demo of using the test framework */ class HelloWorld_test : public Test @@ -58,10 +58,15 @@ namespace test { - /** Register this test class to be invoked in some test groups (suites) */ - Launch<HelloWorld_test> run_HelloWorld_test("HelloWorld_test","unit common"); - - // NOTE: you may use the Macro "LAUNCHER" in run.hpp to simplify this Registration + /** Register this test class to be invoked in some test groups (suites) + * @remarks this macro \c LUNCHER is defined in run.hpp to simplify + * the registration of test classes. It expands to the + * following static variable definition + * \code + * Launch<HelloWorld_test> run_HelloWorld_test("HelloWorld_test","unit common"); + * \endcode + */ + LAUNCHER (HelloWorld_test, "unit common"); diff --git a/tests/library/test-llist.c b/tests/library/test-llist.c index 305ba8d97..0cbef6107 100644 --- a/tests/library/test-llist.c +++ b/tests/library/test-llist.c @@ -22,7 +22,7 @@ #include <nobug.h> #include "lib/llist.h" -#include "tests/test.h" +#include "lib/test/test.h" TESTS_BEGIN diff --git a/tests/library/test-locking.c b/tests/library/test-locking.c index 8a8ad8f61..4210e8327 100644 --- a/tests/library/test-locking.c +++ b/tests/library/test-locking.c @@ -19,7 +19,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "tests/test.h" +#include "lib/test/test.h" #include "lib/mutex.h" #include "lib/recmutex.h" #include "lib/condition.h" diff --git a/tests/library/test-mpool.c b/tests/library/test-mpool.c index fdd4dba22..0975291a6 100644 --- a/tests/library/test-mpool.c +++ b/tests/library/test-mpool.c @@ -19,7 +19,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "tests/test.h" +#include "lib/test/test.h" #include "lib/mpool.h" struct teststruct diff --git a/tests/library/test-psplay.c b/tests/library/test-psplay.c index 2eb738c9f..0d345fd94 100644 --- a/tests/library/test-psplay.c +++ b/tests/library/test-psplay.c @@ -24,7 +24,7 @@ #include <nobug.h> #include "lib/psplay.h" -#include "tests/test.h" +#include "lib/test/test.h" struct testitem { diff --git a/tests/library/test-safeclib.c b/tests/library/test-safeclib.c index cbff85479..bbb77c77f 100644 --- a/tests/library/test-safeclib.c +++ b/tests/library/test-safeclib.c @@ -18,7 +18,7 @@ */ #include "lib/safeclib.h" #include "lib/tmpbuf.h" /* not factored out yet */ -#include "tests/test.h" +#include "lib/test/test.h" #include <sys/time.h> #include <sys/resource.h> diff --git a/tests/library/test-slist.c b/tests/library/test-slist.c index aa1cdf95e..ddacbde1e 100644 --- a/tests/library/test-slist.c +++ b/tests/library/test-slist.c @@ -20,7 +20,7 @@ */ #include "lib/slist.h" -#include "tests/test.h" +#include "lib/test/test.h" #include <sys/time.h> #include <nobug.h> diff --git a/tests/library/test-time.c b/tests/library/test-time.c index b7922e778..97d667435 100644 --- a/tests/library/test-time.c +++ b/tests/library/test-time.c @@ -21,7 +21,7 @@ typedef unsigned int uint; -#include "tests/test.h" +#include "lib/test/test.h" #include "lib/time.h" #include <nobug.h> diff --git a/tests/lumiera/DIR_INFO b/tests/lumiera/DIR_INFO deleted file mode 100644 index 5dbe3e850..000000000 --- a/tests/lumiera/DIR_INFO +++ /dev/null @@ -1 +0,0 @@ -tests for the main lumiera loader diff --git a/tests/plugin/examplepluginc/example_plugin.c b/tests/plugin/test-c-plugin/example_plugin.c similarity index 99% rename from tests/plugin/examplepluginc/example_plugin.c rename to tests/plugin/test-c-plugin/example_plugin.c index 9751413b0..5e9ee257f 100644 --- a/tests/plugin/examplepluginc/example_plugin.c +++ b/tests/plugin/test-c-plugin/example_plugin.c @@ -26,7 +26,7 @@ #include "common/interfacedescriptor.h" #include "common/config_interface.h" -#include "tests/common/hello_interface.h" +#include "interface/say_hello.h" LUMIERA_PLUGIN_INTERFACEHANDLE; diff --git a/tests/plugin/test-cpp-plugin/example_plugin.cpp b/tests/plugin/test-cpp-plugin/example_plugin.cpp index a41473f51..2a0f9be62 100644 --- a/tests/plugin/test-cpp-plugin/example_plugin.cpp +++ b/tests/plugin/test-cpp-plugin/example_plugin.cpp @@ -1,5 +1,5 @@ /* - example_plugin.cpp - example plugin (C++) for testing the interface/plugin system + ExamplePlugin - example plugin (C++) for testing the interface/plugin system Copyright (C) Lumiera.org 2008, Christian Thaeter <ct@pipapo.org>, @@ -32,7 +32,7 @@ extern "C" { #include "common/interface.h" #include "common/interfacedescriptor.h" -#include "tests/common/hello_interface.h" +#include "interface/say_hello.h" } using boost::format; @@ -41,7 +41,7 @@ using std::endl; -class example_plugin +class ExamplePlugin { public: static LumieraInterface @@ -55,13 +55,13 @@ class example_plugin static void myclose (LumieraInterface) { - std::cout << "closed" << endl; + std::cout << "dying" << endl; } }; -class example_plugin_de - : public example_plugin +class ExamplePlugin_de + : public ExamplePlugin { public: static void @@ -73,13 +73,13 @@ class example_plugin_de static void servus (const char* m) { - std::cout << "Tschuess " << m << endl; + std::cout << "Tschüss " << m << endl; } }; -class example_plugin_en - : public example_plugin +class ExamplePlugin_en + : public ExamplePlugin { public: static void @@ -107,22 +107,22 @@ extern "C" { /* ================== define two lumieraorg_testhello instance ==== LUMIERA_INTERFACE_DEFINE (lumieraorg_testhello, 0 ,lumieraorg_hello_german_cpp , NULL /* no descriptor given */ - , example_plugin::myopen - , example_plugin::myclose + , ExamplePlugin::myopen + , ExamplePlugin::myclose , LUMIERA_INTERFACE_MAP (hello, "\300\244\125\265\235\312\175\263\335\044\371\047\247\263\015\322", - example_plugin_de::griazi) + ExamplePlugin_de::griazi) , LUMIERA_INTERFACE_MAP (goodbye, "\115\365\126\102\201\104\012\257\153\232\006\210\010\346\076\070", - example_plugin_de::servus) + ExamplePlugin_de::servus) ), LUMIERA_INTERFACE_DEFINE (lumieraorg_testhello, 0 ,lumieraorg_hello_english_cpp , NULL /* no descriptor given */ - , example_plugin::myopen - , example_plugin::myclose + , ExamplePlugin::myopen + , ExamplePlugin::myclose , LUMIERA_INTERFACE_MAP (hello, "\303\367\107\154\077\063\237\066\034\034\050\136\170\220\260\226", - example_plugin_en::hello) + ExamplePlugin_en::hello) , LUMIERA_INTERFACE_MAP (goodbye, "\107\207\072\105\101\102\150\201\322\043\104\110\232\023\205\161", - example_plugin_en::bye) + ExamplePlugin_en::bye) ) ); From d9f84a9bfdc59441cbb5657046e4e971d7d593ee Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 3 Dec 2011 02:56:50 +0100 Subject: [PATCH 289/496] clean up lib/meta namespaces --- src/common/configrules.hpp | 16 +- src/common/query/fake-configrules.hpp | 9 +- src/lib/itertools.hpp | 2 +- src/lib/meta/configflags.hpp | 14 +- src/lib/meta/duck-detector.hpp | 10 - src/lib/meta/function-closure.hpp | 12 +- src/lib/meta/function-erasure.hpp | 10 +- src/lib/meta/function.hpp | 10 +- src/lib/meta/generator-combinations.hpp | 10 +- src/lib/meta/generator.hpp | 12 +- src/lib/meta/maybe-compare.hpp | 10 +- src/lib/meta/tuple.hpp | 10 +- src/lib/meta/typelist-manip.hpp | 10 +- src/lib/meta/typelist-util.hpp | 10 +- src/lib/meta/typelist.hpp | 12 +- src/lib/meta/typeseq-util.hpp | 10 +- src/lib/meta/util.hpp | 148 ++++--- src/lib/multifact-arg.hpp | 6 +- src/lib/polymorphic-value.hpp | 4 +- src/lib/simple-allocator.hpp | 6 +- src/lib/test/mock-injector.hpp | 4 +- src/lib/time/control-policy.hpp | 2 +- src/lib/time/formats.hpp | 6 +- src/lib/variant.hpp | 6 +- src/lib/visitor-dispatcher.hpp | 4 +- src/lib/visitor-policies.hpp | 6 +- src/lib/visitor.hpp | 10 +- src/lib/wrapper.hpp | 4 +- src/lib/wrapperptr.hpp | 8 +- src/proc/control/argument-tuple-accept.hpp | 2 +- src/proc/control/command-closure.hpp | 16 +- src/proc/control/command-def.hpp | 12 +- src/proc/control/command-invocation.hpp | 2 +- src/proc/control/command-signature.hpp | 10 +- src/proc/control/command.hpp | 2 +- src/proc/control/memento-tie.hpp | 6 +- src/proc/engine/nodeoperation.hpp | 4 +- src/proc/engine/nodewiring-config.hpp | 4 +- src/proc/engine/nodewiring.cpp | 12 +- src/proc/mobject/builder/buildertool.hpp | 10 +- src/proc/mobject/output-designation.hpp | 2 +- .../session/session-service-defaults.hpp | 6 +- .../mobject/session/session-service-fetch.hpp | 6 +- src/proc/mobject/session/session-services.hpp | 4 +- .../control/argument-tuple-accept-test.cpp | 4 +- .../proc/control/command-argument-test.cpp | 2 +- .../control/command-clone-builder-test.cpp | 2 +- .../proc/control/command-equality-test.cpp | 2 +- .../proc/control/command-mutation-test.cpp | 2 +- .../handling-pattern-standard-impl-test.cpp | 2 +- .../session/session-service-access-test.cpp | 4 +- tests/lib/meta/config-flags-test.cpp | 6 +- tests/lib/meta/dummy-functions.hpp | 6 +- tests/lib/meta/function-closure-test.cpp | 8 +- tests/lib/meta/function-composition-test.cpp | 8 +- tests/lib/meta/function-erasure-test.cpp | 6 +- .../lib/meta/generator-combinations-test.cpp | 6 +- tests/lib/meta/generator-test.cpp | 6 +- .../lib/meta/iterable-classification-test.cpp | 6 +- tests/lib/meta/tuple-diagnostics.hpp | 8 +- tests/lib/meta/type-tuple-test.cpp | 8 +- tests/lib/meta/typelist-diagnostics.hpp | 6 +- tests/lib/meta/typelist-manip-test.cpp | 6 +- tests/lib/meta/typelist-test.cpp | 6 +- tests/lib/meta/typelist-util-test.cpp | 6 +- tests/lib/meta/typeseq-manip-test.cpp | 8 +- tests/lib/time/time-control-test.cpp | 6 +- tests/lib/visitingtoolextendedtest.cpp | 362 +++++++++--------- tests/lib/visitingtooltest.cpp | 243 ++++++------ 69 files changed, 594 insertions(+), 614 deletions(-) diff --git a/src/common/configrules.hpp b/src/common/configrules.hpp index 1e91d4813..28b535d02 100644 --- a/src/common/configrules.hpp +++ b/src/common/configrules.hpp @@ -185,7 +185,7 @@ namespace lumiera { ///////TODO: shouldn't that be namespace lib? or proc? */ template<typename TYPES> class ConfigRules - : public typelist::InstantiateForEach<TYPES, QueryHandler> + : public lib::meta::InstantiateForEach<TYPES, QueryHandler> { protected: ConfigRules () {} @@ -212,13 +212,13 @@ namespace lumiera { ///////TODO: shouldn't that be namespace lib? or proc? * the list of all concrete types participating in the * rule based config query system */ - typedef typelist::Types < proc::mobject::session::Track - , proc::asset::Pipe - , const proc::asset::ProcPatt - , proc::asset::Timeline - , proc::asset::Sequence - > ::List - InterfaceTypes; + typedef lib::meta::Types < proc::mobject::session::Track + , proc::asset::Pipe + , const proc::asset::ProcPatt + , proc::asset::Timeline + , proc::asset::Sequence + > ::List + InterfaceTypes; /** * user-visible Interface to the ConfigRules subsystem. diff --git a/src/common/query/fake-configrules.hpp b/src/common/query/fake-configrules.hpp index 2a527e63c..80ec54f74 100644 --- a/src/common/query/fake-configrules.hpp +++ b/src/common/query/fake-configrules.hpp @@ -60,6 +60,7 @@ namespace lumiera { using asset::ProcPatt; using asset::PProcPatt; using proc::mobject::Session; + using lib::meta::InstantiateChained; using util::isnil; @@ -248,10 +249,10 @@ namespace lumiera { * values for some types of interest for testing and debugging. */ class MockConfigRules - : public typelist::InstantiateChained < InterfaceTypes - , LookupPreconfigured // building block used for each of the types - , MockTable // for implementing the base class (interface) - > + : public InstantiateChained < InterfaceTypes + , LookupPreconfigured // building block used for each of the types + , MockTable // for implementing the base class (interface) + > { protected: MockConfigRules (); ///< to be used only by the singleton factory diff --git a/src/lib/itertools.hpp b/src/lib/itertools.hpp index a4efc4829..22a9e6c32 100644 --- a/src/lib/itertools.hpp +++ b/src/lib/itertools.hpp @@ -491,7 +491,7 @@ namespace lib { namespace { // Helper to pick up the produced value type automatically - using lumiera::typelist::FunctionSignature; + using lib::meta::FunctionSignature; template<typename SIG> struct _ProducedOutput diff --git a/src/lib/meta/configflags.hpp b/src/lib/meta/configflags.hpp index 16c81a7bb..4e71bfd9e 100644 --- a/src/lib/meta/configflags.hpp +++ b/src/lib/meta/configflags.hpp @@ -45,15 +45,15 @@ */ -#ifndef LUMIERA_META_CONFIGFLAGS_H -#define LUMIERA_META_CONFIGFLAGS_H +#ifndef LIB_META_CONFIGFLAGS_H +#define LIB_META_CONFIGFLAGS_H #include "lib/meta/typelist.hpp" -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta{ const size_t CONFIG_FLAGS_MAX = 5; @@ -107,13 +107,13 @@ namespace typelist{ > struct ConfigSetFlag<Fl, Config<f1,f2,f3,f4,IGN> > { - typedef typelist::Config<Fl,f1,f2,f3,f4> Config; + typedef lib::meta::Config<Fl,f1,f2,f3,f4> Config; }; /** build a configuration type from a list-of-flags */ - template<class FLAGS, class CONF=typelist::Config<> > + template<class FLAGS, class CONF=Config<> > struct BuildConfigFromFlags { typedef CONF Config; @@ -199,5 +199,5 @@ namespace typelist{ -}} // namespace lumiera::typelist +}} // namespace lib::meta #endif diff --git a/src/lib/meta/duck-detector.hpp b/src/lib/meta/duck-detector.hpp index 84363616e..bed83ce63 100644 --- a/src/lib/meta/duck-detector.hpp +++ b/src/lib/meta/duck-detector.hpp @@ -92,16 +92,6 @@ #include "lib/meta/util.hpp" -namespace lib { -namespace meta{ - - ///////////////TICKET #175 sort out meta namespace - using lumiera::Yes_t; - using lumiera::No_t; - -}} // namespace lib::meta - - diff --git a/src/lib/meta/function-closure.hpp b/src/lib/meta/function-closure.hpp index 374c75883..207c139bd 100644 --- a/src/lib/meta/function-closure.hpp +++ b/src/lib/meta/function-closure.hpp @@ -44,8 +44,8 @@ */ -#ifndef LUMIERA_META_FUNCTION_CLOSURE_H -#define LUMIERA_META_FUNCTION_CLOSURE_H +#ifndef LIB_META_FUNCTION_CLOSURE_H +#define LIB_META_FUNCTION_CLOSURE_H #include "lib/meta/function.hpp" #include "lib/meta/tuple.hpp" @@ -54,9 +54,9 @@ -namespace lumiera { -namespace typelist{ -namespace func { +namespace lib { +namespace meta{ +namespace func{ using std::tr1::function; @@ -884,5 +884,5 @@ namespace func { -}}} // namespace lumiera::typelist::func +}}} // namespace lib::meta::func #endif diff --git a/src/lib/meta/function-erasure.hpp b/src/lib/meta/function-erasure.hpp index 40acfa46c..f2ed457e4 100644 --- a/src/lib/meta/function-erasure.hpp +++ b/src/lib/meta/function-erasure.hpp @@ -52,8 +52,8 @@ */ -#ifndef LUMIERA_META_FUNCTION_ERASURE_H -#define LUMIERA_META_FUNCTION_ERASURE_H +#ifndef LIB_META_FUNCTION_ERASURE_H +#define LIB_META_FUNCTION_ERASURE_H #include "lib/util.hpp" #include "lib/error.hpp" @@ -64,8 +64,8 @@ #include <tr1/functional> -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta{ using std::tr1::function; using util::unConst; @@ -240,5 +240,5 @@ namespace typelist{ -}} // namespace lumiera::typelist +}} // namespace lib::meta #endif diff --git a/src/lib/meta/function.hpp b/src/lib/meta/function.hpp index fa7c38c55..543a22b19 100644 --- a/src/lib/meta/function.hpp +++ b/src/lib/meta/function.hpp @@ -44,8 +44,8 @@ */ -#ifndef LUMIERA_META_FUNCTION_H -#define LUMIERA_META_FUNCTION_H +#ifndef LIB_META_FUNCTION_H +#define LIB_META_FUNCTION_H #include "lib/meta/typelist.hpp" @@ -53,8 +53,8 @@ -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta{ using std::tr1::function; @@ -357,5 +357,5 @@ namespace typelist{ -}} // namespace lumiera::typelist +}} // namespace lib::meta #endif diff --git a/src/lib/meta/generator-combinations.hpp b/src/lib/meta/generator-combinations.hpp index 3cc3a0946..bbb85ed0e 100644 --- a/src/lib/meta/generator-combinations.hpp +++ b/src/lib/meta/generator-combinations.hpp @@ -33,8 +33,8 @@ */ -#ifndef LUMIERA_META_GENERATOR_COMBINATIONS_H -#define LUMIERA_META_GENERATOR_COMBINATIONS_H +#ifndef LIB_META_GENERATOR_COMBINATIONS_H +#define LIB_META_GENERATOR_COMBINATIONS_H #include "lib/meta/typelist.hpp" #include "lib/meta/typelist-manip.hpp" @@ -42,8 +42,8 @@ -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta{ template<class TYPES_1, class TYPES_2> @@ -97,5 +97,5 @@ namespace typelist{ -}} // namespace lumiera::typelist +}} // namespace lib::meta #endif diff --git a/src/lib/meta/generator.hpp b/src/lib/meta/generator.hpp index c3dfa76f4..fd0b01335 100644 --- a/src/lib/meta/generator.hpp +++ b/src/lib/meta/generator.hpp @@ -39,7 +39,7 @@ This code is heavily inspired by /** @file generator.hpp - ** Helpers for working with lumiera::typelist::Types (i.e. lists-of-types). + ** Helpers for working with lib::meta::Types (i.e. lists-of-types). ** The main purpose is to build interfaces and polymorphic implementations ** (using virtual functions) based on templated Types or Collections of types, ** which is not possible without Template Metaprogramming. @@ -56,15 +56,15 @@ This code is heavily inspired by */ -#ifndef LUMIERA_META_GENERATOR_H -#define LUMIERA_META_GENERATOR_H +#ifndef LIB_META_GENERATOR_H +#define LIB_META_GENERATOR_H #include "lib/meta/typelist.hpp" -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta{ /** * Apply a template to a collection of types. @@ -207,5 +207,5 @@ namespace typelist{ }; -}} // namespace lumiera::typelist +}} // namespace lib::meta #endif diff --git a/src/lib/meta/maybe-compare.hpp b/src/lib/meta/maybe-compare.hpp index 3c96cdf01..9fa81c9da 100644 --- a/src/lib/meta/maybe-compare.hpp +++ b/src/lib/meta/maybe-compare.hpp @@ -21,8 +21,8 @@ */ -#ifndef LUMIERA_META_MAYBE_COMPARE_H -#define LUMIERA_META_MAYBE_COMPARE_H +#ifndef LIB_META_MAYBE_COMPARE_H +#define LIB_META_MAYBE_COMPARE_H #include "lib/functor-util.hpp" @@ -30,8 +30,8 @@ #include <tr1/functional> -namespace lumiera { -namespace typelist { +namespace lib { +namespace meta{ using std::tr1::function; @@ -72,5 +72,5 @@ namespace typelist { } -}} // namespace lumiera::typelist +}} // namespace lib::meta #endif diff --git a/src/lib/meta/tuple.hpp b/src/lib/meta/tuple.hpp index 38d5def29..03c68384f 100644 --- a/src/lib/meta/tuple.hpp +++ b/src/lib/meta/tuple.hpp @@ -44,8 +44,8 @@ */ -#ifndef LUMIERA_META_TUPLE_H -#define LUMIERA_META_TUPLE_H +#ifndef LIB_META_TUPLE_H +#define LIB_META_TUPLE_H #include "lib/meta/typelist.hpp" #include "lib/meta/typelist-util.hpp" @@ -54,8 +54,8 @@ -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { @@ -743,5 +743,5 @@ namespace typelist{ -}} // namespace lumiera::typelist +}} // namespace lib::meta #endif diff --git a/src/lib/meta/typelist-manip.hpp b/src/lib/meta/typelist-manip.hpp index b0d789a76..22144e86f 100644 --- a/src/lib/meta/typelist-manip.hpp +++ b/src/lib/meta/typelist-manip.hpp @@ -49,15 +49,15 @@ */ -#ifndef LUMIERA_META_TYPELIST_MANIP_H -#define LUMIERA_META_TYPELIST_MANIP_H +#ifndef LIB_META_TYPELIST_MANIP_H +#define LIB_META_TYPELIST_MANIP_H #include "lib/meta/typelist.hpp" -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { /** pick the n-th element from a typelist */ @@ -325,5 +325,5 @@ namespace typelist{ -}} // namespace lumiera::typelist +}} // namespace lib::meta #endif diff --git a/src/lib/meta/typelist-util.hpp b/src/lib/meta/typelist-util.hpp index c699855ad..ff8086cb3 100644 --- a/src/lib/meta/typelist-util.hpp +++ b/src/lib/meta/typelist-util.hpp @@ -42,15 +42,15 @@ */ -#ifndef LUMIERA_META_TYPELIST_UTIL_H -#define LUMIERA_META_TYPELIST_UTIL_H +#ifndef LIB_META_TYPELIST_UTIL_H +#define LIB_META_TYPELIST_UTIL_H #include "lib/meta/typelist.hpp" -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { /** @@ -123,5 +123,5 @@ namespace typelist{ -}} // namespace lumiera::typelist +}} // namespace lib::meta #endif diff --git a/src/lib/meta/typelist.hpp b/src/lib/meta/typelist.hpp index 3de24176f..d0cbb44f3 100644 --- a/src/lib/meta/typelist.hpp +++ b/src/lib/meta/typelist.hpp @@ -55,7 +55,7 @@ This code is heavily inspired by ** effectively this is a flavour of functional programming. Just the ** "execution environment" is the compiler, during compilation. ** - ** @see lumiera::visitor::Applicable usage example + ** @see lib::visitor::Applicable usage example ** @see control::CommandSignature more elaborate usage example (dissecting a functor signature) ** @see TypeList_test ** @see TypeListManip_test @@ -63,14 +63,14 @@ This code is heavily inspired by */ -#ifndef LUMIERA_META_TYPELIST_H -#define LUMIERA_META_TYPELIST_H +#ifndef LIB_META_TYPELIST_H +#define LIB_META_TYPELIST_H -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { struct NullType { @@ -129,5 +129,5 @@ namespace typelist{ typedef Node<NullType,NullType> NodeNull; -}} // namespace lumiera::typelist +}} // namespace lib::meta #endif diff --git a/src/lib/meta/typeseq-util.hpp b/src/lib/meta/typeseq-util.hpp index 07ecbffa6..79c76e878 100644 --- a/src/lib/meta/typeseq-util.hpp +++ b/src/lib/meta/typeseq-util.hpp @@ -41,8 +41,8 @@ */ -#ifndef LUMIERA_META_TYPESEQ_UTIL_H -#define LUMIERA_META_TYPESEQ_UTIL_H +#ifndef LIB_META_TYPESEQ_UTIL_H +#define LIB_META_TYPESEQ_UTIL_H #include "lib/meta/typelist.hpp" #include "lib/meta/typelist-manip.hpp" @@ -50,8 +50,8 @@ -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { @@ -203,5 +203,5 @@ namespace typelist{ -}} // namespace lumiera::typelist +}} // namespace lib::meta #endif diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index 14c7cd6a8..f32cbb1ed 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -21,12 +21,13 @@ */ -#ifndef LUMIERA_META_UTIL_H -#define LUMIERA_META_UTIL_H +#ifndef LIB_META_UTIL_H +#define LIB_META_UTIL_H -namespace lumiera { +namespace lib { +namespace meta { /* types for figuring out the overload resolution chosen by the compiler */ @@ -36,77 +37,74 @@ namespace lumiera { - namespace typelist { - - /** Compile-time Type equality: - * Simple Trait template to pick up types considered - * \em identical by the compiler. - * @warning identical, not sub-type! - */ - template<typename T1, typename T2> - struct is_sameType - { - static const bool value = false; - }; - - template<typename T> - struct is_sameType<T,T> - { - static const bool value = true; - }; - - - /** semi-automatic detection if an instantiation is possible. - * Requires help by the template to be tested, which needs to define - * a typedef member \c is_defined. The embedded metafunction Test can be used - * as a predicate for filtering types which may yield a valid instantiation - * of the candidate template in question. - * \par - * A fully automated solution for this problem is impossible by theoretic reasons. - * Any non trivial use of such a \c is_defined trait would break the "One Definition Rule", - * as the state of any type can change from "partially defined" to "fully defined" over - * the course of any translation unit. Thus, even if there may be a \em solution out there, - * we can expect it to break at some point by improvements/fixes to the C++ Language. - */ - template<template<class> class _CandidateTemplate_> - struct Instantiation - { - - template<class X> - class Test - { - typedef _CandidateTemplate_<X> Instance; - - template<class U> - static Yes_t check(typename U::is_defined *); - template<class U> - static No_t check(...); - - public: - static const bool value = (sizeof(Yes_t)==sizeof(check<Instance>(0))); - }; - }; - - - /** Trait template for detecting a typelist type. - * For example, this allows to write specialisations with the help of - * boost::enable_if - */ - template<typename TY> - class is_Typelist - { - template<class X> - static Yes_t check(typename X::List *); - template<class> - static No_t check(...); - - public: - static const bool value = (sizeof(Yes_t)==sizeof(check<TY>(0))); - }; - - - - } // namespace typelist -} // namespace lumiera + /** Compile-time Type equality: + * Simple Trait template to pick up types considered + * \em identical by the compiler. + * @warning identical, not sub-type! + */ + template<typename T1, typename T2> + struct is_sameType + { + static const bool value = false; + }; + + template<typename T> + struct is_sameType<T,T> + { + static const bool value = true; + }; + + + /** semi-automatic detection if an instantiation is possible. + * Requires help by the template to be tested, which needs to define + * a typedef member \c is_defined. The embedded metafunction Test can be used + * as a predicate for filtering types which may yield a valid instantiation + * of the candidate template in question. + * \par + * A fully automated solution for this problem is impossible by theoretic reasons. + * Any non trivial use of such a \c is_defined trait would break the "One Definition Rule", + * as the state of any type can change from "partially defined" to "fully defined" over + * the course of any translation unit. Thus, even if there may be a \em solution out there, + * we can expect it to break at some point by improvements/fixes to the C++ Language. + */ + template<template<class> class _CandidateTemplate_> + struct Instantiation + { + + template<class X> + class Test + { + typedef _CandidateTemplate_<X> Instance; + + template<class U> + static Yes_t check(typename U::is_defined *); + template<class U> + static No_t check(...); + + public: + static const bool value = (sizeof(Yes_t)==sizeof(check<Instance>(0))); + }; + }; + + + /** Trait template for detecting a typelist type. + * For example, this allows to write specialisations with the help of + * boost::enable_if + */ + template<typename TY> + class is_Typelist + { + template<class X> + static Yes_t check(typename X::List *); + template<class> + static No_t check(...); + + public: + static const bool value = (sizeof(Yes_t)==sizeof(check<TY>(0))); + }; + + + +}} // namespace lib::meta #endif diff --git a/src/lib/multifact-arg.hpp b/src/lib/multifact-arg.hpp index c68117c2c..eee802e67 100644 --- a/src/lib/multifact-arg.hpp +++ b/src/lib/multifact-arg.hpp @@ -54,9 +54,9 @@ namespace lib { namespace factory { - using lumiera::typelist::Types; - using lumiera::typelist::FunctionSignature; - using lumiera::typelist::FunctionTypedef; + using lib::meta::Types; + using lib::meta::FunctionSignature; + using lib::meta::FunctionTypedef; using std::tr1::function; diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp index a05e04519..a9b92bc5e 100644 --- a/src/lib/polymorphic-value.hpp +++ b/src/lib/polymorphic-value.hpp @@ -155,8 +155,8 @@ namespace lib { namespace error = lumiera::error; using boost::enable_if; - using lumiera::Yes_t; - using lumiera::No_t; + using lib::meta::Yes_t; + using lib::meta::No_t; struct EmptyBase{ }; diff --git a/src/lib/simple-allocator.hpp b/src/lib/simple-allocator.hpp index f36b19a30..8dd5cea25 100644 --- a/src/lib/simple-allocator.hpp +++ b/src/lib/simple-allocator.hpp @@ -60,9 +60,9 @@ namespace lib { - using lumiera::typelist::Types; - using lumiera::typelist::IsInList; - using lumiera::typelist::InstantiateForEach; + using lib::meta::Types; + using lib::meta::IsInList; + using lib::meta::InstantiateForEach; diff --git a/src/lib/test/mock-injector.hpp b/src/lib/test/mock-injector.hpp index 116711966..086cd478d 100644 --- a/src/lib/test/mock-injector.hpp +++ b/src/lib/test/mock-injector.hpp @@ -130,8 +130,8 @@ namespace test{ namespace mock { using boost::enable_if; - using lumiera::Yes_t; - using lumiera::No_t; + using lib::meta::Yes_t; + using lib::meta::No_t; /** * Metafunction: does the Type in question diff --git a/src/lib/time/control-policy.hpp b/src/lib/time/control-policy.hpp index 7ffed6426..a657fb7ff 100644 --- a/src/lib/time/control-policy.hpp +++ b/src/lib/time/control-policy.hpp @@ -115,7 +115,7 @@ namespace time { namespace mutation { using boost::disable_if; - using lumiera::typelist::is_sameType; + using lib::meta::is_sameType; using std::tr1::placeholders::_1; using std::tr1::function; using std::tr1::bind; diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index 12ac1b9b8..252c7145c 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -162,9 +162,9 @@ namespace time { /* == Descriptor to define Support for specific formats == */ - using lumiera::typelist::Types; - using lumiera::typelist::Node; - using lumiera::typelist::NullType; + using lib::meta::Types; + using lib::meta::Node; + using lib::meta::NullType; /** * Descriptor to denote support for a specific (timecode) format. diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp index b4eef9b76..f764ec4bb 100644 --- a/src/lib/variant.hpp +++ b/src/lib/variant.hpp @@ -56,9 +56,9 @@ namespace lib { namespace variant { - using lumiera::typelist::count; - using lumiera::typelist::maxSize; - using lumiera::typelist::InstantiateWithIndex; + using lib::meta::count; + using lib::meta::maxSize; + using lib::meta::InstantiateWithIndex; /** * internal helper used to build a variant storage wrapper. diff --git a/src/lib/visitor-dispatcher.hpp b/src/lib/visitor-dispatcher.hpp index 35f566193..fba85bf1a 100644 --- a/src/lib/visitor-dispatcher.hpp +++ b/src/lib/visitor-dispatcher.hpp @@ -34,7 +34,7 @@ #include <vector> -namespace lumiera { +namespace lib { namespace visitor { using lib::ClassLock; @@ -213,5 +213,5 @@ namespace visitor { -}} // namespace lumiera::visitor +}} // namespace lib::visitor #endif diff --git a/src/lib/visitor-policies.hpp b/src/lib/visitor-policies.hpp index 164b7d1b2..558fbda35 100644 --- a/src/lib/visitor-policies.hpp +++ b/src/lib/visitor-policies.hpp @@ -22,7 +22,7 @@ /** @file visitor-policies.hpp - ** Policies usable for configuring the lumiera::visitor::Tool for different kinds of error handling. + ** Policies usable for configuring the lib::visitor::Tool for different kinds of error handling. ** @see buildertool.hpp for another flavour (calling an catch-all-function there) ** */ @@ -35,7 +35,7 @@ #include "lib/error.hpp" -namespace lumiera { +namespace lib { namespace visitor { /** @@ -70,5 +70,5 @@ namespace visitor { -}} // namespace lumiera::visitor +}} // namespace lib::visitor #endif diff --git a/src/lib/visitor.hpp b/src/lib/visitor.hpp index b2a67e447..851cfc14e 100644 --- a/src/lib/visitor.hpp +++ b/src/lib/visitor.hpp @@ -73,8 +73,8 @@ Credits for many further implementation ideas go to -#ifndef LUMIERA_VISITOR_H -#define LUMIERA_VISITOR_H +#ifndef LIB_VISITOR_H +#define LIB_VISITOR_H #include "lib/visitor-policies.hpp" #include "lib/visitor-dispatcher.hpp" @@ -82,9 +82,11 @@ Credits for many further implementation ideas go to #include "lib/meta/typelist.hpp" -namespace lumiera { +namespace lib { namespace visitor { + namespace typelist = lib::meta; + /** * Marker interface / base class for all "visiting tools". @@ -225,5 +227,5 @@ namespace visitor { -}} // namespace lumiera::visitor +}} // namespace lib::visitor #endif diff --git a/src/lib/wrapper.hpp b/src/lib/wrapper.hpp index d1065612f..8aa616697 100644 --- a/src/lib/wrapper.hpp +++ b/src/lib/wrapper.hpp @@ -56,7 +56,7 @@ namespace wrapper { using util::unConst; using util::isSameObject; - using lumiera::typelist::FunctionSignature; + using lib::meta::FunctionSignature; using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE; using boost::remove_const; @@ -380,7 +380,7 @@ namespace wrapper { { using std::tr1::bind; using std::tr1::placeholders::_1; - using lumiera::typelist::func::chained; + using lib::meta::func::chained; // note: binding "this" mandates noncopyable function<Res(Res)> doCaptureResult = bind (&FunctionResult::captureResult, this, _1 ); function<SIG> chainedWithResCapture = chained (targetFunction, doCaptureResult); diff --git a/src/lib/wrapperptr.hpp b/src/lib/wrapperptr.hpp index 6248438f3..8dbb29885 100644 --- a/src/lib/wrapperptr.hpp +++ b/src/lib/wrapperptr.hpp @@ -39,10 +39,10 @@ namespace proc { namespace mobject { class MObject; } - typedef lumiera::typelist::Types < mobject::Placement<mobject::MObject>* - , lib::P<asset::Asset>* - > ::List - WrapperTypes; + typedef lib::meta::Types < mobject::Placement<mobject::MObject>* + , lib::P<asset::Asset>* + > ::List + WrapperTypes; } namespace lumiera { diff --git a/src/proc/control/argument-tuple-accept.hpp b/src/proc/control/argument-tuple-accept.hpp index 7003c32f7..61c7bf70d 100644 --- a/src/proc/control/argument-tuple-accept.hpp +++ b/src/proc/control/argument-tuple-accept.hpp @@ -69,7 +69,7 @@ namespace control { namespace bind_arg { // internals.... - using namespace lumiera::typelist; + using namespace lib::meta; // diff --git a/src/proc/control/command-closure.hpp b/src/proc/control/command-closure.hpp index 291717bb2..a2246125c 100644 --- a/src/proc/control/command-closure.hpp +++ b/src/proc/control/command-closure.hpp @@ -91,15 +91,15 @@ namespace proc { namespace control { - using lumiera::typelist::FunctionSignature; - using lumiera::typelist::Tuple; - using lumiera::typelist::BuildTupleAccessor; - using lumiera::typelist::func::TupleApplicator; - using lumiera::typelist::FunErasure; - using lumiera::typelist::StoreFunction; - using lumiera::typelist::NullType; + using lib::meta::FunctionSignature; + using lib::meta::Tuple; + using lib::meta::BuildTupleAccessor; + using lib::meta::func::TupleApplicator; + using lib::meta::FunErasure; + using lib::meta::StoreFunction; + using lib::meta::NullType; - using lumiera::typelist::equals_safeInvoke; + using lib::meta::equals_safeInvoke; using lib::TypedAllocationManager; using util::unConst; using std::tr1::function; diff --git a/src/proc/control/command-def.hpp b/src/proc/control/command-def.hpp index 959d98505..292c8f27c 100644 --- a/src/proc/control/command-def.hpp +++ b/src/proc/control/command-def.hpp @@ -86,12 +86,12 @@ namespace control { using lib::Symbol; using util::cStr; - using lumiera::typelist::FunctionSignature; - using lumiera::typelist::FunctionTypedef; - using lumiera::typelist::Types; - using lumiera::typelist::NullType; - using lumiera::typelist::Tuple; - using lumiera::typelist::tuple::makeNullTuple; + using lib::meta::FunctionSignature; + using lib::meta::FunctionTypedef; + using lib::meta::Types; + using lib::meta::NullType; + using lib::meta::Tuple; + using lib::meta::tuple::makeNullTuple; diff --git a/src/proc/control/command-invocation.hpp b/src/proc/control/command-invocation.hpp index 41e1e521f..fd4ee784a 100644 --- a/src/proc/control/command-invocation.hpp +++ b/src/proc/control/command-invocation.hpp @@ -56,7 +56,7 @@ namespace proc { namespace control { - using namespace lumiera::typelist; + using namespace lib::meta; diff --git a/src/proc/control/command-signature.hpp b/src/proc/control/command-signature.hpp index b756cf0ec..93ac35205 100644 --- a/src/proc/control/command-signature.hpp +++ b/src/proc/control/command-signature.hpp @@ -61,11 +61,11 @@ namespace control { using std::tr1::function; - using lumiera::typelist::FunctionSignature; - using lumiera::typelist::FunctionTypedef; - using lumiera::typelist::Types; - using lumiera::typelist::Append; - using lumiera::typelist::SplitLast; + using lib::meta::FunctionSignature; + using lib::meta::FunctionTypedef; + using lib::meta::Types; + using lib::meta::Append; + using lib::meta::SplitLast; /** diff --git a/src/proc/control/command.hpp b/src/proc/control/command.hpp index aa4c6075b..5ec653634 100644 --- a/src/proc/control/command.hpp +++ b/src/proc/control/command.hpp @@ -74,7 +74,7 @@ namespace control { using std::string; using lib::Symbol; using std::tr1::shared_ptr; - using lumiera::typelist::Tuple; + using lib::meta::Tuple; LUMIERA_ERROR_DECLARE (UNBOUND_ARGUMENTS); ///< Command functor not yet usable, because arguments aren't bound diff --git a/src/proc/control/memento-tie.hpp b/src/proc/control/memento-tie.hpp index 917fa1428..0e89f9b21 100644 --- a/src/proc/control/memento-tie.hpp +++ b/src/proc/control/memento-tie.hpp @@ -57,9 +57,9 @@ namespace proc { namespace control { using boost::equality_comparable; - using lumiera::typelist::func::bindLast; - using lumiera::typelist::func::chained; - using lumiera::typelist::equals_safeInvoke; + using lib::meta::func::bindLast; + using lib::meta::func::chained; + using lib::meta::equals_safeInvoke; LUMIERA_ERROR_DECLARE (MISSING_MEMENTO); ///< Undo functor not yet usable, because no undo state has been captured diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp index f6ef36cf9..a715353bf 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -83,7 +83,7 @@ namespace config { */ struct OperationBase { - typedef lumiera::Yes_t is_defined; + typedef lib::meta::Yes_t is_defined; BuffHandle getSource (Invocation& ivo, uint chanNo) @@ -277,7 +277,7 @@ namespace config { }; - using lumiera::typelist::Config; + using lib::meta::Config; ///////////////////////TODO: selecting this way isn't especially readable, ///////////////////////////: but BufferProvider selection is going to be solved differently anyway, see Ticket #249 template<class CONF> diff --git a/src/proc/engine/nodewiring-config.hpp b/src/proc/engine/nodewiring-config.hpp index 5a3b7e7e8..aec901789 100644 --- a/src/proc/engine/nodewiring-config.hpp +++ b/src/proc/engine/nodewiring-config.hpp @@ -65,9 +65,9 @@ namespace engine { namespace config { using util::contains; - using lumiera::typelist::FlagInfo; + using lib::meta::FlagInfo; - using lumiera::typelist::CONFIG_FLAGS_MAX; + using lib::meta::CONFIG_FLAGS_MAX; typedef size_t IxID; ///////////////////////////////TICKET #863 diff --git a/src/proc/engine/nodewiring.cpp b/src/proc/engine/nodewiring.cpp index a3a3b01c1..6ffa2b7be 100644 --- a/src/proc/engine/nodewiring.cpp +++ b/src/proc/engine/nodewiring.cpp @@ -34,12 +34,12 @@ namespace engine { namespace config { - using lumiera::typelist::Flags; - using lumiera::typelist::CombineFlags; - using lumiera::typelist::DefineConfigByFlags; - using lumiera::typelist::Instantiation; - using lumiera::typelist::Apply; - using lumiera::typelist::Filter; + using lib::meta::Flags; + using lib::meta::CombineFlags; + using lib::meta::DefineConfigByFlags; + using lib::meta::Instantiation; + using lib::meta::Apply; + using lib::meta::Filter; using lib::AllocationCluster; diff --git a/src/proc/mobject/builder/buildertool.hpp b/src/proc/mobject/builder/buildertool.hpp index 8ba2b6ac3..2f9dc2078 100644 --- a/src/proc/mobject/builder/buildertool.hpp +++ b/src/proc/mobject/builder/buildertool.hpp @@ -94,7 +94,7 @@ namespace mobject { * an instantiation of the "Applicable" template parametrised with all * concrete Buildable classes, for which it wants calls to be dispatched. * \par - * In addition to lumiera::visitor::Tool, BuilderTool adds support for dealing + * In addition to lib::visitor::Tool, BuilderTool adds support for dealing * with objects normally handled by means of smart-pointers or similar * wrappers, most notably mobject::Placement. The visitation is initiated * by calling the stand-alone function \c (BuilderTool& tool, WRA& wrappedTargetObj) , @@ -106,7 +106,7 @@ namespace mobject { * as we simply store a pointer within the BuilderTool instance. */ class BuilderTool - : public lumiera::visitor::Tool<void, InvokeCatchAllFunction> + : public lib::visitor::Tool<void, InvokeCatchAllFunction> { lumiera::WrapperPtr currentWrapper_; public: @@ -166,11 +166,11 @@ namespace mobject { class TYPELIST // list of all concrete Buildables to be treated > class Applicable - : public lumiera::visitor::Applicable<TOOLImpl, TYPELIST, BuilderTool> + : public lib::visitor::Applicable<TOOLImpl, TYPELIST, BuilderTool> { } ; - using lumiera::typelist::Types; // convenience for the users of "Applicable" + using lib::meta::Types; // convenience for the users of "Applicable" }// namespace mobject::builder @@ -181,7 +181,7 @@ namespace mobject { /** * Marker Interface for classes visitable by Builder tools. */ - class Buildable : public lumiera::visitor::Visitable<builder::BuilderTool> + class Buildable : public lib::visitor::Visitable<builder::BuilderTool> { }; diff --git a/src/proc/mobject/output-designation.hpp b/src/proc/mobject/output-designation.hpp index 690e2a353..d9b24dc6e 100644 --- a/src/proc/mobject/output-designation.hpp +++ b/src/proc/mobject/output-designation.hpp @@ -35,7 +35,7 @@ extern "C" { namespace proc { namespace mobject { - namespace mp = lumiera::typelist; + namespace mp = lib::meta; class MObject; diff --git a/src/proc/mobject/session/session-service-defaults.hpp b/src/proc/mobject/session/session-service-defaults.hpp index 80457864e..623c0aee8 100644 --- a/src/proc/mobject/session/session-service-defaults.hpp +++ b/src/proc/mobject/session/session-service-defaults.hpp @@ -45,9 +45,9 @@ namespace proc { namespace mobject { namespace session { -// using lumiera::typelist::InstantiateChained; -// using lumiera::typelist::InheritFrom; -// using lumiera::typelist::NullType; +// using lib::meta::InstantiateChained; +// using lib::meta::InheritFrom; +// using lib::meta::NullType; class SessionServiceDefaults diff --git a/src/proc/mobject/session/session-service-fetch.hpp b/src/proc/mobject/session/session-service-fetch.hpp index 93d6cb7c2..004668778 100644 --- a/src/proc/mobject/session/session-service-fetch.hpp +++ b/src/proc/mobject/session/session-service-fetch.hpp @@ -48,9 +48,9 @@ namespace proc { namespace mobject { namespace session { -// using lumiera::typelist::InstantiateChained; -// using lumiera::typelist::InheritFrom; -// using lumiera::typelist::NullType; +// using lib::meta::InstantiateChained; +// using lib::meta::InheritFrom; +// using lib::meta::NullType; /** * Implementation-level service for resolving an Placement-ID. diff --git a/src/proc/mobject/session/session-services.hpp b/src/proc/mobject/session/session-services.hpp index ed95186c1..9545d7bae 100644 --- a/src/proc/mobject/session/session-services.hpp +++ b/src/proc/mobject/session/session-services.hpp @@ -86,8 +86,8 @@ namespace proc { namespace mobject { namespace session { - using lumiera::typelist::InstantiateChained; - using lumiera::typelist::Types; + using lib::meta::InstantiateChained; + using lib::meta::Types; /** diff --git a/tests/components/proc/control/argument-tuple-accept-test.cpp b/tests/components/proc/control/argument-tuple-accept-test.cpp index b31ee44c1..75d80c318 100644 --- a/tests/components/proc/control/argument-tuple-accept-test.cpp +++ b/tests/components/proc/control/argument-tuple-accept-test.cpp @@ -44,8 +44,8 @@ namespace test { using lib::time::TimeVar; using std::tr1::function; - using lumiera::typelist::FunctionSignature; - using lumiera::typelist::Tuple; + using lib::meta::FunctionSignature; + using lib::meta::Tuple; diff --git a/tests/components/proc/control/command-argument-test.cpp b/tests/components/proc/control/command-argument-test.cpp index a4413e7fa..60315ad83 100644 --- a/tests/components/proc/control/command-argument-test.cpp +++ b/tests/components/proc/control/command-argument-test.cpp @@ -57,7 +57,7 @@ namespace test { using lib::test::showSizeof; using lib::test::randTime; - using namespace lumiera::typelist; + using namespace lib::meta; diff --git a/tests/components/proc/control/command-clone-builder-test.cpp b/tests/components/proc/control/command-clone-builder-test.cpp index eabc73a69..18458e830 100644 --- a/tests/components/proc/control/command-clone-builder-test.cpp +++ b/tests/components/proc/control/command-clone-builder-test.cpp @@ -38,7 +38,7 @@ namespace proc { namespace control { namespace test { - using namespace lumiera::typelist; + using namespace lib::meta; typedef lib::P<CommandImpl> PCmdImpl; diff --git a/tests/components/proc/control/command-equality-test.cpp b/tests/components/proc/control/command-equality-test.cpp index 7d8380d0e..cf60a738e 100644 --- a/tests/components/proc/control/command-equality-test.cpp +++ b/tests/components/proc/control/command-equality-test.cpp @@ -42,7 +42,7 @@ namespace test { - using namespace lumiera::typelist; + using namespace lib::meta; using lib::Symbol; using util::isnil; using util::isSameObject; diff --git a/tests/components/proc/control/command-mutation-test.cpp b/tests/components/proc/control/command-mutation-test.cpp index 6ba6f8867..6d82a684e 100644 --- a/tests/components/proc/control/command-mutation-test.cpp +++ b/tests/components/proc/control/command-mutation-test.cpp @@ -45,7 +45,7 @@ namespace proc { namespace control { namespace test { - using namespace lumiera::typelist; + using namespace lib::meta; using control::CmdClosure; diff --git a/tests/components/proc/control/handling-pattern-standard-impl-test.cpp b/tests/components/proc/control/handling-pattern-standard-impl-test.cpp index eea9a3ccf..356d63be1 100644 --- a/tests/components/proc/control/handling-pattern-standard-impl-test.cpp +++ b/tests/components/proc/control/handling-pattern-standard-impl-test.cpp @@ -72,7 +72,7 @@ namespace test { // using lumiera::P; - //using lumiera::typelist::BuildTupleAccessor; + //using lib::meta::BuildTupleAccessor; // using lumiera::error::LUMIERA_ERROR_EXTERNAL; namespace { // test data and helpers... diff --git a/tests/components/proc/mobject/session/session-service-access-test.cpp b/tests/components/proc/mobject/session/session-service-access-test.cpp index 613d06be3..a96436e8b 100644 --- a/tests/components/proc/mobject/session/session-service-access-test.cpp +++ b/tests/components/proc/mobject/session/session-service-access-test.cpp @@ -46,8 +46,8 @@ namespace test { namespace { // what follows is a simulated (simplified) version // of the complete Session + SessionManager setup..... - using lumiera::typelist::Types; - using lumiera::typelist::InstantiateChained; + using lib::meta::Types; + using lib::meta::InstantiateChained; diff --git a/tests/lib/meta/config-flags-test.cpp b/tests/lib/meta/config-flags-test.cpp index 8d1222e4b..87649556f 100644 --- a/tests/lib/meta/config-flags-test.cpp +++ b/tests/lib/meta/config-flags-test.cpp @@ -55,8 +55,8 @@ using std::cout; using std::endl; -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { namespace test { @@ -335,4 +335,4 @@ cout << "__________________________\n" \ -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test diff --git a/tests/lib/meta/dummy-functions.hpp b/tests/lib/meta/dummy-functions.hpp index 017624a01..37cf23fd9 100644 --- a/tests/lib/meta/dummy-functions.hpp +++ b/tests/lib/meta/dummy-functions.hpp @@ -32,8 +32,8 @@ //using boost::format; -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { namespace test { @@ -56,5 +56,5 @@ namespace test { -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test #endif diff --git a/tests/lib/meta/function-closure-test.cpp b/tests/lib/meta/function-closure-test.cpp index 4d2eb2a06..bc48c07bf 100644 --- a/tests/lib/meta/function-closure-test.cpp +++ b/tests/lib/meta/function-closure-test.cpp @@ -52,9 +52,9 @@ using std::cout; using std::endl; -namespace lumiera { -namespace typelist{ -namespace test { +namespace lib { +namespace meta { +namespace test { namespace { // test data @@ -352,4 +352,4 @@ namespace test { -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test diff --git a/tests/lib/meta/function-composition-test.cpp b/tests/lib/meta/function-composition-test.cpp index 9761d87b6..38bf82a16 100644 --- a/tests/lib/meta/function-composition-test.cpp +++ b/tests/lib/meta/function-composition-test.cpp @@ -29,9 +29,9 @@ #include "meta/typelist-diagnostics.hpp" -namespace lumiera { -namespace typelist{ -namespace test { +namespace lib { +namespace meta { +namespace test { using ::test::Test; using func::applyFirst; @@ -387,4 +387,4 @@ namespace test { -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test diff --git a/tests/lib/meta/function-erasure-test.cpp b/tests/lib/meta/function-erasure-test.cpp index b4eefad76..8a3db77cb 100644 --- a/tests/lib/meta/function-erasure-test.cpp +++ b/tests/lib/meta/function-erasure-test.cpp @@ -34,8 +34,8 @@ using lumiera::error::LUMIERA_ERROR_ASSERTION; -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { namespace test { using std::tr1::function; @@ -273,4 +273,4 @@ namespace test { -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test diff --git a/tests/lib/meta/generator-combinations-test.cpp b/tests/lib/meta/generator-combinations-test.cpp index 4c90b1f93..6a5bbba88 100644 --- a/tests/lib/meta/generator-combinations-test.cpp +++ b/tests/lib/meta/generator-combinations-test.cpp @@ -35,8 +35,8 @@ using std::cout; using std::endl; -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { namespace test { @@ -137,4 +137,4 @@ namespace test { -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test diff --git a/tests/lib/meta/generator-test.cpp b/tests/lib/meta/generator-test.cpp index 31ca453ab..00a816c22 100644 --- a/tests/lib/meta/generator-test.cpp +++ b/tests/lib/meta/generator-test.cpp @@ -47,8 +47,8 @@ using std::string; using std::cout; -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { namespace test { /** template for generating lots of different test types */ @@ -144,4 +144,4 @@ namespace test { -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test diff --git a/tests/lib/meta/iterable-classification-test.cpp b/tests/lib/meta/iterable-classification-test.cpp index 9a8a21aa1..1dd45a6b2 100644 --- a/tests/lib/meta/iterable-classification-test.cpp +++ b/tests/lib/meta/iterable-classification-test.cpp @@ -41,9 +41,9 @@ #include <set> -namespace lib { -namespace meta{ -namespace test{ +namespace lib { +namespace meta { +namespace test { using proc::mobject::session::Effect; using proc::mobject::session::ScopeQuery; diff --git a/tests/lib/meta/tuple-diagnostics.hpp b/tests/lib/meta/tuple-diagnostics.hpp index 69a29b6f1..d78eb1ae7 100644 --- a/tests/lib/meta/tuple-diagnostics.hpp +++ b/tests/lib/meta/tuple-diagnostics.hpp @@ -52,9 +52,9 @@ using boost::lexical_cast; using util::unConst; -namespace lumiera { -namespace typelist{ -namespace test { +namespace lib { +namespace meta { +namespace test { namespace { // Diagnostics @@ -189,5 +189,5 @@ namespace test { -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test #endif diff --git a/tests/lib/meta/type-tuple-test.cpp b/tests/lib/meta/type-tuple-test.cpp index aa773b3a2..8589b322a 100644 --- a/tests/lib/meta/type-tuple-test.cpp +++ b/tests/lib/meta/type-tuple-test.cpp @@ -25,7 +25,7 @@ ** Interplay of typelists, type tuples and simple record ** data types build on top of them. ** - ** @see lumiera::typelist::Tuple + ** @see lib::meta::Tuple ** @see tuple.hpp ** @see function-closure.hpp ** @see control::CmdClosure real world usage example @@ -45,8 +45,8 @@ using std::cout; using std::endl; -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { namespace test { @@ -455,4 +455,4 @@ namespace test { -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test diff --git a/tests/lib/meta/typelist-diagnostics.hpp b/tests/lib/meta/typelist-diagnostics.hpp index 10e64154a..721498c45 100644 --- a/tests/lib/meta/typelist-diagnostics.hpp +++ b/tests/lib/meta/typelist-diagnostics.hpp @@ -52,8 +52,8 @@ using boost::format; using boost::enable_if; -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { /** dummy interface / baseclass for diagnostics */ struct Numz @@ -209,5 +209,5 @@ namespace typelist{ -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test #endif diff --git a/tests/lib/meta/typelist-manip-test.cpp b/tests/lib/meta/typelist-manip-test.cpp index 57f88d1dd..46e3f784b 100644 --- a/tests/lib/meta/typelist-manip-test.cpp +++ b/tests/lib/meta/typelist-manip-test.cpp @@ -50,8 +50,8 @@ using std::cout; using std::endl; -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { namespace test { @@ -377,4 +377,4 @@ namespace test { -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test diff --git a/tests/lib/meta/typelist-test.cpp b/tests/lib/meta/typelist-test.cpp index 041f8eaf2..80081fe67 100644 --- a/tests/lib/meta/typelist-test.cpp +++ b/tests/lib/meta/typelist-test.cpp @@ -30,8 +30,8 @@ using std::string; using std::cout; -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { namespace test { template<int I> @@ -91,4 +91,4 @@ namespace test { -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test diff --git a/tests/lib/meta/typelist-util-test.cpp b/tests/lib/meta/typelist-util-test.cpp index 22a8c62e8..66a2112c0 100644 --- a/tests/lib/meta/typelist-util-test.cpp +++ b/tests/lib/meta/typelist-util-test.cpp @@ -26,8 +26,8 @@ -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { namespace test { @@ -80,4 +80,4 @@ namespace test { -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test diff --git a/tests/lib/meta/typeseq-manip-test.cpp b/tests/lib/meta/typeseq-manip-test.cpp index 3445d2217..0874eae9f 100644 --- a/tests/lib/meta/typeseq-manip-test.cpp +++ b/tests/lib/meta/typeseq-manip-test.cpp @@ -51,9 +51,9 @@ using std::cout; using std::endl; -namespace lumiera { -namespace typelist{ -namespace test { +namespace lib { +namespace meta { +namespace test { namespace { // test data @@ -183,4 +183,4 @@ namespace test { -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp index f949d49be..f7db3039f 100644 --- a/tests/lib/time/time-control-test.cpp +++ b/tests/lib/time/time-control-test.cpp @@ -54,8 +54,8 @@ namespace test{ using lib::ScopedHolder; using proc::asset::meta::TimeGrid; - using lumiera::typelist::Types; - using lumiera::typelist::InstantiateChainedCombinations; + using lib::meta::Types; + using lib::meta::InstantiateChainedCombinations; using error::LUMIERA_ERROR_UNCONNECTED; @@ -195,7 +195,7 @@ namespace test{ namespace { // Implementation: Matrix of individual test combinations - using lumiera::typelist::is_sameType; + using lib::meta::is_sameType; template<class T> inline bool diff --git a/tests/lib/visitingtoolextendedtest.cpp b/tests/lib/visitingtoolextendedtest.cpp index 8021f0241..06d21cba3 100644 --- a/tests/lib/visitingtoolextendedtest.cpp +++ b/tests/lib/visitingtoolextendedtest.cpp @@ -32,198 +32,192 @@ using std::string; using std::cout; -namespace lumiera - { - namespace visitor +namespace lib { +namespace visitor{ +namespace test2 { + + typedef visitor::Tool<> Tool; + + class HomoSapiens : public Visitable<> { - namespace test2 - { - typedef visitor::Tool<> Tool; - - class HomoSapiens : public Visitable<> - { - public: - DEFINE_PROCESSABLE_BY (Tool); - }; - - class Boss : public HomoSapiens - { - public: - DEFINE_PROCESSABLE_BY (Tool); - }; - - class BigBoss : public Boss - { - public: - DEFINE_PROCESSABLE_BY (Tool); - }; + public: + DEFINE_PROCESSABLE_BY (Tool); + }; + + class Boss : public HomoSapiens + { + public: + DEFINE_PROCESSABLE_BY (Tool); + }; + + class BigBoss : public Boss + { + public: + DEFINE_PROCESSABLE_BY (Tool); + }; - - template<class BASE> - class VerboseVisitor - : public BASE + + template<class BASE> + class VerboseVisitor + : public BASE + { + protected: + void talk_to (string guy) { - protected: - void talk_to (string guy) - { - cout << format ("Hello %s, nice to meet you...\n") % guy; - } - }; - - class Babbler - : public Applicable< Babbler, - Types<Boss,BigBoss>::List, // treat this types - VerboseVisitor<Tool> // intermediary base class - > - { - public: - void treat (Boss&) { talk_to("Boss"); } - void treat (BigBoss&) { talk_to("Big Boss"); } - }; + cout << format ("Hello %s, nice to meet you...\n") % guy; + } + }; + + class Babbler + : public Applicable< Babbler, + Types<Boss,BigBoss>::List, // treat this types + VerboseVisitor<Tool> // intermediary base class + > + { + public: + void treat (Boss&) { talk_to("Boss"); } + void treat (BigBoss&) { talk_to("Big Boss"); } + }; - // the classes above comprise the standard use case, - // what follows covers rather exotic corner cases - - - - /** defines an catch-all-function instead of the silent default error handler */ - template<class RET> - struct Catched - { - RET onUnknown (HomoSapiens&) { cout << "we-do-everything-for-YOU!\n"; } - }; - - /** defines another different visiting tool base */ - typedef visitor::Tool<void, Catched> Hastalavista; - typedef Visitable<Hastalavista> Chief; ///< another special kind of visitables + // the classes above comprise the standard use case, + // what follows covers rather exotic corner cases + + + + /** defines an catch-all-function instead of the silent default error handler */ + template<class RET> + struct Catched + { + RET onUnknown (HomoSapiens&) { cout << "we-do-everything-for-YOU!\n"; return RET(); } + }; + + /** defines another different visiting tool base */ + typedef visitor::Tool<void, Catched> Hastalavista; + typedef Visitable<Hastalavista> Chief; ///< another special kind of visitable #define DEFINE_HASTALAVISTA_PROCESSABLE \ - virtual void apply (Hastalavista& tool) \ - { return Chief::dispatchOp (*this, tool); } - - - /** now mixing the two hierarchies... */ - class Leader : public Chief, - public Boss ///< can act as HomoSapiens or as Chief - { - public: - using HomoSapiens::apply; - DEFINE_HASTALAVISTA_PROCESSABLE; - }; - - class Visionary : public Leader - { - DEFINE_HASTALAVISTA_PROCESSABLE; - }; - - /** Hastalavista-Visiting-Tool - * tailored for the Chief hierarchy - */ - class Blatherer - : public Applicable< Blatherer, - Types<Visionary>::List, // get calls to Visionary dispatched - VerboseVisitor<Hastalavista> // note: different tool base class - > - { - public: - void treat (Leader&) { talk_to("Mr.Future"); } - }; - - - + virtual void apply (Hastalavista& tool) \ + { return Chief::dispatchOp (*this, tool); } + + + /** now mixing the two hierarchies... */ + class Leader : public Chief, + public Boss ///< can act as HomoSapiens or as Chief + { + public: + using HomoSapiens::apply; + DEFINE_HASTALAVISTA_PROCESSABLE; + }; + + class Visionary : public Leader + { + DEFINE_HASTALAVISTA_PROCESSABLE; + }; + + /** Hastalavista-Visiting-Tool + * tailored for the Chief hierarchy + */ + class Blatherer + : public Applicable< Blatherer, + Types<Visionary>::List, // get calls to Visionary dispatched + VerboseVisitor<Hastalavista> // note: different tool base class + > + { + public: + void treat (Leader&) { talk_to("Mr.Future"); } + }; + + + - - - - /************************************************************************* - * @test more esoteric corner cases of our visitor lib implementation. - * Defines a hierarchy of test classes, which mix two different - * kinds of "visitable" by two disjoint tool base classes. One - * of these base classes uses an explicit error handling - * catch-all-function. - */ - class VisitingToolExtended_test : public Test + + + + /************************************************************************* + * @test more esoteric corner cases of our visitor lib implementation. + * Defines a hierarchy of test classes, which mix two different + * kinds of "visitable" by two disjoint tool base classes. One + * of these base classes uses an explicit error handling + * catch-all-function. + */ + class VisitingToolExtended_test : public Test + { + virtual void run(Arg) { - virtual void run(Arg) - { - known_visitor_known_class(); - visitor_not_visiting_some_class(); - visiting_mixed_hierarchy(); - } + known_visitor_known_class(); + visitor_not_visiting_some_class(); + visiting_mixed_hierarchy(); + } + + void known_visitor_known_class() + { + Boss x1; + BigBoss x2; - void known_visitor_known_class() - { - Boss x1; - BigBoss x2; - - // masquerade as HomoSapiens... - HomoSapiens& homo1 (x1); - HomoSapiens& homo2 (x2); - - cout << "=== Babbler meets Boss and BigBoss ===\n"; - Babbler bab; - homo1.apply (bab); - homo2.apply (bab); - } + // masquerade as HomoSapiens... + HomoSapiens& homo1 (x1); + HomoSapiens& homo2 (x2); - void visitor_not_visiting_some_class() - { - HomoSapiens x1; - Leader x2; - - HomoSapiens& homo1 (x1); - HomoSapiens& homo2 (x2); - - cout << "=== Babbler meets HomoSapiens and Leader ===\n"; - Babbler bab; - homo1.apply (bab); // doesn't visit HomoSapiens - homo2.apply (bab); // treats Leader as Boss - } - - void visiting_mixed_hierarchy() - { - Leader x1; - Visionary x2; - - HomoSapiens& homo1 (x1); - HomoSapiens& homo2 (x2); - Chief& chief1 (x1); - Chief& chief2 (x2); - Leader& lead1 (x1); - Leader& lead2 (x2); - - Blatherer bla; - cout << "=== Blatherer meets Leader and Visionary masqueraded as Chief ===\n"; - chief1.apply (bla); // catch-all, because Blatherer doesn't declare to be applicalbe to Leader - chief2.apply (bla); // treat(Visionary&) resolved to treat(Leader&) as expected - - Babbler bab; - Tool& tool1 (bab); - cout << "=== Babbler masqueraded as Tool meets Leader and Visionary masqueraded as HomoSapiens ===\n"; - homo1.apply (tool1); // because just going through the VTable, the dispatch works as expected - homo2.apply (tool1); // same here (in both cases, the call is resolved to treat(Boss&) as expected) - - cout << "=== Babbler masqueraded as Tool meets Leader and Visionary masqueraded as Leader ===\n"; - lead1.apply (tool1); // nothing happens, because Leader here is treated by his HomoSapiens base - lead2.apply (tool1); // surprisingly the VTable mechanism is choosen here, resulting in an correct dispatch - - // note: the following doesn't compile (an this is a feature, not a bug): - - // "Chief chief" : is abstract, because the Visitable-Template enforces implementing - // the "apply(TOOL&)" function, either directly or via the - // DEFINE_PROCESSABLE_BY macro + cout << "=== Babbler meets Boss and BigBoss ===\n"; + Babbler bab; + homo1.apply (bab); + homo2.apply (bab); + } + + void visitor_not_visiting_some_class() + { + HomoSapiens x1; + Leader x2; + + HomoSapiens& homo1 (x1); + HomoSapiens& homo2 (x2); + + cout << "=== Babbler meets HomoSapiens and Leader ===\n"; + Babbler bab; + homo1.apply (bab); // doesn't visit HomoSapiens + homo2.apply (bab); // treats Leader as Boss + } + + void visiting_mixed_hierarchy() + { + Leader x1; + Visionary x2; + + HomoSapiens& homo1 (x1); + HomoSapiens& homo2 (x2); + Chief& chief1 (x1); + Chief& chief2 (x2); + Leader& lead1 (x1); + Leader& lead2 (x2); + + Blatherer bla; + cout << "=== Blatherer meets Leader and Visionary masqueraded as Chief ===\n"; + chief1.apply (bla); // catch-all, because Blatherer doesn't declare to be applicalbe to Leader + chief2.apply (bla); // treat(Visionary&) resolved to treat(Leader&) as expected + + Babbler bab; + Tool& tool1 (bab); + cout << "=== Babbler masqueraded as Tool meets Leader and Visionary masqueraded as HomoSapiens ===\n"; + homo1.apply (tool1); // because just going through the VTable, the dispatch works as expected + homo2.apply (tool1); // same here (in both cases, the call is resolved to treat(Boss&) as expected) + + cout << "=== Babbler masqueraded as Tool meets Leader and Visionary masqueraded as Leader ===\n"; + lead1.apply (tool1); // nothing happens, because Leader here is treated by his HomoSapiens base + lead2.apply (tool1); // surprisingly the VTable mechanism is choosen here, resulting in an correct dispatch + + // note: the following doesn't compile (an this is a feature, not a bug): + + // "Chief chief" : is abstract, because the Visitable-Template enforces implementing + // the "apply(TOOL&)" function, either directly or via the + // DEFINE_PROCESSABLE_BY macro - } - }; - - - /** Register this test class... */ - LAUNCHER (VisitingToolExtended_test, "unit common"); - - - - } // namespace test - - } // namespace visitor - -} // namespace lumiera + } + }; + + + /** Register this test class... */ + LAUNCHER (VisitingToolExtended_test, "unit common"); + + + +}}} // namespace lib::visitor::test2 diff --git a/tests/lib/visitingtooltest.cpp b/tests/lib/visitingtooltest.cpp index 80a8eee5a..7da240e75 100644 --- a/tests/lib/visitingtooltest.cpp +++ b/tests/lib/visitingtooltest.cpp @@ -32,132 +32,127 @@ using std::string; using std::cout; -namespace lumiera { - namespace visitor { - namespace test1 { +namespace lib { +namespace visitor { +namespace test1 { + + typedef visitor::Tool<> VisitingTool; + + class HomoSapiens : public Visitable<> + { + public: + DEFINE_PROCESSABLE_BY (VisitingTool); + }; + + class Boss : public HomoSapiens + { + public: + DEFINE_PROCESSABLE_BY (VisitingTool); + }; + + class BigBoss : public Boss + { + public: + DEFINE_PROCESSABLE_BY (VisitingTool); + }; + + class Visionary : public Boss + { + DEFINE_PROCESSABLE_BY (VisitingTool); + }; + + class Leader : public Visionary + { + }; + + + + class VerboseVisitor + : public VisitingTool + { + protected: + void talk_to (string guy) + { + cout << format ("Hello %s, nice to meet you...\n") % guy; + } + }; + + class Babbler + : public Applicable< Babbler + , Types<Boss,BigBoss,Visionary>::List // dispatch calls to this types + , VerboseVisitor + > + { + public: + void treat (Boss&) { talk_to("Boss"); } + void treat (BigBoss&) { talk_to("Big Boss"); } + }; + + // note the following details: + // - Babbler "forgot" to declare being applicable to HomoSapiens + // - we have new derived class Leader without separate "apply()"-implementation + + + + + /************************************************************************* + * @test our lib implementation of the visitor pattern. + * Defines a hierarchy of test classes to check the following cases + * - calling the correct visiting tool specialised function + * for given concrete hierarchy classes + * - visiting tool not declaring to visit some class + * is silently ignored by default + * - newly added classes will be handled by existing + * functions according to inheritance relations + */ + class VisitingTool_test : public Test + { + virtual void + run (Arg) + { + known_visitor_known_class(); + visiting_extended_hierarchy(); + } - typedef visitor::Tool<> VisitingTool; - - class HomoSapiens : public Visitable<> + void + known_visitor_known_class() { - public: - DEFINE_PROCESSABLE_BY (VisitingTool); - }; - - class Boss : public HomoSapiens - { - public: - DEFINE_PROCESSABLE_BY (VisitingTool); - }; - - class BigBoss : public Boss - { - public: - DEFINE_PROCESSABLE_BY (VisitingTool); - }; - - class Visionary : public Boss - { - DEFINE_PROCESSABLE_BY (VisitingTool); - }; - - class Leader : public Visionary - { - }; - - - - class VerboseVisitor - : public VisitingTool - { - protected: - void talk_to (string guy) - { - cout << format ("Hello %s, nice to meet you...\n") % guy; - } - }; - - class Babbler - : public Applicable< Babbler - , Types<Boss,BigBoss,Visionary>::List // dispatch calls to this types - , VerboseVisitor - > - { - public: - void treat (Boss&) { talk_to("Boss"); } - void treat (BigBoss&) { talk_to("Big Boss"); } - }; - - // note the following details: - // - Babbler "forgot" to declare being applicable to HomoSapiens - // - we have new derived class Leader without separate "apply()"-implementation - - - - - /************************************************************************* - * @test our lib implementation of the visitor pattern. - * Defines a hierarchy of test classes to check the following cases - * <ul><li>calling the correct visiting tool specialised function - * for given concrete hierarchy classes</li> - * <li>visiting tool not declaring to visit some class - * is silently ignored by default</li> - * <li>newly added classes will be handled by existing - * functions according to inheritance relations</li> - * </ul> - */ - class VisitingTool_test : public Test - { - virtual void - run (Arg) - { - known_visitor_known_class(); - visiting_extended_hierarchy(); - } + Boss x1; + BigBoss x2; - void - known_visitor_known_class() - { - Boss x1; - BigBoss x2; - - // masquerade as HomoSapiens... - HomoSapiens& homo1 (x1); - HomoSapiens& homo2 (x2); - - cout << "=== Babbler meets Boss and BigBoss ===\n"; - Babbler bab; - VisitingTool& vista (bab); - homo1.apply (vista); - homo2.apply (vista); - } + // masquerade as HomoSapiens... + HomoSapiens& homo1 (x1); + HomoSapiens& homo2 (x2); - void - visiting_extended_hierarchy() - { - HomoSapiens x1; - Leader x2; - - HomoSapiens& homo1 (x1); - HomoSapiens& homo2 (x2); - - cout << "=== Babbler meets HomoSapiens and Leader ===\n"; - Babbler bab; - VisitingTool& vista (bab); - homo1.apply (vista); // silent error handler (not Applicable to HomoSapiens) - homo2.apply (vista); // Leader handled as Visionary and treated as Boss - } - - }; + cout << "=== Babbler meets Boss and BigBoss ===\n"; + Babbler bab; + VisitingTool& vista (bab); + homo1.apply (vista); + homo2.apply (vista); + } - - /** Register this test class... */ - LAUNCHER (VisitingTool_test, "unit common"); - - - - } // namespace test1 - - } // namespace visitor - -} // namespace lumiera + void + visiting_extended_hierarchy() + { + HomoSapiens x1; + Leader x2; + + HomoSapiens& homo1 (x1); + HomoSapiens& homo2 (x2); + + cout << "=== Babbler meets HomoSapiens and Leader ===\n"; + Babbler bab; + VisitingTool& vista (bab); + homo1.apply (vista); // silent error handler (not Applicable to HomoSapiens) + homo2.apply (vista); // Leader handled as Visionary and treated as Boss + } + + }; + + + /** Register this test class... */ + LAUNCHER (VisitingTool_test, "unit common"); + + + +}}} // namespace lib::visitor::test1 From 7fd28e23ebb516a46e87d7a0bd6eab91bab98089 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 3 Dec 2011 03:14:25 +0100 Subject: [PATCH 290/496] cleanup configflags to use uint instead of char using char for those flag template parameters was never a good idea. It won't save us any space and makes debugging harder --- src/lib/meta/configflags.hpp | 44 ++++++++++++------------- src/proc/engine/nodeoperation.hpp | 6 ++-- tests/lib/meta/config-flags-test.cpp | 2 +- tests/lib/meta/typelist-diagnostics.hpp | 27 +++++++-------- 4 files changed, 40 insertions(+), 39 deletions(-) diff --git a/src/lib/meta/configflags.hpp b/src/lib/meta/configflags.hpp index 4e71bfd9e..b80dae229 100644 --- a/src/lib/meta/configflags.hpp +++ b/src/lib/meta/configflags.hpp @@ -58,15 +58,15 @@ namespace meta{ const size_t CONFIG_FLAGS_MAX = 5; - template<char bit> struct Flag { typedef Flag ID; }; + template<uint bit> struct Flag { typedef Flag ID; }; template<> struct Flag<0> { typedef NullType ID; }; - template< char f1=0 - , char f2=0 - , char f3=0 - , char f4=0 - , char f5=0 + template< uint f1=0 + , uint f2=0 + , uint f3=0 + , uint f4=0 + , uint f5=0 > struct Flags { @@ -81,11 +81,11 @@ namespace meta{ }; - template< char f1=0 - , char f2=0 - , char f3=0 - , char f4=0 - , char f5=0 + template< uint f1=0 + , uint f2=0 + , uint f3=0 + , uint f4=0 + , uint f5=0 > struct Config ///< distinct type representing a configuration { @@ -95,15 +95,15 @@ namespace meta{ - template<char Fl, class CONF> + template<uint Fl, class CONF> struct ConfigSetFlag; ///< set (prepend) the Flag to the given config - template< char Fl - , char f1 - , char f2 - , char f3 - , char f4 - , char IGN + template< uint Fl + , uint f1 + , uint f2 + , uint f3 + , uint f4 + , uint IGN > struct ConfigSetFlag<Fl, Config<f1,f2,f3,f4,IGN> > { @@ -119,7 +119,7 @@ namespace meta{ typedef CONF Config; typedef Config Type; }; - template<char Fl, class FLAGS, class CONF> + template<uint Fl, class FLAGS, class CONF> struct BuildConfigFromFlags< Node<Flag<Fl>,FLAGS>, CONF> { typedef typename ConfigSetFlag< Fl @@ -134,8 +134,8 @@ namespace meta{ namespace { - /** helper comparing enum values and chars (flags) */ - template<char ii, char jj> + /** helper comparing enum values and flags */ + template<uint ii, uint jj> struct maxC { enum{ VAL = ii < jj? jj : ii }; @@ -157,7 +157,7 @@ namespace meta{ template<class FLAGS> struct FlagInfo; - template<char ff, class FLAGS> + template<uint ff, class FLAGS> struct FlagInfo<Node<Flag<ff>, FLAGS> > { enum{ BITS = maxC< ff, FlagInfo<FLAGS>::BITS> ::VAL diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp index a715353bf..7e48b5763 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -282,7 +282,7 @@ namespace config { ///////////////////////////: but BufferProvider selection is going to be solved differently anyway, see Ticket #249 template<class CONF> struct SelectBuffProvider { typedef AllocBufferFromParent Type; }; - template<char PROC_ign, char INPLA_ign> + template<uint PROC_ign, uint INPLA_ign> struct SelectBuffProvider< Config<CACHING, PROC_ign, INPLA_ign> > { typedef AllocBufferFromCache Type; }; @@ -290,7 +290,7 @@ namespace config { struct Strategy ; - template<char INPLACE_ign> + template<uint INPLACE_ign> struct Strategy< Config<CACHING,PROCESS,INPLACE_ign> > : QueryCache< AllocBufferTable< @@ -302,7 +302,7 @@ namespace config { OperationBase > > > > > > > { }; - template<char INPLACE_ign> + template<uint INPLACE_ign> struct Strategy< Config<PROCESS,INPLACE_ign> > : AllocBufferTable< PullInput< diff --git a/tests/lib/meta/config-flags-test.cpp b/tests/lib/meta/config-flags-test.cpp index 87649556f..9319a4e39 100644 --- a/tests/lib/meta/config-flags-test.cpp +++ b/tests/lib/meta/config-flags-test.cpp @@ -102,7 +102,7 @@ namespace test { template<> struct Maybe<Conf1> : Indeed { enum{ CODE = 10 }; }; template<> struct Maybe<Conf3> : Indeed { enum{ CODE = 30 }; }; - template<char Fl> + template<uint Fl> struct Maybe<Config<TWO,Fl> > { typedef Yes_t is_defined; diff --git a/tests/lib/meta/typelist-diagnostics.hpp b/tests/lib/meta/typelist-diagnostics.hpp index 721498c45..d55a84fe2 100644 --- a/tests/lib/meta/typelist-diagnostics.hpp +++ b/tests/lib/meta/typelist-diagnostics.hpp @@ -22,6 +22,7 @@ /** @file typelist-diagnostics.hpp + ** Support for writing metaprogramming unit-tests dealing with typelists and flags. ** a Printer template usable for debugging the structure of a typelist built ** upon some simple debugging-style types. Examples being a Num<int> template, ** or the Flag type. A Printer type generated from this template provides @@ -58,9 +59,9 @@ namespace meta { /** dummy interface / baseclass for diagnostics */ struct Numz { - char o_; - Numz (char x =0) : o_(x) { } - operator char () const { return o_; } + uint o_; + Numz (uint x =0) : o_(x) { } + operator uint () const { return o_; } }; @@ -72,18 +73,18 @@ namespace meta { { enum{ VAL=I }; - Num (char x = char(I)) : Numz(x) { } + Num (uint x = uint(I)) : Numz(x) { } }; /* some forwards used by config-flags-test.cpp */ - template<char bit> struct Flag; - template< char f1 - , char f2 - , char f3 - , char f4 - , char f5 + template<uint bit> struct Flag; + template< uint f1 + , uint f2 + , uint f3 + , uint f4 + , uint f5 > struct Config; @@ -97,7 +98,7 @@ namespace meta { - namespace test { //< unit tests covering typelist manipulating templates + namespace test { // unit tests covering typelist manipulating templates namespace { // hidden internals for diagnostics.... using boost::format; @@ -129,7 +130,7 @@ namespace meta { static string print () { return str( fmt % uint(Num<I>::VAL) % BASE::print()); } }; - template<class BASE, char Fl> + template<class BASE, uint Fl> struct Printer<Flag<Fl>, BASE> ///< display the presence of a Flag in the typelist : BASE { @@ -168,7 +169,7 @@ namespace meta { } }; - template<char f1, char f2, char f3, char f4, char f5, class BASE> + template<uint f1, uint f2, uint f3, uint f4, uint f5, class BASE> struct Printer<Config<f1,f2,f3,f4,f5>, BASE> : BASE { From 77548e74c6f6161e84d70f129c029b93c9cc1e15 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 3 Dec 2011 03:18:03 +0100 Subject: [PATCH 291/496] document a weakness of boost::hash for strings --- src/proc/asset/entry-id.hpp | 9 ++ tests/40components.tests | 5 + tests/components/proc/asset/entry-id-test.cpp | 15 ++- tests/lib/hash-generator-test.cpp | 113 ++++++++++++++++++ tests/lib/time/quantiser-basics-test.cpp | 2 +- 5 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 tests/lib/hash-generator-test.cpp diff --git a/src/proc/asset/entry-id.hpp b/src/proc/asset/entry-id.hpp index 7667c8ec6..445597fb8 100644 --- a/src/proc/asset/entry-id.hpp +++ b/src/proc/asset/entry-id.hpp @@ -82,12 +82,16 @@ namespace asset { * conjunction with LUID. How to create a LuidH instance, if not generating * a new random value. How to make EntryID and asset::Ident interchangeable, /////////TICKET #739 * which would require both to yield the same hash values.... + * @warning there is a weakness in boost::hash for strings of running numbers, causing + * collisions already for a small set with less than 100000 entries. + * To ameliorate the problem, we hash the symbol twice /////////TICKET #865 * @warning this code isn't portable and breaks if sizeof(size_t) < sizeof(void*) */ inline LuidH buildHash (string const& sym, HashVal seed =0) { boost::hash_combine(seed, sym); + boost::hash_combine(seed, sym); ////////////////////////TICKET #865 lumiera_uid tmpLUID; lumiera_uid_set_ptr (&tmpLUID, reinterpret_cast<void*> (seed)); return reinterpret_cast<LuidH&> (tmpLUID); @@ -173,6 +177,11 @@ namespace asset { * EntryID template share a common baseclass, usable for type erased common registration. * @todo currently storing the symbolic-ID as string. It should be a lib::Symbol, * but this isn't possible unless we use a symbol table. //////TICKET #158 + * @warning there is a weakness in boost::hash applied to strings. EntryID by default + * generates symbolic IDs from a type prefix plus a running number, which causes + * collisions already with less than 100000 entries. + * @todo As a temporary workaround, we hash the symbolic ID twice, but we should look + * into using a better hash function ////////////TICKET #865 * * @see mobject::session::Track */ diff --git a/tests/40components.tests b/tests/40components.tests index da70c1ce4..9baa7e6b5 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -356,6 +356,11 @@ return: 0 END +TEST "HashGenerator_test" HashGenerator_test <<END +return: 0 +END + + TEST "HashIndexed_test" HashIndexed_test <<END return: 0 END diff --git a/tests/components/proc/asset/entry-id-test.cpp b/tests/components/proc/asset/entry-id-test.cpp index 757d38092..7c3fcd41d 100644 --- a/tests/components/proc/asset/entry-id-test.cpp +++ b/tests/components/proc/asset/entry-id-test.cpp @@ -38,6 +38,7 @@ using lib::test::showSizeof; using lib::test::randStr; using util::isSameObject; +using util::contains; using util::and_all; using lib::Literal; using std::string; @@ -257,22 +258,28 @@ namespace test { { Hashtable tab; - for (uint i=0; i<1000; ++i) /////////////////////////////////////TICKET #587 we get strange collisions for 10000 entries + for (uint i=0; i<10000; ++i) //////////////////TICKET #865 hash collisions for 100000 entries { DummyID dummy; tab[dummy] = string(dummy); } - CHECK (1000 == tab.size()); - CHECK (and_all (tab, verifyEntry)); + CHECK (10000 == tab.size()); } static bool verifyEntry (Hashtable::value_type entry) { - return entry.second == string(entry.first); + return checkForHashCollision(string(entry.first), entry.second); + } + + static bool + checkForHashCollision(string const& key, string const& val) + { + if (key != val) cout << "Hash collision: "<<key<<" != "<<val<< endl; + return key == val; } }; diff --git a/tests/lib/hash-generator-test.cpp b/tests/lib/hash-generator-test.cpp new file mode 100644 index 000000000..ee9a87525 --- /dev/null +++ b/tests/lib/hash-generator-test.cpp @@ -0,0 +1,113 @@ +/* + HashGenerator(Test) - hash value generation details + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/util.hpp" + +#include <boost/functional/hash.hpp> +#include <boost/lexical_cast.hpp> +#include <iostream> +#include <string> +#include <map> + +using boost::lexical_cast; +using util::contains; +using std::string; +using std::cout; +using std::endl; + + +namespace lib { +namespace test{ + + + + + + /*************************************************************************** + * @test cover various detail aspects regarding hash value generation + * - weakness of boost::hash + * + * @see HashIndexed_test + * @see EntryID_test + */ + class HashGenerator_test : public Test + { + + virtual void + run (Arg) + { + demonstrate_boost_hash_weakness(); + } + + + /** @test demonstrate a serious weakness of boost::hash for strings. + * When hashing just the plain string representation of integers, + * we get collisions already with small numbers below 100000. + * This is counter-intuitive, as the generated hash values + * are 17 digits long and could span much wider scale. + * + * This problem is especially dangerous when storing objects keyed + * by a string-id, which is generated from running numbers. + */ + void + demonstrate_boost_hash_weakness () + { + typedef boost::hash<string> BoostStringHasher; + typedef std::map<size_t, string> StringsTable; + + BoostStringHasher hashFunction; + StringsTable hashValues; + string prefix = "Entry."; + uint collisions(0); + for (uint i=0; i<100000; ++i) + { + string candidate = prefix + lexical_cast<string> (i); + size_t hashVal = hashFunction(candidate); + + if (contains (hashValues, hashVal)) + { + ++collisions; + string other = hashValues[hashVal]; + cout << "Duplicate at "<< i << endl; + cout << "existing--->" << other << endl; + cout << "new-------->" << candidate << endl; + + size_t exHash = hashFunction(other); + size_t newHash = hashFunction(candidate); + cout << "hash-ex---->" << exHash << endl; + cout << "hash_new--->" << newHash << endl; + } + hashValues[hashVal] = candidate; + } + CHECK (0 < collisions, "boost::hash for strings is expected to produce collisions"); + } + + }; + + + /** Register this test class... */ + LAUNCHER (HashGenerator_test, "unit common"); + + +}} // namespace lib::test diff --git a/tests/lib/time/quantiser-basics-test.cpp b/tests/lib/time/quantiser-basics-test.cpp index b503e3d29..cca585491 100644 --- a/tests/lib/time/quantiser-basics-test.cpp +++ b/tests/lib/time/quantiser-basics-test.cpp @@ -88,7 +88,7 @@ namespace test{ FixedFrameQuantiser fixQ(25); uint frames = (rand() % MAX_FRAMES); - FSecs dirt = (F25 / (rand() % DIRT_GRAIN)); + FSecs dirt = (F25 / (1 + rand() % DIRT_GRAIN)); Time rawTime (dirt + frames*F25); From 34f2de641fe3db1e419dcaa545695a69415f6fca Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 3 Dec 2011 01:56:30 +0100 Subject: [PATCH 292/496] Remove Debian/Lenny workarounds definitively requiring Squeeze now --- SConstruct | 7 +++---- src/gui/widgets/timecode-widget.cpp | 20 +++++++++----------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/SConstruct b/SConstruct index 038958348..7abd1217b 100644 --- a/SConstruct +++ b/SConstruct @@ -1,6 +1,6 @@ # -*- python -*- ## -## SConstruct - SCons based build-sytem for Lumiera +## SConstruct - SCons based build-system for Lumiera ## # Copyright (C) Lumiera.org @@ -52,8 +52,9 @@ installIcon = '#$DESTDIR/share/lumiera/icons' installUIRes = '#$DESTDIR/share/lumiera/' installConf = '#$DESTDIR/lib/lumiera/config' -localDefinitions = locals() #-----------------------------------Configuration +localDefinitions = locals() + @@ -87,8 +88,6 @@ def setupBasicEnvironment(localDefinitions): ) handleVerboseMessages(env) - env.Append ( CCCOM=' -std=gnu99') - env.Append ( SHCCCOM=' -std=gnu99') # workaround for a bug: CCCOM currently doesn't honour CFLAGS, only CCFLAGS 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 ' diff --git a/src/gui/widgets/timecode-widget.cpp b/src/gui/widgets/timecode-widget.cpp index b46907e76..4fdd9180f 100644 --- a/src/gui/widgets/timecode-widget.cpp +++ b/src/gui/widgets/timecode-widget.cpp @@ -189,8 +189,7 @@ TimeCode::set_widget_name(string name) void TimeCode::setup_events() { - // FIXME: change to set_can_focus(true) once Debian squeeze is released - clock_base.set_flags(CAN_FOCUS); + clock_base.set_can_focus(true); const Gdk::EventMask eventMask = Gdk::BUTTON_PRESS_MASK| @@ -210,15 +209,14 @@ TimeCode::setup_events() ms_seconds_ebox.add_events(eventMask); audio_frames_ebox.add_events(eventMask); - // FIXME: change to set_can_focus(true) once Debian squeeze is released - hours_ebox.set_flags(CAN_FOCUS); - minutes_ebox.set_flags(CAN_FOCUS); - seconds_ebox.set_flags(CAN_FOCUS); - frames_ebox.set_flags(CAN_FOCUS); - audio_frames_ebox.set_flags(CAN_FOCUS); - ms_hours_ebox.set_flags(CAN_FOCUS); - ms_minutes_ebox.set_flags(CAN_FOCUS); - ms_seconds_ebox.set_flags(CAN_FOCUS); + hours_ebox.set_can_focus(true); + minutes_ebox.set_can_focus(true); + seconds_ebox.set_can_focus(true); + frames_ebox.set_can_focus(true); + audio_frames_ebox.set_can_focus(true); + ms_hours_ebox.set_can_focus(true); + ms_minutes_ebox.set_can_focus(true); + ms_seconds_ebox.set_can_focus(true); hours_ebox.signal_motion_notify_event().connect(bind(mem_fun( *this, &TimeCode::field_motion_notify_event), SMPTE_Hours)); From 08d330310fcda376fa44ac7c6264128841bc2394 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 3 Dec 2011 05:46:36 +0100 Subject: [PATCH 293/496] cleanup unnecessary builder function and flags in SCons build --- SConstruct | 11 +++++------ admin/scons/Buildhelper.py | 19 +++++-------------- admin/scons/LumieraEnvironment.py | 10 +++++----- tests/SConscript | 2 +- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/SConstruct b/SConstruct index 7abd1217b..4fbfa0b84 100644 --- a/SConstruct +++ b/SConstruct @@ -332,10 +332,10 @@ def defineBuildTargets(env, artifacts): We use a custom function to declare a whole tree of srcfiles. """ - lLib = env.SharedLibrary('lumiera', srcSubtree(env,'src/lib'), install=True) - lApp = env.SharedLibrary('lumieracommon', srcSubtree(env,'src/common'), install=True, LIBS=lLib) - lBack = env.SharedLibrary('lumierabackend', srcSubtree(env,'src/backend'),install=True) - lProc = env.SharedLibrary('lumieraproc', srcSubtree(env,'src/proc'), install=True) + 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 @@ -363,8 +363,7 @@ def defineBuildTargets(env, artifacts): envGtk.mergeConf(['gtkmm-2.4','gthread-2.0','cairomm-1.0','gdl','xv','xext','sm']) envGtk.Append(LIBS=core) - objgui = srcSubtree(envGtk,'src/gui', appendCPP='LUMIERA_PLUGIN') - guimodule = envGtk.LumieraPlugin('gtk_gui', objgui, install=True) + 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'] diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index e5d111885..990fa5921 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -49,20 +49,11 @@ def isHelpRequest(): -def srcSubtree(env,tree,isShared=True,builder=None,appendCPP=None, **args): - """ convenience wrapper: scans the given subtree, which is - relative to the current SConscript, find all source files and - declare them as Static or SharedObjects for compilation +def srcSubtree(tree, **args): + """ convenience wrapper: scan the given subtree, which is relative + to the current SConscript, and find all source files. """ - if appendCPP: env.Append(CPPDEFINES=appendCPP) - root = env.subst(tree) # expand Construction Vars - if not builder: - if isShared: - builder = lambda f: env.SharedObject(f, **args) - else: - builder = lambda f: env.Object(f, **args) - - return [builder(f) for f in scanSubtree(root)] + return list(scanSubtree(tree, **args)) @@ -161,7 +152,7 @@ def createPlugins(env, dir, **kw): @return: a list of build nodes defining a plugin for each of these source trees. """ return [env.LumieraPlugin( getDirname(tree) - , srcSubtree(env, tree, appendCPP='LUMIERA_PLUGIN') + , srcSubtree(tree) , **kw ) for tree in findSrcTrees(dir) diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index c0ce77375..1c314401b 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -205,8 +205,8 @@ def register_LumieraResourceBuilder(env): class WrappedStandardExeBuilder(SCons.Util.Proxy): """ Helper to add customisations and default configurations to SCons standard builders. The original builder object is wrapped and most calls are simply forwarded to this - wrapped object by Python magic. But some calls are intecepted in order to inject - suitalbe default configuration based on the project setup. + wrapped object by Python magic. But some calls are intercepted in order to inject + suitable default configuration based on the project setup. """ def __init__(self, originalBuilder): @@ -291,7 +291,7 @@ class LumieraModuleBuilder(WrappedStandardExeBuilder): explicit spec, falling back on the lib filename """ if 'soname' in kw: - soname = self.subst(kw['soname']) # explicitely defined by user + soname = self.subst(kw['soname']) # explicitly defined by user else: # else: use the library filename as DT_SONAME if SCons.Util.is_String(target): pathname = target.strip() @@ -331,7 +331,7 @@ class LumieraPluginBuilder(LumieraModuleBuilder): def register_LumieraCustomBuilders (lumiEnv): - """ install the customised builder versions tightly integrated with our buildsystem. + """ install the customised builder versions tightly integrated with our build system. Especially, these builders automatically add the build and installation locations and set the RPATH and SONAME in a way to allow a relocatable Lumiera directory structure """ @@ -362,7 +362,7 @@ def register_LumieraCustomBuilders (lumiEnv): action = Action(makeLink, "Install link: $TARGET -> "+srcSpec) env.Command (target,source, action) - # adding SymLink direclty as method on the environment object + # adding SymLink directly as method on the environment object # Probably that should better be a real builder, but I couldn't figure out # how to get the linktext through literally, which is necessary for relative links. # Judging from the sourcecode of SCons.Builder.BuilderBase, there seems to be no way diff --git a/tests/SConscript b/tests/SConscript index dd3584595..abbe86d35 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -29,7 +29,7 @@ def testExecutable(env,tree, exeName=None, obj=None): if obj: obj = [path.join(tree,name) for name in obj] else: - obj = srcSubtree(env,tree, isShared=False) # use all sourcefiles found in subtree + obj = srcSubtree(tree) # use all sourcefiles found in subtree if not exeName: exeName = 'test-%s' % tree return env.Program(exeName, obj + core) From 07002ab3af61138ddcb5aa70e7776f6922c387d2 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 3 Dec 2011 06:10:12 +0100 Subject: [PATCH 294/496] SCons: new build target for experimental code --- SConstruct | 6 +++--- research/DIR_INFO | 1 + research/SConscript | 21 +++++++++++++++++++++ {src/tool => research}/try.cpp | 0 src/tool/SConscript | 1 - 5 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 research/DIR_INFO create mode 100644 research/SConscript rename {src/tool => research}/try.cpp (100%) diff --git a/SConstruct b/SConstruct index 4fbfa0b84..16c8c3123 100644 --- a/SConstruct +++ b/SConstruct @@ -190,6 +190,7 @@ USAGE: scons [-c] [OPTS] [key=val [key=val...]] [TARGETS] 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) @@ -370,13 +371,12 @@ def defineBuildTargets(env, artifacts): ) # call subdir SConscript(s) for independent components - SConscript(dirs=['src/tool'], exports='env artifacts core') - SConscript(dirs=['tests'], exports='env artifacts core') + SConscript(dirs=['src/tool','research','tests'], exports='env artifacts core') def definePostBuildTargets(env, artifacts): - """ define further actions after the core build (e.g. Documentaion). + """ define further actions after the core build (e.g. Documentation). define alias targets to trigger the installing. """ build = env.Alias('build', ( artifacts['lumiera'] diff --git a/research/DIR_INFO b/research/DIR_INFO new file mode 100644 index 000000000..59b2583f4 --- /dev/null +++ b/research/DIR_INFO @@ -0,0 +1 @@ +Experiments and Investigations. Not installed diff --git a/research/SConscript b/research/SConscript new file mode 100644 index 000000000..0fef0dae8 --- /dev/null +++ b/research/SConscript @@ -0,0 +1,21 @@ +# -*- python -*- +## +## SConscript - SCons buildscript for experiments and investigations. +## Things defined here usuall won't be installed +## + +Import('env','artifacts','core') + +support_lib = artifacts['support'] + + + +# build additional test and administrative tools.... +experiments = [ env.Program('try', ['try.cpp'] + support_lib) #### to try out some feature... + ] + +# +# define Phony targets +# - 'scons research' triggers building of experimental code +# +env.Alias('research', experiments ) diff --git a/src/tool/try.cpp b/research/try.cpp similarity index 100% rename from src/tool/try.cpp rename to research/try.cpp diff --git a/src/tool/SConscript b/src/tool/SConscript index 583b7032f..49f03addf 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -17,7 +17,6 @@ rsvg = envSvg.Program('rsvg-convert','rsvg-convert.c') # build additional test and administrative tools.... artifacts['tools'] = [ env.Program('hello-world','hello.c', install=True) #### hello world (checks C build) - + env.Program('try', ['try.cpp'] + support_lib) #### to try out some feature... + luidgen + rsvg ] From aef3d50ffde789c8dc9cedcf36c46c6e9e5e28fe Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 8 Dec 2011 23:02:19 +0100 Subject: [PATCH 295/496] implement basics of the diagnostic OutputSlot ...specific diagnostic facilities still lacking --- src/lib/multifact.hpp | 4 +- src/proc/engine/buffhandle-attach.hpp | 2 +- .../engine/diagnostic-buffer-provider.hpp | 2 +- .../engine/tracking-heap-block-provider.cpp | 4 +- .../engine/tracking-heap-block-provider.hpp | 2 +- src/proc/play/output-slot-connection.hpp | 8 +- src/proc/play/output-slot.hpp | 2 +- .../proc/play/diagnostic-output-slot.hpp | 100 +++++++++++++++--- .../proc/play/output-slot-protocol-test.cpp | 1 - 9 files changed, 97 insertions(+), 28 deletions(-) diff --git a/src/lib/multifact.hpp b/src/lib/multifact.hpp index b598a198d..29bef6bce 100644 --- a/src/lib/multifact.hpp +++ b/src/lib/multifact.hpp @@ -21,7 +21,7 @@ */ /** @file multifact.hpp - ** Building blocks of a configurable factory, generating families of related objects. + ** Building blocks to create a configurable factory, generating families of related objects. ** Serving the "classical" factory situation: obtaining objects of various kinds, which ** are related somehow (usually through an common interface). The creation of these ** objects is non-trivial while number and exact parametrisation aren't known beforehand @@ -70,7 +70,7 @@ namespace lib { namespace factory { - //////TODO: couldn't these wrappers be extracted into a separate header? + /////////////////////////////////TICKET #470 : couldn't these wrappers be extracted into a separate header? /** * Dummy "wrapper", diff --git a/src/proc/engine/buffhandle-attach.hpp b/src/proc/engine/buffhandle-attach.hpp index aee29c386..f0447bcad 100644 --- a/src/proc/engine/buffhandle-attach.hpp +++ b/src/proc/engine/buffhandle-attach.hpp @@ -25,7 +25,7 @@ ** This extension is mostly helpful for writing unit-tests, and beyond that for the ** rather unusual case where we need to place an full-blown object into the buffer, ** instead of just plain data. A possible use case for this mechanism is to allow for - ** state pre calculation stream, feeding this local state to the individual render node + ** state per calculation stream, feeding this local state to the individual render node ** embedded into a "state frame". Some effect processors indeed need to maintain state ** beyond the single frame (e.g. averaging, integrating, sound compression), which usually ** is handled by applying an "instance" of that processor to the frames to be calculated diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp index 8355188b6..9d1f3f303 100644 --- a/src/proc/engine/diagnostic-buffer-provider.hpp +++ b/src/proc/engine/diagnostic-buffer-provider.hpp @@ -21,7 +21,7 @@ */ /** @file diagnostic-buffer-provider.hpp - ** An facility for writing unit-tests targeting the BufferProvider interface. + ** A facility for writing unit-tests targeting the BufferProvider interface. ** ** @see buffer-provider-protocol-test.cpp */ diff --git a/src/proc/engine/tracking-heap-block-provider.cpp b/src/proc/engine/tracking-heap-block-provider.cpp index c9f96aa28..695f5674c 100644 --- a/src/proc/engine/tracking-heap-block-provider.cpp +++ b/src/proc/engine/tracking-heap-block-provider.cpp @@ -309,7 +309,7 @@ namespace engine { TrackingHeapBlockProvider::locateBlock (HashVal typeID, void* storage) { diagn::BlockPool& pool = getBlockPoolFor (typeID); - diagn::Block* block4buffer = pool.find (storage); + diagn::Block* block4buffer = pool.find (storage); ////////////////////////////////TICKET #856 return block4buffer? block4buffer : searchInOutSeqeuence (storage); } @@ -317,7 +317,7 @@ namespace engine { diagn::Block* TrackingHeapBlockProvider::searchInOutSeqeuence (void* blockLocation) { - return pick_Block_by_storage (outSeq_, blockLocation); + return pick_Block_by_storage (outSeq_, blockLocation); ////////////////////////////////TICKET #856 } diff --git a/src/proc/engine/tracking-heap-block-provider.hpp b/src/proc/engine/tracking-heap-block-provider.hpp index d9aa53c26..a6db03431 100644 --- a/src/proc/engine/tracking-heap-block-provider.hpp +++ b/src/proc/engine/tracking-heap-block-provider.hpp @@ -66,7 +66,7 @@ namespace engine { /** - * Helper for a diagnostic BufferProvider: + * Helper for implementing a diagnostic BufferProvider: * A block of heap allocated storage, with the capability * to store some additional tracking information. */ diff --git a/src/proc/play/output-slot-connection.hpp b/src/proc/play/output-slot-connection.hpp index 4ab5bdc8b..e4e9b6787 100644 --- a/src/proc/play/output-slot-connection.hpp +++ b/src/proc/play/output-slot-connection.hpp @@ -52,7 +52,7 @@ #include <boost/noncopyable.hpp> #include <boost/scoped_ptr.hpp> //#include <string> -//#include <vector> +#include <vector> //#include <tr1/memory> @@ -66,7 +66,7 @@ namespace play { using lib::transform; using lib::iter_stl::eachElm; -//using std::vector; + using std::vector; //using std::tr1::shared_ptr; using boost::scoped_ptr; @@ -145,7 +145,7 @@ namespace play { : public OutputSlot::ConnectionState , public vector<CON> { - + typedef vector<CON> Connections; typedef OutputSlot::OpenedSinks OpenedSinks; @@ -161,7 +161,7 @@ namespace play { bool isActive() { - return 0 < vector<CON>::size(); + return 0 < Connections::size(); } diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index 095bfb5e1..6fe5b09a2 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -82,7 +82,7 @@ namespace play { protected: - /** Table to maintain connection state */ + /** active connections through this OutputSlot */ class ConnectionState; scoped_ptr<ConnectionState> state_; diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index e3c45cac1..4023132c2 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -37,7 +37,8 @@ #include "proc/play/output-slot-connection.hpp" #include "proc/engine/buffhandle.hpp" #include "proc/engine/tracking-heap-block-provider.hpp" -#include "lib/iter-source.hpp" ////////////TODO really going down that path...? +#include "lib/scoped-ptrvect.hpp" +#include "lib/iter-source.hpp" #include "proc/engine/testframe.hpp" //#include "lib/sync.hpp" @@ -55,6 +56,7 @@ namespace play { using proc::engine::BufferDescriptor; using proc::engine::test::TestFrame; using proc::engine::TrackingHeapBlockProvider; + namespace diagn = proc::engine::diagn; //using std::vector; using std::tr1::shared_ptr; @@ -127,12 +129,34 @@ namespace play { { INFO (engine_dbg, "releasing diagnostic output sequence"); } + + + /* === Diagnostic API === */ + + TestFrame* + accessEmittedFrame (uint frameNo) const + { + REQUIRE (buffProvider_); + if (frameNo <= buffProvider_->emittedCnt()) + return &buffProvider_->accessAs<TestFrame> (frameNo); + else + return 0; ////////////////////////////////TICKET #856 + } + + diagn::Block* + accessEmittedBuffer (uint bufferNo) const + { + REQUIRE (buffProvider_); + if (bufferNo <= buffProvider_->emittedCnt()) + return buffProvider_->access_emitted (bufferNo); + else + return 0; + } }; class SimulatedOutputSequences : public ConnectionStateManager<TrackingInMemoryBlockSequence> - , boost::noncopyable { TrackingInMemoryBlockSequence buildConnection() @@ -162,13 +186,28 @@ namespace play { static const uint MAX_CHANNELS = 5; - /* === hook into the OutputSlot frontend === */ + /** hook into the OutputSlot frontend */ ConnectionState* buildState() { return new SimulatedOutputSequences(MAX_CHANNELS); } + /** @internal is self-managed and non-copyable. + * Clients use #build() to get an instance */ + DiagnosticOutputSlot() { } + + /** @internal access the implementation object + * representing a single stream connection + */ + TrackingInMemoryBlockSequence const& + accessSequence (uint channel) + { + REQUIRE (!isFree(), "diagnostic OutputSlot not (yet) connected"); + REQUIRE (channel <= MAX_CHANNELS); + return static_cast<SimulatedOutputSequences&> (*state_).at(channel); + } + public: /** build a new Diagnostic Output Slot instance, @@ -177,50 +216,81 @@ namespace play { static OutputSlot& build() { - UNIMPLEMENTED ("Diagnostic Output Slot instance"); + static lib::ScopedPtrVect<OutputSlot> diagnosticSlots; + return diagnosticSlots.manage(new DiagnosticOutputSlot); } static DiagnosticOutputSlot& access (OutputSlot& to_investigate) { - UNIMPLEMENTED ("access the diagnostics data for the given OutputSlot instance"); + return dynamic_cast<DiagnosticOutputSlot&> (to_investigate); } + /* === diagnostics API === */ /** - * diagnostic facility to verify - * test data frames written to this - * Test/Dummy "output" + * diagnostic facility to verify test data frames + * written to this Test/Dummy "output". It exposes + * the emitted Data as a sequence of TestFrame objects. */ - struct OutputStreamProtocol - : lib::IterSource<TestFrame> + class OutputFramesLog + : public lib::IterSource<TestFrame> + , boost::noncopyable { - /////////////TODO: implement the extension points required to drive an IterSource + TrackingInMemoryBlockSequence const& outSeq_; + uint currentFrame_; + + + virtual Pos + firstResult () + { + REQUIRE (0 == currentFrame_); + return outSeq_.accessEmittedFrame (currentFrame_); + } + + virtual void + nextResult (Pos& pos) + { + ++currentFrame_; + pos = outSeq_.accessEmittedFrame(currentFrame_); + } + + public: + OutputFramesLog (TrackingInMemoryBlockSequence const& bs) + : outSeq_(bs) + , currentFrame_(0) + { } }; - typedef OutputStreamProtocol::iterator OutFrames; + typedef OutputFramesLog::iterator OutFrames; OutFrames getChannel (uint channel) { - UNIMPLEMENTED ("access output stream tracing entry"); + REQUIRE (channel < MAX_CHANNELS); + return OutputFramesLog::build( + new OutputFramesLog ( + accessSequence(channel))); } bool buffer_was_used (uint channel, FrameID frame) { - UNIMPLEMENTED ("determine if the denoted buffer was indeed used"); + diagn::Block* block = accessSequence(channel) + .accessEmittedBuffer(frame); + return block + && block->was_used(); } bool buffer_unused (uint channel, FrameID frame) { - UNIMPLEMENTED ("determine if the specified buffer was never touched/locked for use"); + return !buffer_was_used(channel, frame); } diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp index ac92dd62e..480b664c8 100644 --- a/tests/components/proc/play/output-slot-protocol-test.cpp +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -63,7 +63,6 @@ namespace test { virtual void run (Arg) { - UNIMPLEMENTED ("build a mock output slot and perform a full lifecycle"); verifyStandardCase(); } From e7ae9a30825fd4fd86238db1016f6add31b8d8c9 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 9 Dec 2011 01:00:50 +0100 Subject: [PATCH 296/496] introduce marker time values to designate corner cases --- src/lib/time/lumitime.cpp | 3 +++ src/lib/time/timevalue.hpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index f80dc9c6e..82de03645 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -41,6 +41,9 @@ namespace time { const Time Time::MIN ( TimeValue::buildRaw_(-_raw(Time::MAX) ) ); const Time Time::ZERO; + const Time Time::ANYTIME(Time::MAX); + const Time Time::NEVER (Time::MIN); + const Offset Offset::ZERO (Time::ZERO); diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 0c8fb2e15..4b429b0ea 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -284,6 +284,9 @@ namespace time { static const Time MIN ; static const Time ZERO; + static const Time ANYTIME; ///< border condition marker value. #ANYTIME <= any time value + static const Time NEVER; ///< border condition marker value. #NEVER >= any time value + explicit Time (TimeValue const& val =TimeValue(0)) : TimeValue(val) From 477b443191a3b7616f1b0e7ba85c3657dcc2a3ab Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 9 Dec 2011 01:03:02 +0100 Subject: [PATCH 297/496] implement diagnostic OutputSlot simple frame tracking ...should be enough to run tests against the OutputSlot interface --- src/lib/time/grid.hpp | 2 +- src/proc/asset/meta/time-grid.cpp | 5 +- .../engine/tracking-heap-block-provider.cpp | 2 +- .../proc/play/diagnostic-output-slot.hpp | 90 +++++++++++++++---- 4 files changed, 78 insertions(+), 21 deletions(-) diff --git a/src/lib/time/grid.hpp b/src/lib/time/grid.hpp index 7ace4a12e..fe068b385 100644 --- a/src/lib/time/grid.hpp +++ b/src/lib/time/grid.hpp @@ -46,7 +46,7 @@ namespace time { * grid point, usable for grid aligning values. * * \par usage - * For one there is the lib::time::Quantiser, which directly + * First off, there is the lib::time::Quantiser, which directly * implements this interface and plays a central role when it comes * to converting continuous time into any kind of frame based timecode. * Besides that, the session stores asset::TimeGrid definitions, which diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp index d37b77866..c46509787 100644 --- a/src/proc/asset/meta/time-grid.cpp +++ b/src/proc/asset/meta/time-grid.cpp @@ -127,6 +127,9 @@ namespace meta { * registered with the AssetManager. * @throw error::Config in case of invalid frames-per-second. The AssetManager * might raise further exception when asset registration fails. + * @note the newly created grid is automatically published through the Advice System. + * This allows client code to pick up that grid definition just by using the + * Grid ID, without requiring an explicit link to the session or Asset subsystem. * @todo currently (12/2010) the AssetManager is unable to detect duplicate assets. * Later on the intention is that in such cases, instead of creating a new grid * we'll silently return the already registered existing and equivalent grid. @@ -137,7 +140,7 @@ namespace meta { if (predecessor_) throw error::Invalid("compound and variable time grids are a planned feature" , error::LUMIERA_ERROR_UNIMPLEMENTED); - ENSURE (fps_, "infinite grid was not properly detected by FrameRate ctor"); + ENSURE (fps_, "infinite grid should have been detected by FrameRate ctor"); if (isnil (id_)) { diff --git a/src/proc/engine/tracking-heap-block-provider.cpp b/src/proc/engine/tracking-heap-block-provider.cpp index 695f5674c..ed056bbce 100644 --- a/src/proc/engine/tracking-heap-block-provider.cpp +++ b/src/proc/engine/tracking-heap-block-provider.cpp @@ -282,7 +282,7 @@ namespace engine { TrackingHeapBlockProvider::access_emitted (uint bufferID) { if (!withinOutputSequence (bufferID)) - return emptyPlaceholder; + return emptyPlaceholder; ////////////////////////////////TICKET #856 else return outSeq_[bufferID]; } diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index 4023132c2..4a6f758d2 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -35,16 +35,20 @@ #include "include/logging.h" #include "proc/play/output-slot.hpp" #include "proc/play/output-slot-connection.hpp" +#include "proc/asset/meta/time-grid.hpp" #include "proc/engine/buffhandle.hpp" #include "proc/engine/tracking-heap-block-provider.hpp" +#include "lib/time/timevalue.hpp" #include "lib/scoped-ptrvect.hpp" #include "lib/iter-source.hpp" +#include "lib/util.hpp" #include "proc/engine/testframe.hpp" //#include "lib/sync.hpp" #include <boost/noncopyable.hpp> //#include <string> //#include <vector> +#include <tr1/unordered_set> #include <tr1/memory> //#include <boost/scoped_ptr.hpp> @@ -53,6 +57,10 @@ namespace proc { namespace play { //using std::string; + using util::contains; + using lib::time::FrameRate; + using proc::asset::meta::PGrid; + using proc::asset::meta::TimeGrid; using proc::engine::BufferDescriptor; using proc::engine::test::TestFrame; using proc::engine::TrackingHeapBlockProvider; @@ -62,33 +70,61 @@ namespace play { using std::tr1::shared_ptr; //using boost::scoped_ptr; - - + /** + * Diagnostic output connection for a single channel, + * allowing to track generated frames and verify + * the processing protocol for output buffers. + */ class TrackingInMemoryBlockSequence : public OutputSlot::Connection { + typedef std::tr1::unordered_set<FrameID> FrameTrackingInfo; + + shared_ptr<TrackingHeapBlockProvider> buffProvider_; BufferDescriptor bufferType_; + FrameTrackingInfo frameTrackingIndex_; + PGrid frameGrid_; + + + BuffHandle + trackFrame (FrameID frameNr, BuffHandle const& newBuffer) + { + REQUIRE (contains (frameTrackingIndex_,frameNr), + "attempt to lock already used frame %lu", frameNr); + + frameTrackingIndex_.insert (frameNr); + return newBuffer; + } + + TimeValue + deadlineFor (FrameID frameNr) + { + return frameGrid_->timeOf (frameNr); + } + + + /* === Connection API === */ BuffHandle claimBufferFor(FrameID frameNr) { - return buffProvider_->lockBuffer (bufferType_); + return trackFrame (frameNr, + buffProvider_->lockBuffer (bufferType_)); } bool isTimely (FrameID frameNr, TimeValue currentTime) { - if (Time::MAX == currentTime) + if (Time::ANYTIME == currentTime) return true; - UNIMPLEMENTED ("find out about timings"); - return false; + return currentTime < deadlineFor (frameNr); } void @@ -120,6 +156,8 @@ namespace play { TrackingInMemoryBlockSequence() : buffProvider_(new TrackingHeapBlockProvider()) , bufferType_(buffProvider_->getDescriptor<TestFrame>()) + , frameTrackingIndex_() + , frameGrid_(TimeGrid::build ("DiagnosticOutputSlot-buffer-grid", FrameRate::PAL)) /////////////TODO should rather pass that in as part of a "timings" definition { INFO (engine_dbg, "building in-memory diagnostic output sequence"); } @@ -134,27 +172,39 @@ namespace play { /* === Diagnostic API === */ TestFrame* - accessEmittedFrame (uint frameNo) const + accessEmittedFrame (uint frameNr) const { REQUIRE (buffProvider_); - if (frameNo <= buffProvider_->emittedCnt()) - return &buffProvider_->accessAs<TestFrame> (frameNo); + if (frameNr <= buffProvider_->emittedCnt()) + return &buffProvider_->accessAs<TestFrame> (frameNr); else return 0; ////////////////////////////////TICKET #856 } diagn::Block* - accessEmittedBuffer (uint bufferNo) const + accessEmittedBuffer (uint bufferNr) const { REQUIRE (buffProvider_); - if (bufferNo <= buffProvider_->emittedCnt()) - return buffProvider_->access_emitted (bufferNo); + if (bufferNr <= buffProvider_->emittedCnt()) + return & buffProvider_->access_emitted (bufferNr); else return 0; } + + bool + wasAllocated (uint frameNr) const + { + REQUIRE (buffProvider_); + return contains (frameTrackingIndex_, frameNr); + } }; + /** + * Special diagnostic connection state implementation, + * establishing diagnostic output connections for each channel, + * thus allowing to verify the handling of individual buffers + */ class SimulatedOutputSequences : public ConnectionStateManager<TrackingInMemoryBlockSequence> { @@ -280,10 +330,8 @@ namespace play { bool buffer_was_used (uint channel, FrameID frame) { - diagn::Block* block = accessSequence(channel) - .accessEmittedBuffer(frame); - return block - && block->was_used(); + return accessSequence(channel) + .wasAllocated(frame); } @@ -297,14 +345,20 @@ namespace play { bool buffer_was_closed (uint channel, FrameID frame) { - UNIMPLEMENTED ("determine if the specified buffer was indeed closed properly"); + diagn::Block* block = accessSequence(channel) + .accessEmittedBuffer(frame); + return block + && block->was_closed(); } bool emitted (uint channel, FrameID frame) { - UNIMPLEMENTED ("determine if the specivied buffer was indeed handed over for emitting output"); + diagn::Block* block = accessSequence(channel) + .accessEmittedBuffer(frame); + return block + && block->was_used(); } From 11f04389426d55e773ef2373efb3000dc07c5c79 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 10 Dec 2011 01:41:21 +0100 Subject: [PATCH 298/496] spelling and some small additions --- doc/technical/code/codingGuidelines.txt | 8 ++++---- doc/technical/overview.txt | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/doc/technical/code/codingGuidelines.txt b/doc/technical/code/codingGuidelines.txt index 96be8032e..1a0aeb89e 100644 --- a/doc/technical/code/codingGuidelines.txt +++ b/doc/technical/code/codingGuidelines.txt @@ -35,11 +35,11 @@ The Lumiera project uses GNU indentation style with slight adaptations. * the opening brace of namespaces is placed on the same line. Optionally, the namespace body may be indented, as long as this helps underpinning the nesting structure. But there is no need to use 3 indents on a 3 level nested namespace. One level is enough - to underpin the presence of a nesting. + to highlight the presence of a nesting. Naming conventions ~~~~~~~~~~~~~~~~~~ -Naming conventions are used to underpin the kind of element at hand and give a visual +Naming conventions are used to characterise the kind of element at hand and give a visual clue to the reader. We use our own conventions -- there is no point in arguing that this and that library or language uses other conventions. @@ -81,8 +81,8 @@ General Code Arrangement and Layout doxygen comment explaining the intention and anything not obvious from reading the code. - when arranging headers and compilation units, please take care of the compilation times and the code size. Avoid unnecessary includes. Use forward declarations where applicable. - Yet still, _all really required includes should be mentioned_ (even if already included by another - dependency) + Yet still, _all immediately required includes should be mentioned_ (even if already included by + another dependency) - The include block starts with our own dependencies, followed by a second block with the library dependencies. After that, optionally some symbols may be brought into scope (through +using+ clauses). Avoid cluttering top-level namespaces. Never import full namespaces (no +using namespace boost;+ please!) diff --git a/doc/technical/overview.txt b/doc/technical/overview.txt index d8dbb8904..e6bc30bf0 100644 --- a/doc/technical/overview.txt +++ b/doc/technical/overview.txt @@ -100,14 +100,20 @@ several important parts of the applications are loaded as plug-ins, starting with the GUI. -User Interfaces ---------------- +User Interface(s) +----------------- The purpose of the user interface(s) is to act on the _high-level data model_ contained within the Session, which belongs to the _processing layer_ below. User interfaces are implemented as plug-ins and are pulled up on demand, they won't contain any relevant persistent state beyond presentation. +_As of 2011, the one and only interface under active development is +the Lumiera GTK GUI,_ based on GTK-2 / gtkmm. The sources are in tree +(directory 'src/gui') and it is integrated into the standard build and +installation process. By default, running the 'lumiera' executable will +load and start this GUI as a Lumiera module from 'modules/gtk_gui.lum' + Processing Layer @@ -190,9 +196,9 @@ reopened on demand. Hardlinked files are recognized and opened only once. All file access is done by memory mapping to reduce data copies between userland and kernel. Moreover the kernel becomes responsible to schedule paging (which will be augmented by lumiera) to make the best use of available -resources. Memory is mapped in biggier possibly overlapping windows of -resonable sized chunks. Requests asking for a contingous set of data from the -file in memory. +resources. Memory is mapped in larger windows of reasonable sized chunks, possibly +overlapping each other. Clients may request a specific continuous set of data from +the file to be accessible as memory block. .Indexing @@ -554,7 +560,7 @@ fail. NULL strings are propagated to "" empty strings. Polymorphic Programming in C ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Just a macro for simplyfying vtable function calls +Just a macro for simplifying vtable function calls VCALL(Handle, function, arguments...) translates to Handle->vtable->function (Handle, arguments...) From 336d4cb33f194e70f38789434a9b737d186cd45a Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 10 Dec 2011 01:52:51 +0100 Subject: [PATCH 299/496] technical overview: player and engine interface --- doc/technical/overview.txt | 92 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/doc/technical/overview.txt b/doc/technical/overview.txt index e6bc30bf0..66c54d258 100644 --- a/doc/technical/overview.txt +++ b/doc/technical/overview.txt @@ -171,9 +171,65 @@ Low Level Model ~~~~~~~~~~~~~~~ _tbw_ -Play/Render processes -~~~~~~~~~~~~~~~~~~~~~ -_tbw_ + +Play/Rendering subsystem +~~~~~~~~~~~~~~~~~~~~~~~~ +Within Lumiera, »Player« is the name for a subsystem responsible for organising and tracking +_ongoing playback and render processes._ The player subsystem does not perform or even manage +any render operations, nor does it handle the outputs directly. + +Yet it addresses some central concerns: + +uniformity:: + All playback and render processes are on equal footing, handled in a similar way. + +integration:: + The player cares for the necessary integration with the other subsystems ++ +it consults the _Output Management,_ retrieves the necessary informations from the _Session_ +and coordinates the forwarding of Backend calls. + +time quantisation:: + The player translates continuous time values into discrete frame counts. ++ +To perform this _quantisation,_ the help of the session for building a TimeGrid +for each output channel is required. + + +The player service +^^^^^^^^^^^^^^^^^^ +Client code accesses the player (subsystem) through the play-facade (`lumiera::Play`). +The exposed service allows to _set up an output connection for playback or rendering,_ +resulting in a play-controller object. + +.Play::Controller +This controller frontend represents the presence of such an active output connection +and incorporates a state machine supporting the usual things you'd expect to do with +a player (Play, pause, FFwd, Rew, scrubbing, jumping, looping). This controller object +is a copyable smart-handle -- all instances act as if wired in parallel. + +.time control +The play-controller frontend makes heavy use of `time::Control`. This is a mediator +to accept and forward _mutations_ on time values and time ranges, possibly involving +frame quantisation. After attaching it to a target time value, it accepts changes, +offsets and nudging, translates these into the appropriate target modifications +and notifies any attached _change listeners_. + +.play process +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 render engine. It maintains a registration entry for the process to keep track of +associated entities, resources allocated and calls dispatched as a consequence. Besides +each play process is wired to at leas one play-controller acting as frontend interface +and information hub for the client code. + +NOTE: the player is in no way engaged in any of the actual calculation and management tasks +necessary to make the stream of calculations actually 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 dispatching the calculation stream and scheduling calculation jobs +- the render engine has the ability to carry out individual frame calculations +- the OutputSlot exposed by the output manager is responsible for accepting timed frame delivery @@ -213,13 +269,41 @@ Manages serveral classes of threads in pools. The threadpool is reasonable dumb. Higher level management will be done by the Schedulers and Jobs. +Engine Interface +~~~~~~~~~~~~~~~~ +While on itself just a thin interface and adaptation layer forwarding calls to +the primary backend facilities, the Engine Interface is the primary point of service +accessed by Proc-Layer to use the backend services for rendering content. + +.Calculation Streams +The Engine Interface is cast in terms of an _calculation stream_ entity. This is +a stream of expected and ongoing frame calculations for multiple channels, to be +managed as a compound. The calculated frames will be delivered into an output slot +right away. No assumptions are made regarding the ordering of these individual +calculations -- they may be performed in parallel, constrained by input and +resource prerequisites solely. + +.Frame Dispatcher +For the actual processing, calculation streams need to be translated into individual +calculation jobs to be scheduled. For each uniform _segment of the effective timeline,_ +the typical recursive descent call characteristic for _pull processing_ results in a +Job Ticket. + +.Job Ticket +This structural descriptor of the actual calculations to be performed is the base +for creating individual jobs: Already specialised for a distinct segment of the +effective timeline and tailored for the needs of a given calculation stream, +the job ticket acts as blueprint for the actual jobs to be enqueued +with the _Scheduler._ + + Schedulers ~~~~~~~~~~ Scheduling Queues for different purposes: .Deadline -Higher priority jobs ordered by a deadline time plus some (negative) hystersis. Jobs are +Higher priority jobs ordered by a deadline time plus some (negative) hysteresis. Jobs are started when they approach their deadline. Jobs who miss their deadline are never scheduled here. From 83621450399193fd6d740bbbfaa95e172424519b Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 10 Dec 2011 03:13:45 +0100 Subject: [PATCH 300/496] spelling and comments --- src/lib/polymorphic-value.hpp | 10 +++++----- src/lib/simple-allocator.hpp | 2 +- src/proc/engine/calc-stream.hpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp index a9b92bc5e..62db081f1 100644 --- a/src/lib/polymorphic-value.hpp +++ b/src/lib/polymorphic-value.hpp @@ -34,8 +34,8 @@ ** assume the presence of a garbage collector or similar mechanism, ** so 'objects' need just to be mentioned by reference. ** - ** In C++ to employ many of the well known techniques, you're more or less - ** bound to explicitly put the objects somewhere in heap allocated memory + ** In C++, in order to employ many of the well known techniques, we're bound + ** more or less to explicitly put the objects somewhere in heap allocated memory ** and then pass an interface pointer or reference into the actual algorithm. ** Often, this hinders a design based on constant values and small descriptor ** objects used inline, thus forcing into unnecessarily complex and heavyweight @@ -68,7 +68,7 @@ ** Moreover, the PolymorphicValue container provides static builder functions, ** allowing to place a concrete instance of a subclass into the content buffer. ** After construction, the actual type of this instance will be forgotten - ** (``type erasure''), but because the embedded vtable, on access the + ** (``type erasure''), but because of the embedded vtable, on access the ** proper implementation functions will be invoked. ** ** Expanding on that pattern, the copying and cloning operations of the whole @@ -86,7 +86,7 @@ ** the copy or clone operations, we need to do an elaborate re-cast operation, ** first going down to the leaf type and then back up into the mixed in ** management interface. Basically this operation is performed by using - ** an \c dynamic_cast<CopyAPI&>(bufferContents) + ** a \c dynamic_cast<CopyAPI&>(bufferContents) ** - but when the used client types provide some collaboration and implement ** this management interface either directly on the API or as an immediate ** sub-interface, then this copy/management interface is located within the @@ -345,7 +345,7 @@ namespace lib { * - the caller cares for thread safety. No concurrent get calls while in mutation! * * @warning when a create or copy-into operation fails with exception, the whole - * PolymorphicValue object is in undefined state and must not be used further. + * PolymorphicValue object is in undefined state and must not be used henceforth. */ template < class IFA ///< the nominal Base/Interface class for a family of types diff --git a/src/lib/simple-allocator.hpp b/src/lib/simple-allocator.hpp index 8dd5cea25..3f99387d8 100644 --- a/src/lib/simple-allocator.hpp +++ b/src/lib/simple-allocator.hpp @@ -143,7 +143,7 @@ namespace lib { : InstantiateForEach< typename TYPES::List // for each of those types... , CustomAllocator // ...mix in the custom allocator > - , COUNTER + , COUNTER // ...Instantiation accounting policy { /** forward plain memory allocation */ diff --git a/src/proc/engine/calc-stream.hpp b/src/proc/engine/calc-stream.hpp index b9b32c9fc..d1086abcf 100644 --- a/src/proc/engine/calc-stream.hpp +++ b/src/proc/engine/calc-stream.hpp @@ -73,13 +73,13 @@ namespace engine{ CalcStream() { - + UNIMPLEMENTED("build a calculation stream"); } public: CalcStream (CalcStream const& o) { - UNIMPLEMENTED("build a calculation stream"); + UNIMPLEMENTED("clone a calculation stream"); } ~CalcStream() { } From d6f5ed3282ebdc0e12b7dd1fb7b01db58745c40a Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 10 Dec 2011 03:15:40 +0100 Subject: [PATCH 301/496] technical overview: document some library and support facilities --- doc/technical/overview.txt | 211 +++++++++++++++++++++++++++++++++++-- 1 file changed, 200 insertions(+), 11 deletions(-) diff --git a/doc/technical/overview.txt b/doc/technical/overview.txt index 66c54d258..6d600d492 100644 --- a/doc/technical/overview.txt +++ b/doc/technical/overview.txt @@ -389,6 +389,27 @@ Plugin loader ~~~~~~~~~~~~~ _tbw_ +Advice framework +~~~~~~~~~~~~~~~~ +This is a ``whiteboard'' system, allowing implementation-level services to _publish_ +some piece of information as _advice_, while other parts of the system may pick up +this advice just by a name token, without requiring a direct knowledge of the +original _advisor._ The _Advice System_ is a singleton service maintaining a +lookup and registration data structure. Individual _piece of advice_ elements +are stored _as value copy_. Publishing new advice requires locking, but accessing +advice is lock-free (actually there needs to be a memory barrier ``somewhere'', +otherwise the advice requesting client might not see new advice) + +.Advice topics +Advice is organised into categories, based on the type of the advice item and +some additional symbolic identifiers. Actually these are syntactically represented +similar to the _atoms_ of a rules based system (``Prolog syntax''). Currently (2010) +only ground terms (completely static symbols) are supported. But the intention is to +extend the system to allow for variables in these terms. This will turn the matching +of advice provisions and requests into an unification, allowing the advice item to +be parametrised. + + Rules system ~~~~~~~~~~~~ _tbw_ @@ -406,6 +427,7 @@ Lua Scripting _tbw_ + Library ------- @@ -426,15 +448,80 @@ Intentionally no semaphores. Time ~~~~ -Time values are represented by an opaque date type `lumiera::Time` +Time values are represented by a family of opaque date types with overloaded operators. The implementation is based on `gavl_time_t`, -an integral (µsec) time tick value. Any Time handling and conversions -is centralised in library routines. +an integral (µsec) time tick value. Thus, the arithmetic on time values +and time spans is limited and any Time handling and conversion is +centralised in library routines. We distinguish between time values and a _quantisation_ into a frame or sample grid. In any case, quantisation has to be done once, explicitly and as late as possible. See the link:{rfc}/TimeHandling.html[Time handling RfC]. +.time values +The Lumiera library defines several flavours of time values. All of +these internal time values have in common that they are _opaque_ and not +directly related to any human readable or external (wall clock) time. +Moreover, most of these time values are immutable, yet there are two +mechanisms to allow for changing time values (TimeVar and Mutation). + +.quantised time +Special flavours of these time values additionally carry the reference +to an (frame) alignment grid, while being time value entities in all other +respects. But _only those quantised time values_ expose functions to +convert the internal opaque time into a human readable or externally +relevant time format -- including SMPTE or frame counts. + +.time (alignment) grid +Thus, any usage of time values is forced to refer to such a time alignment +grid explicitly, at least when leaving the realm of the internal opaque +time values. This is the point where the *time quantisation* is actually +performed, imposing some information loss (as any rounding operation does). + +A time alignment grid is exactly that: a set of functions to perform +this lossy conversion. Implicitly this involves the definition of an +_time origin_ (reference point where the external time is zero), and +typically this also includes the definition of a _frame rate_ (but +in the most general case, this frame rate might be variable and +change at various places of the time axis). Consequently, all time +grids are Assets and defined as part of the concrete session. + + +Time Code +^^^^^^^^^ +Historically, Time Code was seen as the foundation of any film editing. +Similarly, the first generation of editing systems used Time Code as a +foundation. Today, we consider such a design as questionable. + +Lumiera takes a different approach here: Time code is reduced to a mere +mode of presentation, i.e. a formatting of existing time values. It is +void of any substantial meaning. To the contrary, the operation of +_frame quantisation_ (see above) is considered to be fundamental, +causing a irreversible loss of information. The design of time handling +chosen within Lumiera forces you to decide on using a specific _time grid_, +prior to being able to format an internal (opaque) time value in any kind +of time code. And only as late as when _actually retrieving_ this time code +formatted value, the actual quantisation (grid alignment) happens. + +In practice, establishing a time grid requires knowledge about the output +format. Thus, an (sufficiently resolved) *output designation* is required +to perform any grid alignment a and time code formatting. Typically this +happens when a timeline or similar part of the High-Level-Model is connected +to a concrete output or an global bus defining a frame rate already. The model +contents as such are _frame rate independent_. + +The following time code formats are supported, both for programmatic access +and display within the GUI + +- frame count +- SMPTE +- SMPTE drop-frame _[,yellow]#TODO# as of 2011_ +- hours:mins:secs _[,yellow]#TODO# as of 2011_ +- fractional seconds _[,yellow]#TODO# as of 2011_ +- musical bars:beats _[,yellow]#TODO# as of 2011_ + +As a corollary, as any rendering is based on frame numbers, it requires an +output connection or something similar to establish a frame grid. + Errors ~~~~~~ @@ -523,7 +610,33 @@ mayfail() Singletons ~~~~~~~~~~ -_tbw_ +Deliberately, Lumiera stays below the level of utilising a dependency injection framework. +Consequently, we access most services _by type name_, pulling up a single implementation +instance on demand. Rather than placing this singleton lifecycle logic into the individual +implementation classes, we use a _singleton factory_, managing the lifecycle through static +variables and placing the singleton object into a static memory block. Singleton initialisation +is protected by a monitor per singleton type, while shutdown is triggered by the clean-up of +a static variable. This results in the general policy that within Lumiera, performing any +``business'' code in the application shutdown phase (after exiting +main()+) is _strictly +prohibited._ Generally speaking, destructors _must not perform any significant work_ and +are are expected to be failsafe. + +.accessing the singleton instance +By convention, when clients are expected actively to access the singleton instance, +the interface class holds the singleton factory as a public static member with the +name +instance+. This allows clients to write `SomeService::instance()` to get a +reference to the implementation. + +.subclasses and mock testing support +There is a mechanism to ``push aside'' the existing singleton instance and shadow +it temporarily with a mock implementation. Besides, there is a variation of the +general singleton factory, allowing to fabricate a specific subclass of the +exposed interface. Both of these facilities are rather technically involved +and require some specific set-up -- fortunately it turned out that they are +used only occasionally and rarely required. (Probably this is a result of +Lumiera being developed _test-driven_ -- things are being written mostly +in a unit test friendly fashion). + Extensible Factory ~~~~~~~~~~~~~~~~~~ @@ -550,12 +663,58 @@ _tbw_ Wrappers and Opaque Holders ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- smart handle -- unified value/ptr/reference holder -- ownership managing collection -- opaque holder to ``piggypack'' an object inline, - without the need for heap allocated storage -- vector of references +.smart handle +This pervasively used handle type is based on the reference counting +smart-pointer type of C\++ (`boost::shared_ptr` and C++11). Typically +this also includes a pointer to some kind of implementation service. + +Yet still, handles based on `lib::Handle<TY>` should not be confused with +smart pointers. Rather, we use the ref-counting mechanism to invoke a custom +clean-up callback when the last handle goes out of scope. Typically, the +implementation service is kept entirely opaque, while the copyable handle +objects also implement a front-end interface for client access. + +.unified holder for value/ptr/reference +_tbw_ + +.ownership managing collection +_tbw_ + +.opaque holder +There is a family of holder objects, all based on placement-new of the +contained object into an embedded buffer. The purpose is to ``piggyback'' +an object inline, without the need for heap allocated storage. Frequently +the motivation for this usage pattern is *type erasure*: the detailed knowledge +context used to build some kind of object is discarded prior to further use, +relying on generic information and the hidden parametrisation henceforth. + +.polymorphic values +The C++ language has direct support for _value semantics_ and allows to build +value objects to be treated as first class citizens. Unfortunately this doesn't +fit well with the chosen approach to object orientation, where polymorphism relies +on reference semantics. Thus, most of the fundamental design patterns drive us into +having an object manager somewhere hidden within the implementation level, to +manage the memory for maintaining the subclass instances to be concealed +at the usage site. + +To avoid this dilemma, we utilise the technique of the opaque holder to provide +objects with value semantics, while actually placing the instance of a subclass +into the inline buffer. Clients access this embedded object by automatic type +conversion to the interface type, which gives us polymorphism. While the +definition of such a beast is quite involved, the runtime overhead is +surprisingly low. When compared with standard polymorphism, creating +objects and invoking operations has zero overhead, while copying +and assignment incur the cost of an additional virtual call, +assuming the target objects cooperate by mixing in a +copy management interface. + +.vector of references +a minimal interface for an array-like entity, but exposing only references +to the contained elements. Obviously this means to use a virtual call for +the subscript operation. This interface allows interfaces to expose something +_array-like_ without committing to a specific implementation type for the +exposed elements within the ``array''. The Lumiera library provides a set +of standard implementations for this +lib::RefArray+ interface, including +a vector based and a directly array based variant. + Unique Identifiers ~~~~~~~~~~~~~~~~~~ @@ -584,7 +743,32 @@ tables allow unique IDs per type Allocators ~~~~~~~~~~ -_tbw_ +Lumiera utilises several custom allocators, each tailored to a specific purpose. +All these allocator-_frontends_ share a common pooling allocation backend + +WARNING: currently (as of 2011) the low-level pooled allocator within the + backend isn't implemented; instead we do just heap allocations. + See Ticket #231 + +.Allocation Cluster +This allocation scheme is used within the context of the Builder and the +Low-Level-Model. The predominant usage trend in this realm is to create +and wire a family of small objects right after each other, within a build +process. These objects are intended to work together and will be discarded +all at once, after hot-swapping a new version of that model segment. + +.Typed Allocation Manager +This allocation framework is used at various places when a large number of +similar objects is expected to be coming and going. New objects are +placement-constructed into the allocated space and immediately wrapped +with a ref-counting smart-ptr to manage ownership. + +.Simple Allocator +Based on the allocator interface of the STL, allowing just for plain +allocations and de-allocations without any further instance and lifecycle +management. Currently (as of 2011) this allocator isn't used much -- it is +conceivable that later we'll detect some specific STL based containers to be +performance critical with respect to allocation. Memory Pools @@ -623,6 +807,11 @@ Functor Utils ^^^^^^^^^^^^^ _tbw_ +Duck Typing +^^^^^^^^^^^ +_tbw_ + + Preprocessor Metaprogramming ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 76b24ce50d3ec160336851875bd5b7085b5e91f2 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 10 Dec 2011 03:18:48 +0100 Subject: [PATCH 302/496] WIP draft the building of a render CalculationStream --- src/proc/play/play-process.cpp | 13 ++-- src/proc/play/play-process.hpp | 13 +++- src/proc/play/play-service.cpp | 8 ++- src/proc/play/render-configurator.cpp | 79 ++++++++++++++++++++++++ src/proc/play/render-configurator.hpp | 87 +++++++++++++++++++++++++++ 5 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 src/proc/play/render-configurator.cpp create mode 100644 src/proc/play/render-configurator.hpp diff --git a/src/proc/play/play-process.cpp b/src/proc/play/play-process.cpp index ca1417659..6a3f2e465 100644 --- a/src/proc/play/play-process.cpp +++ b/src/proc/play/play-process.cpp @@ -1,5 +1,5 @@ /* - PlayProcess.hpp - state frame for an ongoing play/render process + PlayProcess - state frame for an ongoing play/render process Copyright (C) Lumiera.org 2011, Hermann Vosseler <Ichthyostega@web.de> @@ -22,6 +22,7 @@ #include "proc/play/play-process.hpp" +#include "proc/play/render-configurator.hpp" #include "lib/itertools.hpp" //#include <string> @@ -57,7 +58,8 @@ namespace play { if (!slot.isFree()) throw error::State("unable to acquire a suitable output slot" /////////////////////TICKET #197 #816 , LUMIERA_ERROR_CANT_PLAY); - return Feed (port,slot); + + return Feed (port,slot); //////////////////////////////////TODO: how to get the RenderConfigurator (strategy) here??? } @@ -110,10 +112,9 @@ namespace play { /** */ - Feed::Feed (ModelPort port, OutputSlot& output) - { - UNIMPLEMENTED("build an active playback/render feed"); - } + Feed::Feed (ModelPort port, OutputSlot& output, RenderConfigurator& renderStrategy) + : renderStreams_(renderStrategy.buildCalculationStreams(port,output)) + { } diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index 193dc252e..818f0725f 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -83,6 +83,10 @@ namespace play { namespace error = lumiera::error; + /** Strategy for configuring the render process */ + class RenderConfigurator; + + /** * Rendering data feed, corresponding to a single * global pipe and to be delivered into a single OutputSlot. @@ -95,7 +99,14 @@ namespace play { public: typedef lib::IterSource<Feed>::iterator Connections; - Feed (ModelPort, OutputSlot&); + typedef std::vector<engine::CalcStream> RenderStreams; + + RenderStreams renderStreams_; + + /** building a Feed effectively involves the EngineService + * to establish an actual rendering plan. Which is abstracted + * here through the RenderConfigurator instance */ + Feed (ModelPort, OutputSlot&, RenderConfigurator&); }; diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index aab3306b7..a3932d1c9 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -21,6 +21,8 @@ * *****************************************************/ +#include "lib/error.hpp" +#include "include/play-facade.h" #include "proc/play/play-service.hpp" #include "proc/play/play-process.hpp" #include "lib/util.hpp" @@ -97,6 +99,7 @@ namespace play { void endProcess (PlayProcess* dyingProcess) { + /////////////////////////////////////////////TICKET #867 : somehow ensure sane abort of all attached calculation efforts delete dyingProcess; Lock sync(this); @@ -114,7 +117,10 @@ namespace play { using lumiera::Play; - PlayService::~PlayService() { } + PlayService::~PlayService() + { + UNIMPLEMENTED ("block waiting for any ongoing play processes. Ensure the process table is empty -- OR -- hand it over to some kind of cleanup service"); + } /** bring up the global render- and playback service. diff --git a/src/proc/play/render-configurator.cpp b/src/proc/play/render-configurator.cpp new file mode 100644 index 000000000..ffd091feb --- /dev/null +++ b/src/proc/play/render-configurator.cpp @@ -0,0 +1,79 @@ +/* + RenderConfigurator - strategy to help building an actual render calculation stream + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/play/render-configurator.hpp" +#include "proc/engine/engine-service.hpp" +//#include "lib/itertools.hpp" + +//#include <string> +//#include <memory> +//#include <tr1/functional> +//#include <boost/scoped_ptr.hpp> + + + +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::transform; + + class DefaultRenderProcessBuilder + : public RenderConfigurator + { + + RenderStreams + buildCalculationStreams (ModelPort port, OutputSlot& output) + { + UNIMPLEMENTED("build an active playback/render feed"); + + ///TODO allocate the output slot + ///TODO extract the individual channels + ///TODO get the timings + + engine::EngineService::instance().calculate(port, nominalTimings, activeOutputConnection, serviceQuality); + } + }; + + + } // (End) hidden service impl details + + + + + + /** */ + + +}} // namespace proc::play diff --git a/src/proc/play/render-configurator.hpp b/src/proc/play/render-configurator.hpp new file mode 100644 index 000000000..8811d803f --- /dev/null +++ b/src/proc/play/render-configurator.hpp @@ -0,0 +1,87 @@ +/* + RENDER-CONFIGURATOR.hpp - strategy to help building an actual render calculation stream + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file render-configurator.hpp + ** Strategy holding all the detail knowledge necessary to establish + ** a running render CalculationStream. + ** + ** @see engine::EngineService + ** @see engine::Feed + ** @see engine::PlayService + */ + + +#ifndef PROC_PLAY_RENDER_CONFIGURATOR_H +#define PROC_PLAY_RENDER_CONFIGURATOR_H + + +#include "lib/error.hpp" +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +#include "proc/mobject/model-port.hpp" +//#include "proc/play/output-manager.hpp" +//#include "lib/iter-source.hpp" +//#include "lib/util.hpp" +// +//#include <boost/noncopyable.hpp> +//#include <boost/scoped_ptr.hpp> +//#include <string> +#include <vector> + + +namespace proc { +namespace play { + +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// using lumiera::DummyPlayer; +// using util::isnil; + using proc::mobject::ModelPort; + +// typedef proc::play::POutputManager POutputManager; +// typedef lib::IterSource<ModelPort>::iterator ModelPorts; + + namespace error = lumiera::error; + + + /** Strategy for configuring the render process */ + class RenderConfigurator + { + + public: + virtual ~RenderConfigurator(); ///< this is an interface + + + typedef std::vector<engine::CalcStream> RenderStreams; ////TODO this belongs rather into class Feed + + virtual RenderStreams buildCalculationStreams (ModelPort, OutputSlot&) =0; + + }; + + + + +}} // namespace proc::play +#endif From 4b6f4fc140e92060aeb583768626280a37d4d79f Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 16 Dec 2011 19:55:24 +0100 Subject: [PATCH 303/496] StreamTypes RFC: spelling and wording improvements --- doc/devel/rfc_pending/StreamTypeSystem.txt | 56 +++++++++++----------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/doc/devel/rfc_pending/StreamTypeSystem.txt b/doc/devel/rfc_pending/StreamTypeSystem.txt index ea07777c2..2516ce5c0 100644 --- a/doc/devel/rfc_pending/StreamTypeSystem.txt +++ b/doc/devel/rfc_pending/StreamTypeSystem.txt @@ -24,7 +24,7 @@ immediate consequences on the way the code can test and select the appropriate path to deal with some data or a given case. This brings us in a difficult situation: - * almost everything regarding media data and media handling is notriously + * almost everything regarding media data and media handling is notoriously convoluted * because we can't hope ever to find a general umbrella, we need an extensible solution @@ -44,27 +44,27 @@ Terminology ^^^^^^^^^^^ * *Media* is comprised of a set of streams or channels * *Stream* denotes a homogeneous flow of media data of a single kind - * *Channel* denotes a elementary stream, which can't be further separated _in - the given context_ + * *Channel* denotes an elementary stream, which -- _in the given context_ -- + can't be further decomposed * all of these are delivered and processed in a smallest unit called *Frame*. Each frame corresponds to a time interval. - * a *Buffer* is a data structure capable of holding a Frame of media data. + * a *Buffer* is a data structure capable of holding one or multiple Frames of media data. * the *Stream Type* describes the kind of media data contained in the stream Levels of classification ^^^^^^^^^^^^^^^^^^^^^^^^ The description/classification of streams is structured into several levels. A -complete stream type (implemented by a stream type descriptor) containts a tag +complete stream type (implemented by a stream type descriptor) contains a tag or selection regarding each of these levels. * Each media belongs to a fundamental *kind of media*, examples being _Video, Image, Audio, MIDI, Text,..._ This is a simple Enum. * Below the level of distinct kinds of media streams, within every kind we - have an open ended collection of *Prototypes*, which, whithin the high-level + have an open ended collection of *Prototypes*, which, within the high-level model and for the purpose of wiring, act like the "overall type" of the media stream. Everything belonging to a given Prototype is considered to be - roughly equivalent and can be linked together by automatic, lossles + roughly equivalent and can be linked together by automatic, lossless conversions. Examples for Prototypes are: stereoscopic (3D) video versus the common flat video lacking depth information, spatial audio systems (Ambisonics, Wave Field Synthesis), panorama simulating sound systems (5.1, @@ -85,15 +85,15 @@ _library_ routines, which also yield a _type classification system_ suitable for their intended use. Most notably, for raw sound and video data we use the http://gmerlin.sourceforge.net/[GAVL] library, which defines a fairly complete classification system for buffers and streams. For the relevant operations in -the Proc-Layer, we access each such library by means of a Facade; it may sound +the Proc-Layer, we access each such library by means of a Façade; it may sound surprising, but actually we just need to access a very limited set of operations, like allocating a buffer. _Within_ the Proc-Layer, the actual implementation type is mostly opaque; all we need to know is if we can connect two streams and get an conversion plugin. Thus, to integrate an external library into Lumiera, we need explicitly to -implement such a Lib Facade for this specific case, but the intention is to be -able to add this Lib Facade implementation as a plugin (more precisely as a +implement such a Lib Façade for this specific case, but the intention is to be +able to add this Lib Façade implementation as a plugin (more precisely as a "Feature Bundle", because it probably includes several plugins and some additional rules) @@ -105,8 +105,8 @@ with, determining a suitable prototype for a given implementation type is sort of a tagging operation. But it can be supported by heuristic rules and an flexible configuration of defaults. For example, if confronted with a media with 6 sound channels, we simply can't tell if it's a 5.1 sound source, or if -it's a pre mixed orchesrta music arrangement to be routed to the final balance -mixing or if it's a prepared set of spot pickups and overdubbed dialogue. But a +it's a pre mixed orchestra music arrangement to be routed to the final balance +mixing or if it's a prepared set of spot pick-ups and overdubbed dialogue. But a heuristic rule defaulting to 5.1 would be a good starting point, while individual projects should be able to set up very specific additional rules (probably based on some internal tags, conventions on the source folder or the @@ -132,7 +132,7 @@ connections and conversions into each other. * Conversions and judging the possibility of making connections at the level of implementation types is coupled tightly to the used library; indeed, most - of the work to provide a Lib Facade consists of coming up with a generic + of the work to provide a Lib Façade consists of coming up with a generic scheme to decide this question for media streams implemented by this library. @@ -140,11 +140,11 @@ connections and conversions Tasks ^^^^^ * draft the interfaces ([green]#✔ done#) - * define a fallback and some basic behaviour for the relation between + * define a fall-back and some basic behaviour for the relation between implementation type and prototypes [,yellow]#WIP# * find out if it is necessary to refer to types in a symbolic manner, or if it - is sufficient to have a ref to a descriptor record or Facade object. - * provide a Lib Facade for GAVL [,yellow]#WIP# + is sufficient to have a ref to a descriptor record or Façade object. + * provide a Lib Façade for GAVL [,yellow]#WIP# * evaluate if it's a good idea to handle (still) images as a separate distinct kind of media @@ -153,21 +153,21 @@ Tasks Alternatives ^^^^^^^^^^^^ -Instead of representing types my metadata, leave the distinction implicit and +Instead of representing types by metadata, leave the distinction implicit and instead implement the different behaviour directly in code. Have video tracks and audio tracks. Make video clip objects and audio clip objects, each -utilizing some specific flags, like sound is mono or stereo. Then either -switch, swich-on-type or scatter out the code into a bunch of virtual +utilising some specific flags, like sound is mono or stereo. Then either +switch, switch-on-type or scatter out the code into a bunch of virtual functions. See the Cinelerra source code for details. In short, following this route, Lumiera would be plagued by the same notorious problems as most existing video/sound editing software. Which is, implicitly -assuming "everyone" just does "normal" things. Of course, users always were and -always will be clever enough to work around this assumption, but the problem -is, all those efforts will mostly stay isolated and can't crystalize into a +assuming ``everyone'' just does ``normal'' things. Of course, users always were +and always will be clever enough to work around this assumption, but the problem +is, all those efforts will mostly stay isolated and can't crystallise into a reusable extension. Users will do manual tricks, use some scripting or rely on project organisation and conventions, which in turn creates more and more -coercion for the "normal" user to just do "normal" things. +coercion for the ``average'' user to just do ``normal'' things. To make it clear: both approaches discussed here do work in practice, and it's more a cultural issue, not a question guided by technical necessities to select @@ -214,8 +214,8 @@ number of inputs and outputs) need in some way to be connected. The fact that we don't have a rule based system for deciding queries currently is not much of a problem. A table with some pre configured default answers for a small number of common query cases is enough to get the first clip rendered. -(Such a solution is already in place and working.) - -- link:Ichthyostega[] 2008-10-05 +(Such a solution is already in place and working.) + + -- link:Ichthyostega[] 2008-10-05 Woops fast note, I didn't read this proposal completely yet. Stream types could or maybe should be coopertatively handled together with the backend. Basically @@ -226,9 +226,9 @@ number, plus adding the capabilitiy of per frame metadata. This indices get abstracted by "indexing engines" it will be possible to have different kinds of indices over one file (for example, one enumerating single frames, one enumerating keyframes or gops). Such a indexing engine would be also the place -to attach per media metadata. From the proc layer it can then look like +struct -frameinfo* get_frame(unsigned num)+ where +struct frameinfo+ (not yet defined) -is something like +{ void* data; size_t size; struct metadata* meta; ...}+ +to attach per media metadata. From the proc layer it can then look like `struct +frameinfo* get_frame(unsigned num)` where `struct frameinfo` (not yet defined) +is something like `{ void* data; size_t size; struct metadata* meta; ...}` + -- link:ct[] 2008-10-06 Needs Work From c3bf3bb2698ba840331e2dd7b2e312ed247f80c5 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 16 Dec 2011 19:55:24 +0100 Subject: [PATCH 304/496] StreamTypes RFC: spelling and wording improvements --- doc/devel/rfc_pending/StreamTypeSystem.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/devel/rfc_pending/StreamTypeSystem.txt b/doc/devel/rfc_pending/StreamTypeSystem.txt index 2516ce5c0..84c12b1dd 100644 --- a/doc/devel/rfc_pending/StreamTypeSystem.txt +++ b/doc/devel/rfc_pending/StreamTypeSystem.txt @@ -45,7 +45,7 @@ Terminology * *Media* is comprised of a set of streams or channels * *Stream* denotes a homogeneous flow of media data of a single kind * *Channel* denotes an elementary stream, which -- _in the given context_ -- - can't be further decomposed + can't be decomposed any further * all of these are delivered and processed in a smallest unit called *Frame*. Each frame corresponds to a time interval. * a *Buffer* is a data structure capable of holding one or multiple Frames of media data. From 23d4202ec2c4b752d905180b59d61be1a70b87db Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 16 Dec 2011 20:12:21 +0100 Subject: [PATCH 305/496] update StreamTypes RFC to our current standard RRF structure --- doc/devel/rfc_pending/StreamTypeSystem.txt | 51 ++++++++++++++++------ doc/devel/template/new_rfc.sh | 4 +- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/doc/devel/rfc_pending/StreamTypeSystem.txt b/doc/devel/rfc_pending/StreamTypeSystem.txt index 84c12b1dd..12323606f 100644 --- a/doc/devel/rfc_pending/StreamTypeSystem.txt +++ b/doc/devel/rfc_pending/StreamTypeSystem.txt @@ -1,23 +1,30 @@ +Stream Type System +================== + +// please don't remove the //word: comments + [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Draft_ *Date* _2008-10-05_ *Proposed by* link:Ichthyostega[] ------------------------------------- -Stream Type System ------------------- -Especially in the Proc-Layer, we need a framework to deal with different -"kinds" of media streams. + +******************************************************************************** +.Abstract +Especially in the Proc-Layer, within the Builder and at the interface to the +Engine we need sort of a framework to deal with different »kinds« of +media streams. + This is the foundation to be able to define what can be connected and to separate out generic parts and isolate specific parts. - +******************************************************************************** Description -~~~~~~~~~~~ +----------- +//description: add a detailed description: The general idea is that we need meta information, and -- more precisely -- that _we_ need to control the structure of this metadata. Because it has immediate consequences on the way the code can test and select the appropriate @@ -41,7 +48,7 @@ role for some of the more advanced things possible within the session. Terminology -^^^^^^^^^^^ +~~~~~~~~~~~ * *Media* is comprised of a set of streams or channels * *Stream* denotes a homogeneous flow of media data of a single kind * *Channel* denotes an elementary stream, which -- _in the given context_ -- @@ -52,11 +59,17 @@ Terminology * the *Stream Type* describes the kind of media data contained in the stream +Concept of a Stream Type +~~~~~~~~~~~~~~~~~~~~~~~~ + +The Goal of our Stream Type system is to provide a framework for precisely +describing the ``kind'' of a media stream at hand. The central idea is to +structure the description/classification of streams into several levels. +A complete stream type (implemented by a stream type descriptor) contains +a tag or selection regarding each of these levels. + Levels of classification ^^^^^^^^^^^^^^^^^^^^^^^^ -The description/classification of streams is structured into several levels. A -complete stream type (implemented by a stream type descriptor) contains a tag -or selection regarding each of these levels. * Each media belongs to a fundamental *kind of media*, examples being _Video, Image, Audio, MIDI, Text,..._ This is a simple Enum. @@ -138,7 +151,8 @@ connections and conversions Tasks -^^^^^ +~~~~~ +// List what needs to be done to implement this Proposal: * draft the interfaces ([green]#✔ done#) * define a fall-back and some basic behaviour for the relation between implementation type and prototypes [,yellow]#WIP# @@ -150,9 +164,12 @@ Tasks +Discussion +~~~~~~~~~~ Alternatives ^^^^^^^^^^^^ +//alternatives: explain alternatives and tell why they are not viable: Instead of representing types by metadata, leave the distinction implicit and instead implement the different behaviour directly in code. Have video tracks and audio tracks. Make video clip objects and audio clip objects, each @@ -175,6 +192,8 @@ the one or the other. Rationale +--------- +//rationale: Give a concise summary why it should be done *this* way: * use type metadata to factor out generic behaviour and make variations in behaviour configurable. @@ -189,13 +208,17 @@ Rationale * provide the possibility to incorporate very project specific knowledge as rules. +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: Comments -------- - +//comments: append below As usual, see the http://www.lumiera.org/wiki/renderengine.html#StreamType[Proc-Layer impl doku] for more information and implementation details. @@ -239,5 +262,7 @@ agreed that we want this concept as proposed here. Do 14 Apr 2011 03:06:42 CEST Christian Thaeter +//endof_comments: +'''' Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/template/new_rfc.sh b/doc/devel/template/new_rfc.sh index a08ac3d06..eccaecc8d 100644 --- a/doc/devel/template/new_rfc.sh +++ b/doc/devel/template/new_rfc.sh @@ -12,9 +12,9 @@ ${TITLE//?/=} *Proposed by* $(git config --get user.name) <$(git config --get user.email)> ------------------------------------- -[abstract] ******************************************************************************** - +.Abstract +_give a short summary of this proposal_ ******************************************************************************** Description From 34d0622dd3cb1be1f61cfa711177ff670c270ed4 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 16 Dec 2011 20:25:34 +0100 Subject: [PATCH 306/496] notes to the EngineInterface RfC --- doc/devel/rfc_pending/EngineInterfaceOverview.txt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/doc/devel/rfc_pending/EngineInterfaceOverview.txt b/doc/devel/rfc_pending/EngineInterfaceOverview.txt index d33461893..e9e931661 100644 --- a/doc/devel/rfc_pending/EngineInterfaceOverview.txt +++ b/doc/devel/rfc_pending/EngineInterfaceOverview.txt @@ -163,7 +163,8 @@ buffers. Dispatching Step ~~~~~~~~~~~~~~~~ -The dispatcher translates a render process into sequences of node invocations, +The dispatcher translates a render process (actually a _calculation stream_ as +part of a render process) into sequences of node invocations, which then can be analysed further (including planning the invocation of prerequisites) and scheduled. This mapping is assisted by the engine model API (to find the right exit node in the right segment), the render process (for @@ -212,7 +213,7 @@ Tasks ([green]#-> yes, implementation deferred#) * find out about the exact handling of multichannel data streams ([green]#✔ done#) * design and prototypical implementation of frame quantisation ([green]#✔ done#) - * design a buffer descriptor [red]#TODO# + * design a buffer descriptor ([green]#✔ done#) * design a buffer designation scheme [red]#TODO# * expand on the node identification scheme [red]#TODO# * clarify how control data frames can be addressed [red]#TODO# @@ -223,7 +224,11 @@ Discussion Pros/Cons/Alternatives ^^^^^^^^^^^^^^^^^^^^^^ -Possible variants to consider.... +Currently we're focussing on how to implement _this_ concept, not on +evaluating alternatives. Especially the idea of scheduling individual frame jobs +is a core concept of Lumiera. This RfC tries to bridge from the session model to +an engine based on these concepts. It's the attempt to link two concepts already +defined and decided on.... Rationale From bda0dea9903d47b761dc558db79ba42cef7e8bf5 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 17 Dec 2011 02:20:48 +0100 Subject: [PATCH 307/496] clarify the relation of PlayProcess, CalcStream and EngineFacade --- src/include/play-facade.h | 4 +-- src/lib/iter-source.hpp | 4 +-- src/proc/engine/calc-stream.hpp | 4 +-- src/proc/engine/dispatcher.hpp | 2 +- src/proc/play/output-manager.hpp | 2 ++ src/proc/play/play-process.cpp | 3 +- src/proc/play/play-process.hpp | 1 + src/proc/play/play-service.cpp | 38 +++++++++++++++++----- src/proc/play/play-service.hpp | 31 +++++++++++++++--- src/proc/play/render-configurator.cpp | 5 ++- src/proc/play/render-configurator.hpp | 5 ++- wiki/renderengine.html | 47 +++++++++++++++++++-------- 12 files changed, 107 insertions(+), 39 deletions(-) diff --git a/src/include/play-facade.h b/src/include/play-facade.h index c5c2c0f00..480a1d6fd 100644 --- a/src/include/play-facade.h +++ b/src/include/play-facade.h @@ -60,7 +60,7 @@ namespace lumiera { /****************************************************************** - * Interface to the Player subsystem of Lumiera (Proc-Layer). + * 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. * @@ -100,7 +100,7 @@ namespace lumiera { void play(bool); ///< play/pause toggle void scrub(bool); ///< scrubbing playback void adjustSpeed(double); ///< playback speed control - void go(lib::time::Time); ///< skip to the given point in time + void go(time::Time); ///< skip to the given point in time void controlPlayhead (time::Control<time::Time> & ctrl); void controlDuration (time::Control<time::Duration> & ctrl); diff --git a/src/lib/iter-source.hpp b/src/lib/iter-source.hpp index 83f55e373..41b56e2c5 100644 --- a/src/lib/iter-source.hpp +++ b/src/lib/iter-source.hpp @@ -339,10 +339,10 @@ namespace lib { /** pipes a given Lumiera Forward Iterator through * a transformation function and wraps the resulting - * transforming Iterator, exposing just a IterSource. + * transforming Iterator, exposing just an IterSource. * This convenience shortcut can be used to build a * processing chain; the resulting IterSource will - * hide any involved detail types. + * hide any detail types involved. * @note as with any IterSource, there is one virtual * function call for every fetched element. */ diff --git a/src/proc/engine/calc-stream.hpp b/src/proc/engine/calc-stream.hpp index d1086abcf..3263ddfd5 100644 --- a/src/proc/engine/calc-stream.hpp +++ b/src/proc/engine/calc-stream.hpp @@ -71,12 +71,12 @@ namespace engine{ friend class EngineService; + public: CalcStream() { - UNIMPLEMENTED("build a calculation stream"); + UNIMPLEMENTED("build a disabled/dead calculation stream"); } - public: CalcStream (CalcStream const& o) { UNIMPLEMENTED("clone a calculation stream"); diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp index 6be0a2634..d96c40e96 100644 --- a/src/proc/engine/dispatcher.hpp +++ b/src/proc/engine/dispatcher.hpp @@ -40,7 +40,7 @@ namespace engine { // class ExitNode; /** - * @todo + * @todo 11/11 extremely fuzzy at the moment */ class Dispatcher { diff --git a/src/proc/play/output-manager.hpp b/src/proc/play/output-manager.hpp index 165c1af2f..696350e1a 100644 --- a/src/proc/play/output-manager.hpp +++ b/src/proc/play/output-manager.hpp @@ -61,6 +61,8 @@ namespace play { /****************************************************** * Management of external Output connections. * + * @todo need a concrete implementation + * @todo need a unit-test * @todo write Type comment */ class OutputManager diff --git a/src/proc/play/play-process.cpp b/src/proc/play/play-process.cpp index 6a3f2e465..01301d102 100644 --- a/src/proc/play/play-process.cpp +++ b/src/proc/play/play-process.cpp @@ -59,7 +59,8 @@ namespace play { throw error::State("unable to acquire a suitable output slot" /////////////////////TICKET #197 #816 , LUMIERA_ERROR_CANT_PLAY); - return Feed (port,slot); //////////////////////////////////TODO: how to get the RenderConfigurator (strategy) here??? + RenderConfigurator& configurator (*(RenderConfigurator*)NULL); //////////////////////////////////TODO: how to get the RenderConfigurator (strategy) here??? + return Feed (port,slot,configurator); } diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index 818f0725f..80df7a389 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -58,6 +58,7 @@ //#include "lib/singleton-ref.hpp" #include "proc/mobject/model-port.hpp" #include "proc/play/output-manager.hpp" +#include "proc/engine/calc-stream.hpp" #include "lib/iter-source.hpp" #include "lib/util.hpp" // diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index a3932d1c9..c2265c588 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -25,7 +25,7 @@ #include "include/play-facade.h" #include "proc/play/play-service.hpp" #include "proc/play/play-process.hpp" -#include "lib/util.hpp" +#include "lib/util-foreach.hpp" #include <string> @@ -61,6 +61,7 @@ namespace play { //using std::tr1::function; using std::tr1::placeholders::_1; using util::remove_if; + using util::and_all; namespace { // hidden local details of the service implementation.... @@ -80,6 +81,8 @@ namespace play { typedef weak_ptr<PlayProcess> Entry; typedef std::vector<Entry> ProcTable; + /** @note holding just weak references + * to any ongoing playback processes */ ProcTable processes_; public: @@ -88,13 +91,26 @@ namespace play { establishProcess (PlayProcess* newProcess) { lumiera::Play::Controller frontend; + try { + frontend.activate (newProcess, bind (&ProcessTable::endProcess, this, _1 )); + } + catch(...) + { + delete newProcess; + throw; + } Lock sync(this); - frontend.activate (newProcess, bind (&ProcessTable::endProcess, this, _1 )); - processes_.push_back (frontend); + processes_.push_back (frontend); // keeping a weak-reference return frontend; } + bool + isActive() const + { + return ! and_all (processes_, isDead); + } + private: void endProcess (PlayProcess* dyingProcess) @@ -119,7 +135,11 @@ namespace play { PlayService::~PlayService() { - UNIMPLEMENTED ("block waiting for any ongoing play processes. Ensure the process table is empty -- OR -- hand it over to some kind of cleanup service"); + if (pTable_->isActive()) + { + UNIMPLEMENTED ("block waiting for any ongoing play processes. Ensure the process table is empty -- OR -- hand it over to some kind of cleanup service"); + } + ENSURE (!pTable_->isActive()); } @@ -127,7 +147,7 @@ namespace play { * 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. + * this service through the lumiera::Play facade. */ PlayService::PlayService() : facadeAccess_(*this, "Player") @@ -143,11 +163,11 @@ namespace play { * * 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 + * 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. + * calculated media data to the outputs. */ Play::Controller PlayService::connect (ModelPorts dataGenerators, Output outputDestinations) @@ -155,7 +175,7 @@ namespace play { return pTable_->establishProcess( PlayProcess::initiate(dataGenerators, outputDestinations)); } - + LUMIERA_ERROR_DEFINE (CANT_PLAY, "unable to build playback or render process for this configuration"); @@ -195,7 +215,7 @@ namespace lumiera { Play::Controller Play::perform(Viewer) { - UNIMPLEMENTED ("build PlayProcess directly for a Viwer element"); + UNIMPLEMENTED ("build PlayProcess directly for a Viewer element"); } diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp index 9d70e23b6..144e24925 100644 --- a/src/proc/play/play-service.hpp +++ b/src/proc/play/play-service.hpp @@ -21,10 +21,33 @@ */ /** @file play-service.hpp - ** Player subsystem...... - ** - ** @see lumiera::DummyPlayer - ** @see gui::PlaybackController usage example + ** Primary service of the Player subsystem: Starting and controlling render/playback. + ** This is the implementation level service interface for the "Player". Client code + ** should access this service through the lumiera::Play facade interface. + ** + ** The player and render control subsystem allows to start and control playback and + ** rendering at any given collection of model ports (the conceptual exit points + ** of the High-Level-Model). These model ports are the (side effect) result of a + ** build process performed on the High-Level-Model, causing all nominal output + ** designations within the model to be resolved as far as possible. Additionally, + ** for \em playback, the global level of the model (the global pipes of a timeline) + ** need to be connected to a viewer component. This results in a further resolution + ** step, mapping the output designations to the concrete output possibilities of + ** the running Lumiera instance. This mapping information is kept embedded within + ** an OutputManager instance, passed as parameter when invoking the player service. + ** Alternatively, for \em rendering, a similar kind of output mapping information + ** needs to be provided, again embedded within an OutputManager instance, this + ** time leading to an output file to be rendered. + ** + ** The result of an invocation of the Player service is a Play::Controller + ** frontend object. This smart-ptr like handle can be used by the client to + ** control all aspects of playback or rendering; it behaves like a state machine. + ** When the last copy of this Play::Controller frontend goes out of scope, behind + ** the scenes the corresponding PlayProcess gets terminated and prepared for cleanup. + ** + ** @see engine::EngineService + ** @todo WIP-WIP-WIP as of Nov.2011 + ** */ diff --git a/src/proc/play/render-configurator.cpp b/src/proc/play/render-configurator.cpp index ffd091feb..65df04958 100644 --- a/src/proc/play/render-configurator.cpp +++ b/src/proc/play/render-configurator.cpp @@ -53,16 +53,19 @@ namespace play { : public RenderConfigurator { - RenderStreams + Feed::RenderStreams buildCalculationStreams (ModelPort port, OutputSlot& output) { UNIMPLEMENTED("build an active playback/render feed"); +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #832 ///TODO allocate the output slot ///TODO extract the individual channels ///TODO get the timings + ///TODO define the Quality engine::EngineService::instance().calculate(port, nominalTimings, activeOutputConnection, serviceQuality); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #832 } }; diff --git a/src/proc/play/render-configurator.hpp b/src/proc/play/render-configurator.hpp index 8811d803f..4dc94861b 100644 --- a/src/proc/play/render-configurator.hpp +++ b/src/proc/play/render-configurator.hpp @@ -40,6 +40,7 @@ //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" #include "proc/mobject/model-port.hpp" +#include "proc/play/play-process.hpp" //#include "proc/play/output-manager.hpp" //#include "lib/iter-source.hpp" //#include "lib/util.hpp" @@ -74,9 +75,7 @@ namespace play { virtual ~RenderConfigurator(); ///< this is an interface - typedef std::vector<engine::CalcStream> RenderStreams; ////TODO this belongs rather into class Feed - - virtual RenderStreams buildCalculationStreams (ModelPort, OutputSlot&) =0; + virtual Feed::RenderStreams buildCalculationStreams (ModelPort, OutputSlot&) =0; }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 848bfeafd..a80120a0d 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1376,6 +1376,25 @@ at the lowest level within the builder there is the step of building a //connect * by default, a timeline is outfitted with one video and one sound master bus </pre> </div> +<div title="CalcStream" modifier="Ichthyostega" modified="201112162336" created="201112162053" tags="spec Rendering" changecount="7"> +<pre>Calculation stream is an organisational unit used at the interface level of the Lumiera engine. +Representing a //stream of calculations,// delivering generated data within //timing constraints,// it is used +*by the [[play process(es)|PlayProcess]] to define and control properties of the output generation +*at the engine backbone to feed the [[Scheduler]] with individual [[render jobs|RenderJob]] to implement this stream of calculations +Calculation stream objects are stateless, constant chunks of definition -- any altering of playback or rendering parameters just causes the respective descriptors to be superseeded. The presence of a CalcStream (being alive within the denoted time span) implies that using any of the associated jobs, dispatcher tables, node and wiring descriptors is safe + +!lifecycle +Calculation stream descriptors can be default constructed, representing a //void calculation.// You can't do anything with these. +Any really interesting calculation stream needs to be retrieved from the EngineFaçade. Additionally, an existing calculation stream can be chained up or superseded, yielding a new CalcStream based on the parameters of the existing one, possibly with some alterations. + +!purpose +When a calculation stream is retrieved from the EngineFaçade it is already registered and attached there and represents an ongoing activity. Under the hood, several further collaborators will hold a copy of that calculation stream descriptor. While, as such, a CalcStream has no explicit state, at any time it //represents a current state.// In case the running time span of that stream is limited, it becomes superseded automatically, just by the passing of time. + +Each calculation stream refers a relevant [[frame dispatcher table|FrameDispatcher]]. Thus, for the engine (interface level), the calculation stream allows to produce the individual [[render jobs|RenderJob]] to enqueue with the [[Scheduler]]. This translation step is what links and relates nominal time with running wall clock time, thereby obeying the [[timing constraints|Timings]] given while initially defining the calculation stream. + +Additionally, each calculation stream knows how to access a //render environment closure,// allowing to re-schedule and re-adjust the setup of this stream. Basically, this closure is comprised of several functors (callbacks), which could be invoked to perform management tasks later on. Amongst others, this allows the calculation stream to redefine, supersede or "cancel itself", without the need to access a central registration table at the engine interface level. +</pre> +</div> <div title="ColorPalette" modifier="Ichthyostega" modified="200807131329" created="200706190033" tags="excludeMissing" changecount="14"> <pre>Background: #fefefd Foreground: #000 @@ -1973,12 +1992,12 @@ 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.// </pre> </div> -<div title="FixtureDatastructure" modifier="Ichthyostega" modified="201109031600" created="201012162304" tags="spec Builder" changecount="11"> +<div title="FixtureDatastructure" modifier="Ichthyostega" modified="201112162317" created="201012162304" tags="spec Builder" changecount="12"> <pre>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 -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. +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]] @@ -4386,13 +4405,13 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T <<tasksum end>> </pre> </div> -<div title="PlayProcess" modifier="Ichthyostega" modified="201108141652" created="201012181714" tags="def spec Player img" changecount="14"> +<div title="PlayProcess" modifier="Ichthyostega" modified="201112092000" created="201012181714" tags="def spec Player img" changecount="16"> <pre>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|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 [[Backend]] is responsible for [[dispatching|Dispatcher]] the [[calculation stream|CalcStream]] and scheduling individual calculation jobs +* the RenderEngine has the ability to carry out individual frame calculations * the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery [>img[Anatomy of a Play Process|uml/fig144005.png]] @@ -4402,7 +4421,7 @@ The Controller is exposed to the client and acts as frontend handle, while the p 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. </pre> </div> -<div title="PlayService" modifier="Ichthyostega" modified="201105221918" created="201105221900" tags="Player spec draft" changecount="9"> +<div title="PlayService" modifier="Ichthyostega" modified="201112161614" created="201105221900" tags="Player spec draft" changecount="11"> <pre>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 @@ -4415,7 +4434,7 @@ This is the core service provided by the player subsystem. The purpose is to cre ;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 +:to be able to create output, the PlayProcess needs to cooperate with [[output slots|OutputSlot]] :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. @@ -4425,16 +4444,16 @@ This is the core service provided by the player subsystem. The purpose is to cre :any details of this processing remain opaque for the clients; even the player subsystem just accesses the EngineFaçade </pre> </div> -<div title="Player" modifier="Ichthyostega" modified="201106131443" created="201012181700" tags="def overview" changecount="10"> +<div title="Player" modifier="Ichthyostega" modified="201112091904" created="201012181700" tags="def overview" changecount="13"> <pre>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: +Yet it addresses some central concerns: ;uniformity :all playback and render processes are on equal footing, handled in a similar way. ;integration :the player cares for the necessary integration with the other subsystems -:it consults the OutputManagement, retrieves the necessary informations from the [[Session]] and coordinates [[Backend]] calls. +:it consults the OutputManagement, retrieves the necessary informations from the [[Session]] and coordinates the forwarding of [[Backend]] calls. ;time quantisation :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. @@ -5132,8 +5151,8 @@ __see also__ &rarr; the protocol [[how to operate the nodes|NodeOperationProtocol]] </pre> </div> -<div title="RenderProcess" modifier="Ichthyostega" modified="201007170307" created="200706190705" tags="Rendering operational" changecount="28"> -<pre>For each segment (of the effective timeline), there is a Processor holding the exit node(s) of a processing network, which is a "Directed Acyclic Graph" of small, preconfigured, stateless [[processing nodes|ProcNode]]. This network is operated according to the ''pull principle'', meaning that the rendering is just initiated by "pulling" output from the exit node, causing a cascade of recursive downcalls or prerequisite calculations to be scheduled as separate jobs. Each node knows its predecessor(s), thus the necessary input can be pulled from there. Consequently, there is no centralized "engine object" which may invoke nodes iteratively or table driven &mdash; rather, the rendering can be seen as a passive service provided for the backend, which may pull from the exit nodes at any time, in any order (?), and possibly multithreaded. +<div title="RenderProcess" modifier="Ichthyostega" modified="201112162056" created="200706190705" tags="Rendering operational" changecount="29"> +<pre>For each segment (of the effective timeline), there is a Processor holding the exit node(s) of a processing network, which is a "Directed Acyclic Graph" of small, preconfigured, stateless [[processing nodes|ProcNode]]. This network is operated according to the ''pull principle'', meaning that the rendering is just initiated by "pulling" output from the exit node, causing a cascade of recursive downcalls or prerequisite calculations to be scheduled as individual [[jobs|RenderJob]]. Each node knows its predecessor(s), thus the necessary input can be pulled from there. Consequently, there is no centralized "engine object" which may invoke nodes iteratively or table driven &mdash; rather, the rendering can be seen as a passive service provided for the backend, which may pull from the exit nodes at any time, in any order (?), and possibly multithreaded. All State necessary for a given calculation process is encapsulated and accessible by a StateProxy object, which can be seen as the representation of "the process". At the same time, this proxy provides the buffers holding data to be processed and acts as a gateway to the backend to handle the communication with the Cache. In addition to this //top-level State,// each calculation step includes a small [[state adapter object|StateAdapter]] (stack allocated), which is pre-configured by the builder and serves the purpose to isolate the processing function from the detals of buffer management. @@ -5167,8 +5186,8 @@ Later on we expect a distinct __query subsystem__ to emerge, presumably embeddin &rarr; QuantiserImpl</pre> </div> -<div title="SchedulerRequirements" modifier="Ichthyostega" created="201107080145" tags="Rendering spec draft discuss" changecount="1"> -<pre>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: +<div title="SchedulerRequirements" modifier="Ichthyostega" modified="201112162055" created="201107080145" tags="Rendering spec draft discuss" changecount="3"> +<pre>The [[Scheduler]] is responsible for geting the individual [[render jobs|RenderJob]] 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 From 6852a6feffc4757dcdc0a6e3e12f7abca0ceef53 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 17 Dec 2011 04:45:42 +0100 Subject: [PATCH 308/496] WIP refactor building of the active render feeds RenderConfigurator becomes an internal strategy controlled by the PlayService --- src/proc/engine/calc-stream.hpp | 4 ++ src/proc/engine/engine-service.cpp | 18 +++-- src/proc/engine/engine-service.hpp | 4 +- src/proc/play/output-slot-connection.hpp | 8 ++- src/proc/play/output-slot.hpp | 9 +-- src/proc/play/play-process.cpp | 47 +++--------- src/proc/play/play-process.hpp | 16 ++--- src/proc/play/play-service.cpp | 29 ++++++-- src/proc/play/play-service.hpp | 6 ++ src/proc/play/render-configurator.cpp | 72 +++++++++++++++---- src/proc/play/render-configurator.hpp | 31 +++++++- src/proc/play/timings.hpp | 6 ++ .../proc/engine/engine-interface-test.cpp | 2 +- 13 files changed, 171 insertions(+), 81 deletions(-) diff --git a/src/proc/engine/calc-stream.hpp b/src/proc/engine/calc-stream.hpp index 3263ddfd5..9772c1c34 100644 --- a/src/proc/engine/calc-stream.hpp +++ b/src/proc/engine/calc-stream.hpp @@ -35,6 +35,7 @@ //#include <boost/noncopyable.hpp> //#include <boost/scoped_ptr.hpp> //#include <string> +#include <vector> namespace proc { @@ -86,6 +87,9 @@ namespace engine{ }; + typedef std::vector<CalcStream> CalcStreams; + + diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp index a4e989a19..926cef268 100644 --- a/src/proc/engine/engine-service.cpp +++ b/src/proc/engine/engine-service.cpp @@ -57,25 +57,33 @@ namespace engine{ - /** */ - CalcStream + /** core operation: activate the Lumiera Render Engine. + * Invoking this service effectively hooks up each channel + * of the given model exit point to deliver into the corresponding + * output sink on the given OutputConnection (which is assumed + * to be already allocated for active use by this connection). + * The generated calculation streams represent actively ongoing + * calculations within the engine, started right away, according + * to the given timing constraints and service quality. + */ + CalcStreams EngineService::calculate(ModelPort mPort, Timings nominalTimings, OutputConnection& output, Quality serviceQuality) { - UNIMPLEMENTED ("build a standard calculation stream"); + UNIMPLEMENTED ("build a list of standard calculation streams"); } /** */ - CalcStream + CalcStreams EngineService::calculateBackground(ModelPort mPort, Timings nominalTimings, Quality serviceQuality) { - UNIMPLEMENTED ("build a calculation stream for background rendering"); + UNIMPLEMENTED ("build calculation streams for background rendering"); } diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp index 7e5c1339b..3c0c1e708 100644 --- a/src/proc/engine/engine-service.hpp +++ b/src/proc/engine/engine-service.hpp @@ -138,13 +138,13 @@ namespace engine{ EngineService(); ~EngineService() { } - CalcStream + CalcStreams calculate(ModelPort mPort, Timings nominalTimings, OutputConnection& output, Quality serviceQuality =QoS_DEFAULT); - CalcStream + CalcStreams calculateBackground(ModelPort mPort, Timings nominalTimings, Quality serviceQuality =QoS_BACKGROUND); diff --git a/src/proc/play/output-slot-connection.hpp b/src/proc/play/output-slot-connection.hpp index e4e9b6787..2cbd34c4e 100644 --- a/src/proc/play/output-slot-connection.hpp +++ b/src/proc/play/output-slot-connection.hpp @@ -158,8 +158,14 @@ namespace play { return transform (eachElm(*this), connectOutputSink); } + Timings + getTimingConstraints() + { + UNIMPLEMENTED ("find out about timing constraints"); //////////////////////////TICKET #831 + } + bool - isActive() + isActive() const { return 0 < Connections::size(); } diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index 6fe5b09a2..8c7dd9c86 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -98,12 +98,13 @@ namespace play { class Allocation { public: - virtual OpenedSinks getOpenedSinks() =0; - virtual bool isActive() =0; + virtual OpenedSinks getOpenedSinks() =0; + virtual bool isActive() const =0; + + virtual Timings getTimingConstraints() =0; - /////TODO add here the getters for timing constraints protected: - ~Allocation(); + ~Allocation(); ///< never to be managed by clients directly }; /** established output channel */ diff --git a/src/proc/play/play-process.cpp b/src/proc/play/play-process.cpp index 01301d102..de1a66795 100644 --- a/src/proc/play/play-process.cpp +++ b/src/proc/play/play-process.cpp @@ -22,6 +22,7 @@ #include "proc/play/play-process.hpp" +#include "proc/play/play-service.hpp" #include "proc/play/render-configurator.hpp" #include "lib/itertools.hpp" @@ -40,65 +41,33 @@ namespace play { // using std::auto_ptr; // using boost::scoped_ptr; // using std::tr1::bind; + using lib::transform; namespace { // Implementation details... - using std::tr1::bind; - using std::tr1::function; - using std::tr1::placeholders::_1; - using lib::transform; - - - 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); - - RenderConfigurator& configurator (*(RenderConfigurator*)NULL); //////////////////////////////////TODO: how to get the RenderConfigurator (strategy) here??? - return Feed (port,slot,configurator); - } - - - typedef function<Feed(ModelPort)> 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); - } } // (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) + PlayProcess::initiate (ModelPorts dataGenerators, RenderConfigurator& activeOutputFeedBuilder) { return new PlayProcess (transform (dataGenerators, - resolve(outputDestinations))); - + activeOutputFeedBuilder)); } /** @internal actually create and configure a play process instance */ - PlayProcess::PlayProcess (Feed::Connections pipeConnections) + PlayProcess::PlayProcess (Connections pipeConnections) { if (isnil (pipeConnections)) throw error::State ("creating a PlayProcess without any usable output connections" @@ -113,8 +82,8 @@ namespace play { /** */ - Feed::Feed (ModelPort port, OutputSlot& output, RenderConfigurator& renderStrategy) - : renderStreams_(renderStrategy.buildCalculationStreams(port,output)) + Feed::Feed (engine::CalcStreams const& newActiveRenderingConnections) + : renderStreams_(newActiveRenderingConnections) { } diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index 80df7a389..532c3c70c 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -57,7 +57,6 @@ //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" #include "proc/mobject/model-port.hpp" -#include "proc/play/output-manager.hpp" #include "proc/engine/calc-stream.hpp" #include "lib/iter-source.hpp" #include "lib/util.hpp" @@ -78,7 +77,6 @@ namespace play { using util::isnil; using proc::mobject::ModelPort; - typedef proc::play::POutputManager POutputManager; typedef lib::IterSource<ModelPort>::iterator ModelPorts; namespace error = lumiera::error; @@ -96,18 +94,14 @@ namespace play { */ class Feed { + engine::CalcStreams renderStreams_; public: - typedef lib::IterSource<Feed>::iterator Connections; - - typedef std::vector<engine::CalcStream> RenderStreams; - - RenderStreams renderStreams_; /** building a Feed effectively involves the EngineService * to establish an actual rendering plan. Which is abstracted * here through the RenderConfigurator instance */ - Feed (ModelPort, OutputSlot&, RenderConfigurator&); + Feed (engine::CalcStreams const&); }; @@ -132,11 +126,13 @@ namespace play { { std::vector<Feed> outputFeeds_; - PlayProcess (Feed::Connections pipeConnections); + typedef lib::IterSource<Feed>::iterator Connections; + + PlayProcess (Connections pipeConnections); public: static PlayProcess* - initiate (ModelPorts dataGenerators, POutputManager outputDestinations); + initiate (ModelPorts dataGenerators, RenderConfigurator&); }; diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index c2265c588..b6ae24042 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -25,6 +25,7 @@ #include "include/play-facade.h" #include "proc/play/play-service.hpp" #include "proc/play/play-process.hpp" +#include "proc/play/render-configurator.hpp" #include "lib/util-foreach.hpp" @@ -62,7 +63,7 @@ namespace play { using std::tr1::placeholders::_1; using util::remove_if; using util::and_all; - + namespace { // hidden local details of the service implementation.... @@ -74,6 +75,11 @@ namespace play { } // (End) hidden service impl details + using lumiera::Play; + + typedef POutputManager Output; + + class ProcessTable : public Sync<RecursiveLock_NoWait> @@ -87,10 +93,10 @@ namespace play { public: - lumiera::Play::Controller + Play::Controller establishProcess (PlayProcess* newProcess) { - lumiera::Play::Controller frontend; + Play::Controller frontend; try { frontend.activate (newProcess, bind (&ProcessTable::endProcess, this, _1 )); } @@ -105,6 +111,7 @@ namespace play { return frontend; } + bool isActive() const { @@ -130,8 +137,7 @@ namespace play { }; - using lumiera::Play; - + PlayService::~PlayService() { @@ -173,11 +179,22 @@ namespace play { PlayService::connect (ModelPorts dataGenerators, Output outputDestinations) { return pTable_->establishProcess( - PlayProcess::initiate(dataGenerators, outputDestinations)); + PlayProcess::initiate(dataGenerators, + buildRenderConfiguration(outputDestinations))); } + /** */ + RenderConfigurator& + PlayService::buildRenderConfiguration (Output outputDestinations) + { + UNIMPLEMENTED ("how to build a suitable render configuration"); + } + + + + LUMIERA_ERROR_DEFINE (CANT_PLAY, "unable to build playback or render process for this configuration"); diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp index 144e24925..6cc4f9386 100644 --- a/src/proc/play/play-service.hpp +++ b/src/proc/play/play-service.hpp @@ -78,6 +78,7 @@ namespace play { // class DummyImageGenerator; // class TickService; class ProcessTable; + class RenderConfigurator; @@ -108,6 +109,8 @@ namespace play { /** Implementation: build a PlayProcess */ virtual Controller connect(ModelPorts, Output); + RenderConfigurator& buildRenderConfiguration(Output); + public: PlayService(); /////TODO Subsys::SigTerm terminationHandle); @@ -117,6 +120,9 @@ namespace play { }; + LUMIERA_ERROR_DECLARE (CANT_PLAY); ///< unable to build playback or render process for this configuration + + }} // namespace proc::play diff --git a/src/proc/play/render-configurator.cpp b/src/proc/play/render-configurator.cpp index 65df04958..b134de40b 100644 --- a/src/proc/play/render-configurator.cpp +++ b/src/proc/play/render-configurator.cpp @@ -21,13 +21,15 @@ * *****************************************************/ +#include "lib/error.hpp" #include "proc/play/render-configurator.hpp" +#include "proc/play/output-manager.hpp" #include "proc/engine/engine-service.hpp" //#include "lib/itertools.hpp" //#include <string> //#include <memory> -//#include <tr1/functional> +#include <tr1/functional> //#include <boost/scoped_ptr.hpp> @@ -35,11 +37,36 @@ namespace proc { namespace play { + namespace error = lumiera::error; // using std::string; // using lumiera::Subsys; // using std::auto_ptr; // using boost::scoped_ptr; -// using std::tr1::bind; + using std::tr1::bind; + using std::tr1::placeholders::_1; + using engine::EngineService; + + typedef EngineService::QoS_Definition RenderQuality; + + + + /** Template Method: how to build an active render feed, + * pulling from the given exit point of the model and + * feeding the OutputSlot established appropriately + * to deliver media data of suitable type */ + Feed + RenderConfigurator::buildActiveFeed (ModelPort port) + { + OutputSlot& slot = getOutputFor (port); + return Feed (buildCalculationStreams (port,slot)); + } + + + RenderConfigurator::RenderConfigurator() + : function<Feed(ModelPort)> (bind (&RenderConfigurator::buildActiveFeed, this, _1)) + { } + + namespace { // Implementation details... @@ -53,20 +80,41 @@ namespace play { : public RenderConfigurator { - Feed::RenderStreams + POutputManager outputResolver_; + Timings playbackTimings_; + RenderQuality renderQuality_; + + + OutputSlot& + getOutputFor (ModelPort port) + { + 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 slot; + } + + + engine::CalcStreams buildCalculationStreams (ModelPort port, OutputSlot& output) { - UNIMPLEMENTED("build an active playback/render feed"); -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #832 + OutputSlot::Allocation& activeOutputConnection = output.allocate(); + Timings nominalTimings = activeOutputConnection.getTimingConstraints() + .constrainedBy(playbackTimings_); - ///TODO allocate the output slot - ///TODO extract the individual channels - ///TODO get the timings - ///TODO define the Quality - - engine::EngineService::instance().calculate(port, nominalTimings, activeOutputConnection, serviceQuality); -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #832 + return EngineService::instance().calculate(port, nominalTimings, activeOutputConnection, renderQuality_); } + + + public: + DefaultRenderProcessBuilder(POutputManager outputManager, Timings playbackSpeed) + : outputResolver_(outputManager) + , playbackTimings_(playbackSpeed) + , renderQuality_(EngineService::QoS_DEFAULT) + { } + }; diff --git a/src/proc/play/render-configurator.hpp b/src/proc/play/render-configurator.hpp index 4dc94861b..7b9ef6336 100644 --- a/src/proc/play/render-configurator.hpp +++ b/src/proc/play/render-configurator.hpp @@ -41,6 +41,8 @@ //#include "lib/singleton-ref.hpp" #include "proc/mobject/model-port.hpp" #include "proc/play/play-process.hpp" +#include "proc/engine/calc-stream.hpp" +#include "proc/play/output-slot.hpp" //#include "proc/play/output-manager.hpp" //#include "lib/iter-source.hpp" //#include "lib/util.hpp" @@ -48,6 +50,7 @@ //#include <boost/noncopyable.hpp> //#include <boost/scoped_ptr.hpp> //#include <string> +#include <tr1/functional> #include <vector> @@ -59,6 +62,7 @@ namespace play { // using lumiera::Display; // using lumiera::DummyPlayer; // using util::isnil; + using std::tr1::function; using proc::mobject::ModelPort; // typedef proc::play::POutputManager POutputManager; @@ -69,13 +73,38 @@ namespace play { /** Strategy for configuring the render process */ class RenderConfigurator + : public function<Feed(ModelPort)> { public: virtual ~RenderConfigurator(); ///< this is an interface + private: + Feed buildActiveFeed (ModelPort); - virtual Feed::RenderStreams buildCalculationStreams (ModelPort, OutputSlot&) =0; + protected: + RenderConfigurator(); + + /** retrieve a suitable output sink for the data + * to be produced at the given model exit point. + * While the port already defines the necessary StreamType, + * this strategy still has to decide what concrete output sink + * to use accordingly. + */ + virtual OutputSlot& getOutputFor (ModelPort port) =0; + + + /** build active rendering connections, thereby delivering each channel + * of the given model port into the corresponding output sink. + * This strategy will try to allocate the output slot for output (may fail). + * Moreover, a suitable combination of output timings and service quality + * will be picked + * @return List of active CalcStream descriptors, created and registered + * with the EngineFacade, one for each channel connection. + * @note when this strategy function returns, the corresponding + * render activities are already up and running. + */ + virtual engine::CalcStreams buildCalculationStreams (ModelPort, OutputSlot&) =0; }; diff --git a/src/proc/play/timings.hpp b/src/proc/play/timings.hpp index a3613bbf9..5c6963f17 100644 --- a/src/proc/play/timings.hpp +++ b/src/proc/play/timings.hpp @@ -81,6 +81,12 @@ namespace play { { public: //////////////TODO accessor functions here + + Timings + constrainedBy (Timings additionalConditions) + { + UNIMPLEMENTED ("how to combine timing constraints"); + } }; diff --git a/tests/components/proc/engine/engine-interface-test.cpp b/tests/components/proc/engine/engine-interface-test.cpp index 3b0e96bd8..0345b0334 100644 --- a/tests/components/proc/engine/engine-interface-test.cpp +++ b/tests/components/proc/engine/engine-interface-test.cpp @@ -97,7 +97,7 @@ namespace test { Timings timings; /////////TODO // Invoke test subject... - CalcStream calc = engine.calculate(port, timings, output); + CalcStreams calc = engine.calculate(port, timings, output); ////TODO some direct checks on the calculation stream?? From c4bc53b6f6cb689a65363d2386489912be901a80 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 17 Dec 2011 22:37:53 +0100 Subject: [PATCH 309/496] how to pass a render configuration strategy when creating a new play process --- src/proc/play/play-process.cpp | 2 +- src/proc/play/play-process.hpp | 6 +++--- src/proc/play/play-service.cpp | 15 ++++++------- src/proc/play/play-service.hpp | 4 +--- src/proc/play/render-configurator.cpp | 31 +++++++++++++++++++++++---- src/proc/play/render-configurator.hpp | 21 ++++++++++++------ 6 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/proc/play/play-process.cpp b/src/proc/play/play-process.cpp index de1a66795..709b508c4 100644 --- a/src/proc/play/play-process.cpp +++ b/src/proc/play/play-process.cpp @@ -58,7 +58,7 @@ namespace play { * The caller gets to own and manage the returned process entry. */ PlayProcess* - PlayProcess::initiate (ModelPorts dataGenerators, RenderConfigurator& activeOutputFeedBuilder) + PlayProcess::initiate (ModelPorts dataGenerators, function<Feed(ModelPort)> activeOutputFeedBuilder) { return new PlayProcess (transform (dataGenerators, activeOutputFeedBuilder)); diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index 532c3c70c..fdcb68b10 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -63,6 +63,7 @@ // #include <boost/noncopyable.hpp> //#include <boost/scoped_ptr.hpp> +#include <tr1/functional> //#include <string> #include <vector> @@ -76,14 +77,13 @@ namespace play { // using lumiera::DummyPlayer; using util::isnil; using proc::mobject::ModelPort; + using std::tr1::function; typedef lib::IterSource<ModelPort>::iterator ModelPorts; namespace error = lumiera::error; - /** Strategy for configuring the render process */ - class RenderConfigurator; /** @@ -132,7 +132,7 @@ namespace play { public: static PlayProcess* - initiate (ModelPorts dataGenerators, RenderConfigurator&); + initiate (ModelPorts dataGenerators, function<Feed(ModelPort)>); }; diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index b6ae24042..5ddb4fe3f 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -26,12 +26,13 @@ #include "proc/play/play-service.hpp" #include "proc/play/play-process.hpp" #include "proc/play/render-configurator.hpp" +#include "proc/play/output-manager.hpp" #include "lib/util-foreach.hpp" #include <string> //#include <memory> -//#include <tr1/functional> +////#include <tr1/functional> #include <tr1/memory> //#include <boost/scoped_ptr.hpp> @@ -176,22 +177,18 @@ namespace play { * calculated media data to the outputs. */ Play::Controller - PlayService::connect (ModelPorts dataGenerators, Output outputDestinations) + PlayService::connect (ModelPorts dataGenerators, POutputManager outputPossibilities) { + Timings playbackTimings; /////////////////////////////////////////////////////////////TODO + return pTable_->establishProcess( PlayProcess::initiate(dataGenerators, - buildRenderConfiguration(outputDestinations))); + buildRenderConfiguration(outputPossibilities, playbackTimings))); } /** */ - RenderConfigurator& - PlayService::buildRenderConfiguration (Output outputDestinations) - { - UNIMPLEMENTED ("how to build a suitable render configuration"); - } - diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp index 6cc4f9386..daa494122 100644 --- a/src/proc/play/play-service.hpp +++ b/src/proc/play/play-service.hpp @@ -107,9 +107,7 @@ namespace play { /** Implementation: build a PlayProcess */ - virtual Controller connect(ModelPorts, Output); - - RenderConfigurator& buildRenderConfiguration(Output); + virtual Controller connect(ModelPorts, POutputManager); public: diff --git a/src/proc/play/render-configurator.cpp b/src/proc/play/render-configurator.cpp index b134de40b..42fc31a37 100644 --- a/src/proc/play/render-configurator.cpp +++ b/src/proc/play/render-configurator.cpp @@ -29,6 +29,7 @@ //#include <string> //#include <memory> +#include <tr1/memory> #include <tr1/functional> //#include <boost/scoped_ptr.hpp> @@ -42,6 +43,7 @@ namespace play { // using lumiera::Subsys; // using std::auto_ptr; // using boost::scoped_ptr; + using std::tr1::shared_ptr; using std::tr1::bind; using std::tr1::placeholders::_1; using engine::EngineService; @@ -49,6 +51,10 @@ namespace play { typedef EngineService::QoS_Definition RenderQuality; + + RenderConfigurator::~RenderConfigurator() { } // emit VTable here... + + /** Template Method: how to build an active render feed, * pulling from the given exit point of the model and @@ -62,9 +68,6 @@ namespace play { } - RenderConfigurator::RenderConfigurator() - : function<Feed(ModelPort)> (bind (&RenderConfigurator::buildActiveFeed, this, _1)) - { } @@ -124,7 +127,27 @@ namespace play { - /** */ + /** @internal this builder function is used by the PlayService + * when it comes to creating a new PlayProcess. The generated RenderConfigurator + * embodies the specific knowledge how to configure and setup the rendering or + * playback at the EngineFacade, based on the general playback speed and + * quality desirable for this playback process to be initiated. + * @remarks building a special subclass here and managing this instance + * by smart-ptr. Then wrapping all of this up into a functor, + * which can thus be passed on by value. This functor will + * later on be used to transform each desired model port + * into a suitable output connection, where the actual + * output will be resolved through the given + * OutputManager + */ + RenderConfigurator::ConnectFunction + buildRenderConfiguration (POutputManager outputPossibilities, Timings playbackTimings) + { + shared_ptr<RenderConfigurator> specialConfig (new DefaultRenderProcessBuilder (outputPossibilities, playbackTimings)); + + return bind (&RenderConfigurator::buildActiveFeed, specialConfig, _1 ); + } + }} // namespace proc::play diff --git a/src/proc/play/render-configurator.hpp b/src/proc/play/render-configurator.hpp index 7b9ef6336..44b44f171 100644 --- a/src/proc/play/render-configurator.hpp +++ b/src/proc/play/render-configurator.hpp @@ -43,11 +43,11 @@ #include "proc/play/play-process.hpp" #include "proc/engine/calc-stream.hpp" #include "proc/play/output-slot.hpp" -//#include "proc/play/output-manager.hpp" +#include "proc/play/output-manager.hpp" //#include "lib/iter-source.hpp" //#include "lib/util.hpp" // -//#include <boost/noncopyable.hpp> +#include <boost/noncopyable.hpp> //#include <boost/scoped_ptr.hpp> //#include <string> #include <tr1/functional> @@ -73,18 +73,17 @@ namespace play { /** Strategy for configuring the render process */ class RenderConfigurator - : public function<Feed(ModelPort)> + : boost::noncopyable { public: virtual ~RenderConfigurator(); ///< this is an interface - private: Feed buildActiveFeed (ModelPort); - protected: - RenderConfigurator(); + typedef function<Feed(ModelPort)> ConnectFunction; + protected: /** retrieve a suitable output sink for the data * to be produced at the given model exit point. * While the port already defines the necessary StreamType, @@ -107,6 +106,16 @@ namespace play { virtual engine::CalcStreams buildCalculationStreams (ModelPort, OutputSlot&) =0; }; + + + /** Factory function to build a RenderConfigurator + * specifically tailored for a given PlayProcess. + * @return the public access point to an RenderConfigurator, + * wrapped as generic function object + */ + RenderConfigurator::ConnectFunction + buildRenderConfiguration (POutputManager outputPossibilities, Timings playbackTimings); + From cedfa3407400bdc3c5ee3117d3178f64a92bb991 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 17 Dec 2011 22:40:01 +0100 Subject: [PATCH 310/496] considerations regarding clean-up of superseded model segments --- wiki/renderengine.html | 48 +++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index a80120a0d..9f4be7f2b 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1958,7 +1958,7 @@ Some further details * a special case of this factory use is the [[Singleton]] factory, which is used a lot within the Proy-Layer code </pre> </div> -<div title="Fixture" modifier="Ichthyostega" modified="201109031601" created="200706220324" tags="def spec Builder Model" changecount="48"> +<div title="Fixture" modifier="Ichthyostega" modified="201112171718" created="200706220324" tags="def spec Builder Model" changecount="50"> <pre>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. @@ -1967,7 +1967,7 @@ The Fixture acts as //isolation layer// between the two models, and as //backbon * these ~ExplicitPlacements are contained immediately within the Fixture, ordered by time * besides, there is a collection of all effective, possibly externally visible [[model ports|ModelPortRegistry]] -As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine. [[Creating the Fixture|BuildFixture]] is an important sideeffect of running the [[Builder]] when createing the [[render engine network|LowLevelModel]]. +As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine. [[Creating the Fixture|BuildFixture]] is an important first step and sideeffect of running the [[Builder]] when createing the [[render engine network|LowLevelModel]]. ''Note'': all of the especially managed storage of the LowLevelModel is hooked up behind the Fixture &rarr; FixtureStorage &rarr; FixtureDatastructure @@ -1985,14 +1985,14 @@ The fixture is like a grid, where one dimension is given by the [[model ports|Mo ;Segmentation :The segmentation partitiones the time axis of a single timeline into segments of constant (wiring) configuration :Together, the segments form a seamless sequence of time intervals. They contain a copy of each (explicit) placement of a visible object touching that time interval. Besides that, segments are the top level grouping device of the render engine node graph; they are always built and discarded at once. -:Segments may be //hot swapped// into an ongoing render. +:Segments (and even a different Segmentation) may be //hot swapped// into an ongoing render. ;Exit Nodes :Each segment holds an ExitNode for each relevant ModelPort of the corresponding timeline. :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.// </pre> </div> -<div title="FixtureDatastructure" modifier="Ichthyostega" modified="201112162317" created="201012162304" tags="spec Builder" changecount="12"> +<div title="FixtureDatastructure" modifier="Ichthyostega" modified="201112171816" created="201012162304" tags="spec Builder" changecount="13"> <pre>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. @@ -2009,7 +2009,7 @@ To support this usage pattern, the Fixture implementation makes use of the [[PIm ** get or create the frame dispatcher table ** dispatch a single frame to yield the corresponding ExitNode * (re)building -** create a new implementation frame +** create a new implementation transaction ** create a new segmentation ** establish what segments actually need to be rebuilt ** dispatch a newly built segment into the transaction @@ -2022,7 +2022,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 </pre> </div> -<div title="FixtureStorage" modifier="Ichthyostega" modified="201108142259" created="201012140231" tags="Builder impl operational draft" changecount="35"> +<div title="FixtureStorage" modifier="Ichthyostega" modified="201112171930" created="201012140231" tags="Builder impl operational draft" changecount="43"> <pre>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. @@ -2037,10 +2037,10 @@ Basically the concern is that each new CalcStream had to access the shared count ''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 calculation stream is done and re-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. Obviously, the [[Scheduler]] is in the best position for notifying the rest of the system when this and that [[job|RenderJob]] has terminated, because the Scheduler is the only facility required to touch each job reliably. Thus it seems favourable to add basic support for either termination callbacks or for guaranteed execution of some notification jobs to the [[Scheduler's requirements|SchedulerRequirements]]. !!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 (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. +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 CalcStream) 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 some kind of 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? @@ -2059,22 +2059,22 @@ But the primary question here is to judge the impact of such an implementation. &rArr; we should spend a second thought about how actually to find and process the segments to be discarded !!!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. +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 committing 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 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. ;Model A -:use a logarithmic datastructure, e.g. a priority queue -:problem here is that the priorities change, which either means shared access or a lot of "superseeded" entries +:use a logarithmic datastructure, e.g. a priority queue. Possibly together with LRU ordering +:problem here is that the priorities change, which either means shared access or a lot of "superseded" entries ;Model B -:keep all candidates around and track the tainted processes instead +:keep all superseded segments around and track the tainted processes instead :problem here is how to get the tainted processes precisely and with low overhead //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 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. +* is it even possible to continue a process when parts of the covered time-range 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 superseded segments until all of them are known to be terminated. </pre> </div> <div title="ForwardIterator" modifier="Ichthyostega" modified="200912190027" created="200910312114" tags="Concepts def spec" changecount="17"> @@ -5186,8 +5186,8 @@ Later on we expect a distinct __query subsystem__ to emerge, presumably embeddin &rarr; QuantiserImpl</pre> </div> -<div title="SchedulerRequirements" modifier="Ichthyostega" modified="201112162055" created="201107080145" tags="Rendering spec draft discuss" changecount="3"> -<pre>The [[Scheduler]] is responsible for geting the individual [[render jobs|RenderJob]] 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: +<div title="SchedulerRequirements" modifier="Ichthyostega" modified="201112171835" created="201107080145" tags="Rendering spec draft discuss" changecount="5"> +<pre>The [[Scheduler]] is responsible for geting the individual [[render jobs|RenderJob]] 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 &rarr; FixtureStorage), 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 @@ -5195,7 +5195,11 @@ Later on we expect a distinct __query subsystem__ to emerge, presumably embeddin ;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</pre> +: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 + +!detecting termination +The way other parts of the system are built, requires us to obtain a guaranteed knowledge of some job's termination. It is possible to obtain that knowledge with some limited delay, but it nees to be absoultely reliable (violations leading to segfault). The requirements stated above assume this can be achieved through //jobs with guaranteed execution.// Alternatively we could consider installing specific callbacks -- in this case the scheduler itself has to guarantee the invocation of these callbacks, even if the corresponding job fails or is never invoked. It doesn't seem there is any other option. +</pre> </div> <div title="ScopeLocator" modifier="Ichthyostega" modified="200911202035" created="200911192145" tags="def SessionLogic" changecount="10"> <pre>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. @@ -5249,13 +5253,13 @@ We need to detect attaching and detaching of * root &harr; [[Track]] </pre> </div> -<div title="Segmentation" modifier="Ichthyostega" modified="201105222236" created="201012121901" tags="def spec Builder" changecount="28"> +<div title="Segmentation" modifier="Ichthyostega" modified="201112171808" created="201012121901" tags="def spec Builder" changecount="34"> <pre>//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 ** which in turn is a list of non-overlapping segments *** each holding -**** an ExplicitPlacement for each covered object +**** an ExplicitPlacement for each object touching that time interval **** an ExitNode for each ModelPort of the corresponding timeline !Usage pattern @@ -5264,9 +5268,9 @@ When [[building the fixture|BuildFixture]], ~MObjects -- as handled by their Pla :&rarr; after //sorting,// the segmentation can be established, thereby copying placements spanning multiple segments :&rarr; only //after running the complete build process for each segment,// the list of model ports and exit nodes can be established ;(2) commit stage -: -- after the build process(es) are completed, the new fixture gets ''committed'', thus becoming the officially valid state to be rendered. As render processes might be going on in parallel, some kind of locking or barrier is required. It seems advisable to make the change into a single atomic hot-swap. Meaning we'd get a single access point to be protected. But there is another twist: We need to find out which render processes to cancel an restart, to pick up the changes introduced by this build process, which might include adding and deleting of timelines as a whole, and any changes to the segmentation grid. Because of the highly dynamic nature of the placements, on the other hand it isn't viable to expect the high-level model to provide this information. Thus we need to find out about a ''change coverage'' at this point. We might expand on that idea to //prune any new segments which aren't changed.// This way, only a write barrier would be necessary on switching the actually changed segments, and any render processes touching these would be //tainted.// Old allocations could be released after all tainted processes are known to be terminated. +: -- after the build process(es) are completed, the new fixture gets ''committed'', thus becoming the officially valid state to be rendered. As render processes might be going on in parallel, some kind of locking or barrier is required. It seems advisable to make the change into a single atomic hot-swap. Meaning we'd get a single access point to be protected. But there is another twist: We need to find out which render processes to cancel and restart, to pick up the changes introduced by this build process -- which might include adding and deleting of timelines as a whole, and any conceivable change to the segmentation grid. Because of the highly dynamic nature of the placements, on the other hand it isn't viable to expect the high-level model to provide this information. Thus we need to find out about a ''change coverage'' at this point. We might expand on that idea to //prune any new segments which aren't changed.// This way, only a write barrier would be necessary on switching the actually changed segments, and any render processes touching these would be //tainted.// Old allocations could be released after all tainted processes are known to be terminated. ;(3) rendering use -:Each play/render process employs a ''frame dispatch step'' to get the right exit node for pulling a given frame. From there on, the process proceeds into the [[processing nodes|ProcNodes]], interleaved with backend/scheduler actions due to splitting into individually scheduled jobs. The storage of these processing nodes and accompanying wiring descriptors is hooked up behind the individual segments, by sharing a common {{{AllocationCluster}}}. Yet the calculation of individual frames also depends on ''parameters'' and especially ''automation'' connected with objects in the high-level model. It is likely that there might be some sharing, as the intention was to allow ''live changes'' to automated values. <br/>{{red{WIP 12/2010}}} details need to be worked out. &rarr; [[parameter wiring concept|Wiring]] +:Each play/render process employs a ''frame dispatch step'' to get the right exit node for pulling a given frame. From there on, the process proceeds into the [[processing nodes|ProcNodes]], interleaved with backend/scheduler actions due to splitting into individually scheduled jobs. The storage of these processing nodes and accompanying wiring descriptors is hooked up behind the individual segments, by sharing a common {{{AllocationCluster}}}. Yet the calculation of individual frames also depends on ''parameters'' and especially ''automation'' linked with objects in the high-level model. It is likely that there might be some sharing or some kind of additional communication interface, as the intention was to allow ''live changes'' to automated values. <br/>{{red{WIP 12/2010}}} details need to be worked out. &rarr; [[parameter wiring concept|Wiring]] !!!observations * Storage and initialisation for explicit placements is an issue. We should strive at making that inline as much as possible. * the overall segmentation emerges from a sorting of time points, which are start points of explicit placements @@ -5280,7 +5284,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 is handled by the Fixture -# during the build process, there is a collection of placements; this can be discarded afterwards +# during the build process, there is a collecting and ordering of placements; these intermediaries as well as the initial collection 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. # the frame dispatch and the tracking of processes can be combined; data duplication is a virtue when it comes to parallel processes From fa0588b584536aaa07ec04fefa02f9c23442dc1d Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 18 Dec 2011 02:02:55 +0100 Subject: [PATCH 311/496] refactor to simplify generating a PlayProcess --- src/proc/play/play-process.cpp | 36 +++++++++++++++++----------------- src/proc/play/play-process.hpp | 11 ++++++----- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/proc/play/play-process.cpp b/src/proc/play/play-process.cpp index 709b508c4..159e111df 100644 --- a/src/proc/play/play-process.cpp +++ b/src/proc/play/play-process.cpp @@ -28,7 +28,7 @@ //#include <string> //#include <memory> -#include <tr1/functional> +//#include <tr1/functional> //#include <boost/scoped_ptr.hpp> @@ -42,6 +42,7 @@ namespace play { // using boost::scoped_ptr; // using std::tr1::bind; using lib::transform; + using lib::append_all; namespace { // Implementation details... @@ -50,6 +51,13 @@ namespace play { } // (End) hidden service impl details + PlayProcess::PlayProcess (OutputFeeds feeds) + : outputFeeds_(feeds) + { + if (isnil (feeds)) + throw error::State ("creating a PlayProcess without any usable output connections" + , LUMIERA_ERROR_CANT_PLAY); + } @@ -58,27 +66,19 @@ namespace play { * The caller gets to own and manage the returned process entry. */ PlayProcess* - PlayProcess::initiate (ModelPorts dataGenerators, function<Feed(ModelPort)> activeOutputFeedBuilder) - { - return new PlayProcess (transform (dataGenerators, - activeOutputFeedBuilder)); + PlayProcess::initiate (ModelPorts dataGenerators, FeedBuilder activeOutputFeedBuilder) + { + OutputFeeds newFeeds; + append_all (transform (dataGenerators, activeOutputFeedBuilder), newFeeds); + return new PlayProcess (newFeeds); +/////////////////////////////////////////////////////////////////////////////////////////////TICKET #874 : use a pipeline builder to write it as follows: +// treat_all(dataGenerators) +// .apply (activeOutputFeedBuilder) +// .buildVector(); } - /** @internal actually create and configure a play process instance */ - PlayProcess::PlayProcess (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) - { - - } - } /** */ diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index fdcb68b10..bf0733a72 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -103,6 +103,9 @@ namespace play { * here through the RenderConfigurator instance */ Feed (engine::CalcStreams const&); }; + + typedef function<Feed(ModelPort)> FeedBuilder; + typedef std::vector<Feed> OutputFeeds; /****************************************************** @@ -124,15 +127,13 @@ namespace play { class PlayProcess : boost::noncopyable { - std::vector<Feed> outputFeeds_; + OutputFeeds outputFeeds_; - typedef lib::IterSource<Feed>::iterator Connections; - - PlayProcess (Connections pipeConnections); + PlayProcess (OutputFeeds feeds); public: static PlayProcess* - initiate (ModelPorts dataGenerators, function<Feed(ModelPort)>); + initiate (ModelPorts dataGenerators, FeedBuilder); }; From 7fc84733375f5ae66b9c2de4e05c70337a57959c Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 19 Dec 2011 03:02:48 +0100 Subject: [PATCH 312/496] fix some size_t printf specs (32/64bit problem) --- src/proc/asset.cpp | 4 ++-- src/proc/mobject/session/scope-path.cpp | 2 +- tests/lib/meta/generator-test.cpp | 2 +- tests/lib/scoped-holder-test.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/proc/asset.cpp b/src/proc/asset.cpp index 531566b63..8b8865c65 100644 --- a/src/proc/asset.cpp +++ b/src/proc/asset.cpp @@ -66,12 +66,12 @@ namespace asset { , id(AssetManager::reg (this, idi)) , enabled(true) { - TRACE (assetmem, "ctor Asset(id=%lu) : adr=%p %s", size_t(id), this, cStr(this->ident) ); + TRACE (assetmem, "ctor Asset(id=%zu) : adr=%p %s", size_t(id), this, cStr(this->ident) ); } Asset::~Asset () { - TRACE (assetmem, "dtor Asset(id=%lu) : adr=%p", size_t(id), this ); + TRACE (assetmem, "dtor Asset(id=%zu) : adr=%p", size_t(id), this ); } diff --git a/src/proc/mobject/session/scope-path.cpp b/src/proc/mobject/session/scope-path.cpp index badf0b1db..f48054802 100644 --- a/src/proc/mobject/session/scope-path.cpp +++ b/src/proc/mobject/session/scope-path.cpp @@ -134,7 +134,7 @@ namespace session { ScopePath::~ScopePath() { - WARN_IF (refcount_, session, "Destroying a scope path frame with ref-count=%lu", refcount_); + WARN_IF (refcount_, session, "Destroying a scope path frame with ref-count=%zu", refcount_); } diff --git a/tests/lib/meta/generator-test.cpp b/tests/lib/meta/generator-test.cpp index 00a816c22..9e1583ed2 100644 --- a/tests/lib/meta/generator-test.cpp +++ b/tests/lib/meta/generator-test.cpp @@ -134,7 +134,7 @@ namespace test { subInterface.eat (b13); me_can_has_more_numberz.eat(); - INFO (test, "SizeOf = %lu", sizeof(me_can_has_more_numberz)); + INFO (test, "SizeOf = %zu", sizeof(me_can_has_more_numberz)); } }; diff --git a/tests/lib/scoped-holder-test.cpp b/tests/lib/scoped-holder-test.cpp index dd0734bb7..40b2637e7 100644 --- a/tests/lib/scoped-holder-test.cpp +++ b/tests/lib/scoped-holder-test.cpp @@ -108,8 +108,8 @@ namespace test{ TRACE (test, "holder at %p", &holder); TRACE (test, "object at %p", holder.get() ); - TRACE (test, "size(object) = %lu", sizeof(*holder)); - TRACE (test, "size(holder) = %lu", sizeof(holder)); + TRACE (test, "size(object) = %zu", sizeof(*holder)); + TRACE (test, "size(holder) = %zu", sizeof(holder)); } CHECK (0 == Dummy::checksum()); } From 74a982409e42794fda5ad0888d01ca52a6ddc41c Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 20 Dec 2011 04:59:00 +0100 Subject: [PATCH 313/496] document the internal structore of OutputSlot ...as far as decided and clarified by now --- doc/devel/uml/fig151685.png | Bin 0 -> 26732 bytes uml/lumiera/132229 | 35 ++- uml/lumiera/133637 | 404 ++++++++++++++++++++++++++++++-- uml/lumiera/136453.diagram | 38 +-- uml/lumiera/138885.diagram | 66 +++--- uml/lumiera/143877.diagram | 58 ++--- uml/lumiera/144005.diagram | 40 ++-- uml/lumiera/145157.diagram | 88 +++---- uml/lumiera/151685.diagram | 114 +++++++++ uml/lumiera/5.session | 14 +- uml/lumiera/generation_settings | 4 + uml/lumiera/lumiera.prj | 14 +- uml/lumiera/stereotypes | 3 + wiki/renderengine.html | 96 ++++++-- 14 files changed, 751 insertions(+), 223 deletions(-) create mode 100644 doc/devel/uml/fig151685.png create mode 100644 uml/lumiera/151685.diagram diff --git a/doc/devel/uml/fig151685.png b/doc/devel/uml/fig151685.png new file mode 100644 index 0000000000000000000000000000000000000000..a84679ddfc3db15f873cd102bc00ff9f97c7f5a9 GIT binary patch literal 26732 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 KlP^S=`u_)cmFs~3 literal 0 HcmV?d00001 diff --git a/uml/lumiera/132229 b/uml/lumiera/132229 index eebdd4e5b..1c0d62b34 100644 --- a/uml/lumiera/132229 +++ b/uml/lumiera/132229 @@ -1,16 +1,16 @@ -format 58 +format 74 "Session" // ProcessingLayer::MObject::Session - revision 4 + revision 6 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility 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 + 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 show_class_context_mode default show_msg_context_mode 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 + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default show_msg_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 @@ -28,13 +28,13 @@ format 58 classview 128005 "Session parts" //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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility 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 + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default show_msg_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 + 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 show_class_context_mode default show_msg_context_mode 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 @@ -43,7 +43,7 @@ format 58 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 128133 "Session structure" - draw_all_relations no 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 + draw_all_relations no hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default size A4 end @@ -846,7 +846,6 @@ ${members}}; python_decl "" idl_decl "" end - end class 128773 "AbstractMO" @@ -1478,13 +1477,13 @@ ${inlines} classview 131973 "Object ref" //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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility 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 + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default show_msg_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 + 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 show_class_context_mode default show_msg_context_mode 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 @@ -1493,7 +1492,7 @@ ${inlines} 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 136581 "MObjectRef" - 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default size A4 end @@ -1625,13 +1624,13 @@ ${inlines} classview 132101 "Datastructure" //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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility 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 + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default show_msg_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 + 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 show_class_context_mode default show_msg_context_mode 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 @@ -1640,7 +1639,7 @@ ${inlines} 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 136453 "Session backbone" - draw_all_relations no 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 + draw_all_relations no hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default size A4 end diff --git a/uml/lumiera/133637 b/uml/lumiera/133637 index 9857cb0e0..6aa5780ec 100644 --- a/uml/lumiera/133637 +++ b/uml/lumiera/133637 @@ -1,16 +1,16 @@ -format 58 +format 74 "Play" // ProcessingLayer::Play - revision 3 + revision 5 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility 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 + 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 show_class_context_mode default show_msg_context_mode 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 + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default show_msg_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 @@ -28,13 +28,13 @@ format 58 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility 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 + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default show_msg_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 + 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 show_class_context_mode default show_msg_context_mode 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 @@ -43,7 +43,12 @@ format 58 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + size A4 + end + + classdiagram 151685 "Player Output" + draw_all_relations no hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default size A4 end @@ -65,6 +70,276 @@ ${inlines} idl_decl "" explicit_switch_type "" + class 185221 "Allocation" + visibility protected + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 223749 // <directional composition> + relation 212613 *--> + stereotype "for_each_channel" + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type} ${name}${value}; +" + classrelation_ref 223749 // <directional composition> + b parent class_ref 178565 // DataSink + end + + operation 151301 "getOpenedSinks" + public explicit_return_type "" + nparams 0 + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + + + end + + operation 151429 "getTimings" + public explicit_return_type "" + nparams 0 + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + + + end + end + + classrelation 223621 // <dependency> + relation 212485 -_-> + stereotype "when-in-use" + a package + cpp default "#include in source" + classrelation_ref 223621 // <dependency> + b parent class_ref 185221 // Allocation + end + + operation 150917 "isFree" + public explicit_return_type "" + nparams 0 + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + + + end + + operation 151045 "allocate" + public explicit_return_type "" + nparams 0 + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + + + end + + operation 151173 "disconnect" + public explicit_return_type "" + nparams 0 + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + + + end + 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 "" + + classrelation 223877 // <unidirectional association> + relation 212741 ---> + stereotype "smart-Handle" + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 223877 // <unidirectional association> + b parent class_ref 185349 // Connection + end + + operation 150661 "lockBufferFor" + public explicit_return_type "" + nparams 0 + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + + + end + + operation 150789 "emit" + public explicit_return_type "" + nparams 0 + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + + + end + end + + class 185349 "Connection" + 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 "" + + operation 150277 "claimBufferFor" + public explicit_return_type "" + nparams 0 + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + + + end + + operation 150405 "transfer" + public explicit_return_type "" + nparams 0 + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + + + end + + operation 150533 "pushout" + public explicit_return_type "" + nparams 0 + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + + + end + end + + class 185477 "OutputSlotImpl" + 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 224005 // <generalisation> + relation 212869 ---|> + a public + cpp default "${type}" + classrelation_ref 224005 // <generalisation> + b parent class_ref 176133 // OutputSlot + end + + classrelation 224773 // <dependency> + relation 213637 -_-> + a default + cpp default "#include in source" + classrelation_ref 224773 // <dependency> + b parent class_ref 185861 // ConnectionStateManager + end end class 176261 "Controller" @@ -354,7 +629,7 @@ ${inlines} 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 + 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 show_class_context_mode default show_msg_context_mode default overlapping_bars size A4 end @@ -373,21 +648,6 @@ ${inlines} 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} @@ -402,5 +662,99 @@ ${inlines} explicit_switch_type "" end + + class 185605 "ConnectionState" + 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 224133 // <realization> + relation 212997 -_-|> + a public + cpp default "${type}" + classrelation_ref 224133 // <realization> + b parent class_ref 185221 // Allocation + end + end + + class 185733 "ConcreteConnection" + 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 224261 // <realization> + relation 213125 -_-|> + a public + cpp default "${type}" + classrelation_ref 224261 // <realization> + b parent class_ref 185349 // Connection + end + + classrelation 224901 // <unidirectional association> + relation 213765 ---> + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 224901 // <unidirectional association> + b parent class_ref 178693 // BufferProvider + end + end + + class 185861 "ConnectionStateManager" + visibility package + nformals 1 + formal name "CON" type "class" explicit_default_value "" + explicit_extends "" + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 224389 // <generalisation> + relation 213253 ---|> + a public + cpp default "${type}" + classrelation_ref 224389 // <generalisation> + b parent class_ref 185605 // ConnectionState + end + + classrelation 224517 // <dependency> + relation 213381 -_-> + a default + cpp default "#include in source" + classrelation_ref 224517 // <dependency> + b parent class_ref 185733 // ConcreteConnection + end + + classrelation 224645 // <dependency> + relation 213509 -_-> + a default + cpp default "#include in source" + classrelation_ref 224645 // <dependency> + b parent class_ref 185733 // ConcreteConnection + end + end end end diff --git a/uml/lumiera/136453.diagram b/uml/lumiera/136453.diagram index 3635c7caa..19f68b7d3 100644 --- a/uml/lumiera/136453.diagram +++ b/uml/lumiera/136453.diagram @@ -1,39 +1,39 @@ -format 58 +format 74 classcanvas 128005 class_ref 152069 // PlacementIndex - 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 410 14 2000 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 420.5 15.9 2000 end classcanvas 128261 class_ref 145541 // 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 43 87 2000 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 50.8 88.8 2000 end classcanvas 129029 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 124 194 2000 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 130 195.9 2000 end classcanvas 129157 class_ref 152325 // Binding - 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 124 87 2000 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 130.2 88.8 2000 end classcanvas 129285 class_ref 128389 // 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 247 194 3005 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 248 195.9 3005 end classcanvas 130437 class_ref 128005 // SessionImpl - 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 34 15 3005 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 43.4 16.2 3005 end classcanvas 130821 class_ref 167429 // BusMO - 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 229 88 2000 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 233.8 89.1 2000 end classcanvas 131077 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 - xyz 318 88 3005 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 318 89.1 3005 end textcanvas 131845 "»global pipes«" - xyzwh 245 73 2005 69 13 + xyzwh 245.9 73.9 2005 69 13 relationcanvas 129797 relation_ref 193413 // <unidirectional association> decenter_end 409 from ref 129157 z 1999 to ref 129029 @@ -52,7 +52,7 @@ relationcanvas 130309 relation_ref 193669 // <dependency> end relationcanvas 130565 relation_ref 184709 // <directional composition> from ref 130437 z 1999 to ref 128261 - role_a_pos 80 69 3000 no_role_b + role_a_pos 87 71 3000 no_role_b no_multiplicity_a no_multiplicity_b end relationcanvas 130693 relation_ref 193797 // <unidirectional association> diff --git a/uml/lumiera/138885.diagram b/uml/lumiera/138885.diagram index adeb01316..4b5a3c397 100644 --- a/uml/lumiera/138885.diagram +++ b/uml/lumiera/138885.diagram @@ -1,97 +1,97 @@ -format 58 +format 74 classinstancecanvas 128005 classinstance_ref 138373 // - xyz 32 157 2000 + xyz 32.7 158.8 2000 show_context_mode no end classinstancecanvas 128133 classinstance_ref 138501 // - xyz 200 157 2000 + xyz 200.5 158.8 2000 show_context_mode no end classinstancecanvas 128261 classinstance_ref 138629 // - xyz 200 211 2000 + xyz 200.5 212.2 2000 show_context_mode no end classinstancecanvas 128389 classinstance_ref 138757 // - xyz 236 252 2000 + xyz 236.1 253.3 2000 show_context_mode no end classinstancecanvas 128517 classinstance_ref 138885 // - xyz 236 279 2000 + xyz 236.1 280 2000 show_context_mode no end classinstancecanvas 128645 classinstance_ref 139013 // - xyz 236 307 2000 + xyz 236.1 308.8 2000 show_context_mode no end classinstancecanvas 128773 classinstance_ref 139141 // - xyz 33 211 2000 + xyz 37.2 212.2 2000 show_context_mode no end classinstancecanvas 128901 classinstance_ref 139269 // - xyz 96 211 2000 + xyz 100.1 212.2 2000 show_context_mode no end classinstancecanvas 130693 classinstance_ref 139397 // - xyz 319 191 2000 + xyz 323.8 192.2 2000 show_context_mode no end classinstancecanvas 130949 classinstance_ref 139525 // - xyz 313 157 2000 + xyz 317.8 158.8 2000 show_context_mode no end classinstancecanvas 131333 classinstance_ref 139653 // - xyz 319 308 2000 + xyz 319.4 309.9 2000 show_context_mode no end classinstancecanvas 131461 classinstance_ref 139781 // - xyz 276 346 2000 + xyz 276.1 347.7 2000 show_context_mode no end classinstancecanvas 131589 classinstance_ref 139909 // - xyz 393 343 2000 + xyz 393.8 344.4 2000 show_context_mode no end classinstancecanvas 131717 classinstance_ref 140037 // - xyz 393 274 2005 + xyz 393.8 275.5 2005 show_context_mode no end classinstancecanvas 132741 classinstance_ref 140165 // - xyz 319 91 2004 + xyz 319.4 92.2 2004 show_context_mode no end classinstancecanvas 133125 classinstance_ref 140293 // - xyz 236 129 2009 + xyz 236.1 130 2009 show_context_mode no end classinstancecanvas 133637 classinstance_ref 140421 // - xyz 503 308 2000 + xyz 503.8 309.9 2000 show_context_mode no end classinstancecanvas 133893 classinstance_ref 140677 // - xyz 492 252 2000 + xyz 497.5 253.3 2000 show_context_mode no end classinstancecanvas 134021 classinstance_ref 140805 // - xyz 562 129 2000 + xyz 566.1 130 2000 show_context_mode no end classinstancecanvas 134789 classinstance_ref 141189 // - xyz 319 53 2000 + xyz 319.4 54.4 2000 show_context_mode no end classinstancecanvas 135173 classinstance_ref 141317 // - xyz 562 91 2000 + xyz 566.1 92.2 2000 show_context_mode no end classinstancecanvas 135301 classinstance_ref 141445 // - xyz 562 53 2005 + xyz 566.1 54.4 2005 show_context_mode no end fragment 135813 "TypedLookup" - xyzwh 532 23 1994 148 137 + xyzwh 531.5 23.3 1994 148 137 end classinstancecanvas 136197 classinstance_ref 141573 // plugin1 - xyz 490 342 2000 + xyz 495.4 343.3 2000 show_context_mode no end classinstancecanvas 136325 classinstance_ref 141701 // plugin2 - xyz 490 396 2000 + xyz 495.4 397.7 2000 show_context_mode no end classinstancecanvas 137221 classinstance_ref 141829 // - xyz 32 263 2000 + xyz 36.4 264.4 2000 show_context_mode no end packagecanvas 137477 package_ref 128133 // Asset - name_in_tab yes color verylightorange xyzwh 470 183 1989 213 254 + name_in_tab yes color verylightorange xyzwh 470.6 181.8 1989 213 254 end packagecanvas 137605 package_ref 132229 // Session - name_in_tab yes xyzwh 15 25 1984 456 408 + name_in_tab yes xyzwh 15.2 23.7 1984 456 408 end note 137733 "top-level Timeline" - xyzwh 91 263 2000 64 45 + xyzwh 90.6 263.5 2000 64 45 note 137861 "virtual clip" - xyzwh 393 235 2010 69 35 + xyzwh 393.1 235.6 2010 69 35 classinstancecanvas 138117 classinstance_ref 139653 // - xyz 319 252 2015 + xyz 319.4 253.3 2015 show_context_mode no end objectlinkcanvas 129029 norel from ref 128773 z 1999 to ref 128901 @@ -161,7 +161,7 @@ objectlinkcanvas 138373 norel no_role_a no_role_b objectlinkcanvas 138885 norel decenter_begin 205 - from ref 133893 z 1999 to point 411 198 + from ref 133893 z 1999 to point 411.3 199 line 139013 z 1999 to ref 130693 no_role_a no_role_b line 130565 -_-_ diff --git a/uml/lumiera/143877.diagram b/uml/lumiera/143877.diagram index f49ed5f08..e39b7082b 100644 --- a/uml/lumiera/143877.diagram +++ b/uml/lumiera/143877.diagram @@ -1,56 +1,56 @@ -format 58 +format 74 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 356.7 107.5 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 78.4 173.3 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 150.8 223.3 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 482.8 48.8 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 484.8 166.2 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 362.9 267.5 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 207.4 381.7 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 362.2 382 2000 end fragment 130053 "output management" - xyzwh 299 17 2000 324 218 + xyzwh 299 16.5 2000 324 218 end fragment 130181 "Model / Fixture" - xyzwh 299 239 1995 323 91 + xyzwh 299 239.2 1995 323 91 end fragment 130309 "Player" - xyzwh 16 87 1990 274 243 + xyzwh 15.6 87.4 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 46.3 119.1 2005 end fragment 130565 "Engine" - xyzwh 16 346 1995 606 208 + xyzwh 16.4 345.7 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 + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 218 274.6 2000 end relationcanvas 128645 relation_ref 205445 // <realization> from ref 128517 z 1999 to ref 128389 @@ -59,15 +59,15 @@ relationcanvas 128645 relation_ref 205445 // <realization> end relationcanvas 128773 relation_ref 205573 // <directional aggregation> geometry HVH - from ref 128389 z 1999 to point 439 64 - line 129285 z 1999 to point 439 123 + from ref 128389 z 1999 to point 453 64 + line 129285 z 1999 to point 453 138 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 // <directional composition> geometry VH - from ref 128261 z 1999 to point 178 290 + from ref 128261 z 1999 to point 183 290 line 130949 z 1999 to ref 130693 no_role_a no_role_b no_multiplicity_a no_multiplicity_b @@ -83,6 +83,6 @@ relationcanvas 131205 relation_ref 205957 // <unidirectional association> no_multiplicity_a no_multiplicity_b end line 131333 -_-_ geometry VH - from ref 128133 z 1999 to point 99 239 + from ref 128133 z 1999 to point 105 239 line 131461 z 1999 to ref 128261 end diff --git a/uml/lumiera/144005.diagram b/uml/lumiera/144005.diagram index 8a28bdf33..6e70081c9 100644 --- a/uml/lumiera/144005.diagram +++ b/uml/lumiera/144005.diagram @@ -1,59 +1,59 @@ -format 58 +format 74 classinstancecanvas 128005 classinstance_ref 145669 // - xyz 41 85 2000 + xyz 46.7 86.6 2000 show_context_mode no end classinstancecanvas 128133 classinstance_ref 145797 // - xyz 105 142 2000 + xyz 112.7 143.3 2000 show_context_mode no end classinstancecanvas 128261 classinstance_ref 145925 // - xyz 171 173 2000 + xyz 171.6 174.4 2000 show_context_mode no end classinstancecanvas 128389 classinstance_ref 146053 // - xyz 171 240 2000 + xyz 171.6 241.1 2000 show_context_mode no end classinstancecanvas 128517 classinstance_ref 146181 // video-L - xyz 233 194 2000 color lightgreen + xyz 242.4 195.5 2000 color lightgreen show_context_mode no end classinstancecanvas 128645 classinstance_ref 146309 // video-R - xyz 233 216 2000 color lightgreen + xyz 243.5 217.7 2000 color lightgreen show_context_mode no end classinstancecanvas 128773 classinstance_ref 146437 // sound-W - xyz 233 269 2000 color lightgreen + xyz 243.8 270 2000 color lightgreen show_context_mode no end classinstancecanvas 128901 classinstance_ref 146565 // sound-X - xyz 233 291 2000 color lightgreen + xyz 242.6 292.2 2000 color lightgreen show_context_mode no end classinstancecanvas 129029 classinstance_ref 146693 // sound-Y - xyz 233 313 2000 color lightgreen + xyz 242.6 314.4 2000 color lightgreen show_context_mode no end classinstancecanvas 132101 classinstance_ref 146821 // - xyz 420 269 2000 color lightblue + xyz 425.6 270 2000 color lightblue show_context_mode no end classinstancecanvas 132229 classinstance_ref 146949 // - xyz 420 194 2000 color lightblue + xyz 425.6 195.5 2000 color lightblue show_context_mode no end note 132357 "stateless (functional)" - xyzwh 348 140 2000 124 35 + xyzwh 347.7 140.1 2000 124 35 note 132485 "stateful" - color verylightorange xyzwh 131 85 2000 72 36 + color verylightorange xyzwh 130.8 84.6 2000 72 36 classinstancecanvas 132613 classinstance_ref 147077 // - xyz 233 23 2000 + xyz 237 24.4 2000 show_context_mode no end classinstancecanvas 132741 classinstance_ref 147205 // - xyz 233 46 2000 + xyz 237 47.7 2000 show_context_mode no end classinstancecanvas 132869 classinstance_ref 148357 // sound-Z - xyz 233 335 2000 color lightgreen + xyz 242.6 336.6 2000 color lightgreen show_context_mode no end objectlinkcanvas 129285 norel geometry VH - from ref 128133 z 1999 to point 139 181 + from ref 128133 z 1999 to point 144 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 + from ref 128133 z 1999 to point 144 248 line 129797 z 1999 to ref 128389 no_role_a no_role_b objectlinkcanvas 129925 norel @@ -83,7 +83,7 @@ objectlinkcanvas 130949 norel no_role_a no_role_b objectlinkcanvas 131845 norel geometry VH - from ref 128005 z 1999 to point 68 150 + from ref 128005 z 1999 to point 74 150 line 131973 z 1999 to ref 128133 no_role_a no_role_b objectlinkcanvas 132997 norel diff --git a/uml/lumiera/145157.diagram b/uml/lumiera/145157.diagram index f75dd4db5..b051824fd 100644 --- a/uml/lumiera/145157.diagram +++ b/uml/lumiera/145157.diagram @@ -1,130 +1,130 @@ -format 58 +format 74 classinstance 128005 class_ref 178437 // Client - name "" xyz 53 4 2000 life_line_z 2000 + name "" xyz 53.1 3.7 2000 life_line_z 2000 classinstance 128133 class_ref 176133 // OutputSlot - name "" xyz 281 5 2000 life_line_z 2000 + name "" xyz 285.4 5 2000 life_line_z 2000 classinstance 128261 class_ref 178565 // DataSink - name "" xyz 140 28 2000 life_line_z 2000 + name "" xyz 143.6 27.9 2000 life_line_z 2000 classinstance 128389 class_ref 178693 // BufferProvider - name "" xyz 463 5 2000 life_line_z 2000 + name "" xyz 471.5 5 2000 life_line_z 2000 fragment 131845 "client side" - xyzwh 41 28 1995 156 279 + xyzwh 41.4 28.1 1995 156 279 end fragment 131973 "output implementation" - xyzwh 281 29 1995 262 277 + xyzwh 281.3 29.1 1995 262 277 end durationcanvas 128517 classinstance_ref 128005 // :Client - xyzwh 72 83 2010 11 52 + xyzwh 72 81.7 2010 11 52 end durationcanvas 129541 classinstance_ref 128005 // :Client - xyzwh 72 211 2010 11 41 + xyzwh 72 211.1 2010 11 41 end durationcanvas 129669 classinstance_ref 128261 // :DataSink - xyzwh 162 238 2010 11 25 + xyzwh 166 238 2010 11 25 end durationcanvas 129925 classinstance_ref 128133 // :OutputSlot - xyzwh 307 242 2010 11 63 + xyzwh 313 242.3 2010 11 63 overlappingdurationcanvas 133637 - xyzwh 313 272 2020 11 28 + xyzwh 319 271.5 2020 11 28 end end durationcanvas 130309 classinstance_ref 128389 // :BufferProvider - xyzwh 497 250 2010 11 26 + xyzwh 507 250.2 2010 11 26 end durationcanvas 131333 classinstance_ref 128133 // :OutputSlot - xyzwh 307 85 2010 11 34 + xyzwh 313 85.1 2010 11 34 end durationcanvas 132101 classinstance_ref 128261 // :DataSink - xyzwh 162 107 2010 11 25 + xyzwh 166 107.9 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 + xyzwh 166 169.1 2010 11 25 end durationcanvas 132869 classinstance_ref 128133 // :OutputSlot - xyzwh 307 173 2010 11 25 + xyzwh 313 173.5 2010 11 25 end durationcanvas 133125 classinstance_ref 128005 // :Client - xyzwh 72 211 2010 11 29 + xyzwh 72 211.5 2010 11 29 end durationcanvas 133381 classinstance_ref 128389 // :BufferProvider - xyzwh 497 180 2010 11 25 + xyzwh 507 180.2 2010 11 25 end durationcanvas 133893 classinstance_ref 128389 // :BufferProvider - xyzwh 497 280 2010 11 25 + xyzwh 507 280.2 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 + show_full_operations_definition default drawing_language default show_context_mode default + label_xy 110 231 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 + show_full_operations_definition default drawing_language default show_context_mode default + label_xy 220 235 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 + show_full_operations_definition default drawing_language default show_context_mode default + label_xy 383 244 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 + show_full_operations_definition default drawing_language default show_context_mode default + label_xy 105 76 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 + show_full_operations_definition default drawing_language default show_context_mode default + label_xy 208 100 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 + show_full_operations_definition default drawing_language default show_context_mode default + label_xy 105 122 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 + show_full_operations_definition default drawing_language default show_context_mode default + label_xy 102 160 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 + show_full_operations_definition default drawing_language default show_context_mode default + label_xy 227 166 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 + show_full_operations_definition default drawing_language default show_context_mode default + label_xy 90 200 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 + show_full_operations_definition default drawing_language default show_context_mode default + label_xy 383 174 reflexivemsg 133765 synchronous to durationcanvas_ref 133637 yz 272 2040 explicitmsg "pushout()" - show_full_operations_definition default drawing_language default - label_xy 325 260 + show_full_operations_definition default drawing_language default show_context_mode default + label_xy 328 261 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 + show_full_operations_definition default drawing_language default show_context_mode default + label_xy 381 283 preferred_whz 586 416 1 end diff --git a/uml/lumiera/151685.diagram b/uml/lumiera/151685.diagram new file mode 100644 index 000000000..359efa5c4 --- /dev/null +++ b/uml/lumiera/151685.diagram @@ -0,0 +1,114 @@ +format 74 + +classcanvas 128005 class_ref 176133 // OutputSlot + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 157.7 37 2000 +end +fragment 128261 "output management" + xyzwh 123 14 1990 244 292 +end +classcanvas 129029 class_ref 185221 // Allocation + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 260 126 2005 +end +classcanvas 129541 class_ref 178565 // DataSink + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 14 191 2005 +end +classcanvas 129925 class_ref 185349 // Connection + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 443 184 2000 +end +classcanvas 130309 class_ref 185477 // OutputSlotImpl + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + color green + xyz 149 422 2000 +end +classcanvas 130565 class_ref 185605 // ConnectionState + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 261 250 2000 +end +classcanvas 130949 class_ref 185733 // ConcreteConnection + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + color lightgreen + xyz 432 357 2000 +end +classcanvas 131205 class_ref 185861 // ConnectionStateManager + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 240 351 2000 +end +classcanvas 132613 class_ref 178693 // BufferProvider + draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default + xyz 534 456 2000 +end +fragment 132997 "concrete output driver" + xyzwh 123 309 1995 508 205 +end +note 133125 "Client +front-end" + xyzwh 15 270 2000 75 43 +note 133381 "Knowledge specific to this external output and driver" + xyzwh 564 338 2000 129 57 +relationcanvas 129157 relation_ref 212485 // <dependency> + decenter_begin 726 + from ref 128005 z 2006 stereotype "<<when-in-use>>" xyz 229 73 3000 to point 227 82 + line 132485 z 2006 to point 295 82 + line 132357 z 2006 to ref 129029 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 129669 relation_ref 212613 // <directional composition> + geometry HV + from ref 129029 z 2006 stereotype "<<for_each_channel>>" xyz 70 155 3000 to point 52 153 + line 130821 z 2006 to ref 129541 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +line 130053 ---+ decenter_end 876 + from ref 129029 z 2006 to ref 128005 +relationcanvas 130181 relation_ref 212741 // <unidirectional association> + from ref 129541 z 2006 stereotype "<<smart-Handle>>" xyz 92 206 3000 to ref 129925 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 130437 relation_ref 212869 // <generalisation> + from ref 130309 z 2001 to ref 128005 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 130693 relation_ref 212997 // <realization> + from ref 130565 z 2006 to ref 129029 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 131077 relation_ref 213125 // <realization> + from ref 130949 z 2001 to ref 129925 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 131333 relation_ref 213253 // <generalisation> + from ref 131205 z 2001 to ref 130565 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 131717 relation_ref 213509 // <dependency> + from ref 131205 z 2001 to ref 130949 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 131845 relation_ref 213637 // <dependency> + geometry HV + decenter_end 197 + from ref 130309 z 2001 to point 264 438 + line 132101 z 2001 to ref 131205 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 132741 relation_ref 213765 // <unidirectional association> + from ref 130949 z 2001 to point 574 423 + line 133253 z 2001 to ref 132613 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +preferred_whz 723 654 1 +end diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session index 87c7f7827..eff2c4d90 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -1,4 +1,4 @@ -window_sizes 1615 1020 270 1335 872 71 +window_sizes 1619 1028 270 1339 881 71 diagrams classdiagram_ref 136453 // Session backbone 631 352 100 4 0 0 @@ -8,8 +8,10 @@ diagrams 663 648 100 4 0 0 objectdiagram_ref 144005 // Play Process Structure 562 424 100 4 0 0 - active sequencediagram_ref 145157 // output data exchange + sequencediagram_ref 145157 // output data exchange 586 416 100 4 0 0 + active classdiagram_ref 151685 // Player Output + 723 654 100 4 0 0 end show_stereotypes selected @@ -18,7 +20,13 @@ open package_ref 128005 // design classview_ref 128389 // Controller Workings - classview_ref 136837 // PlayOut + class_ref 185221 // Allocation + class_ref 178565 // DataSink + class_ref 185349 // Connection + class_ref 185477 // OutputSlotImpl + class_ref 178693 // BufferProvider + + package_ref 132229 // Session class_ref 153733 // QueryFocusStack usecaseview_ref 128261 // config examples diff --git a/uml/lumiera/generation_settings b/uml/lumiera/generation_settings index 7ce8e827c..fd55246b6 100644 --- a/uml/lumiera/generation_settings +++ b/uml/lumiera/generation_settings @@ -244,6 +244,10 @@ ${docstring}${members} " // multiplicity != 1 python_default_operation_definition "${@}${static}${abstract}def ${name}${(}${)}: ${docstring}${body} +" + python_default_initoperation_definition "${@}${static}${abstract}def ${name}${(}${p0}${v0}${)}: +${docstring}super(${class}, ${p0}).__init__() +${body} " python_get "get${Name}" python_set "set${Name}" diff --git a/uml/lumiera/lumiera.prj b/uml/lumiera/lumiera.prj index 6c6cb4c64..b8b727236 100644 --- a/uml/lumiera/lumiera.prj +++ b/uml/lumiera/lumiera.prj @@ -1,6 +1,6 @@ -format 58 +format 74 "lumiera" - revision 71 + revision 73 modified_by 5 "hiv" cpp_root_dir "../../src/" @@ -9,13 +9,13 @@ format 58 // class settings default_attribute_visibility protected default_relation_visibility protected default_operation_visibility public //class diagram settings - draw_all_relations yes hide_attributes no hide_operations no show_members_full_definition no show_members_visibility no show_members_stereotype no show_members_multiplicity no show_members_initialization no member_max_width 127 show_parameter_dir yes show_parameter_name yes package_name_in_tab yes class_drawing_mode natural drawing_language uml show_context_mode no auto_label_position yes show_infonote no shadow yes show_stereotype_properties no + draw_all_relations yes hide_attributes no hide_operations no hide_getset_operations no show_members_full_definition no show_members_visibility no show_members_stereotype no show_members_context no show_members_multiplicity no show_members_initialization no show_attribute_modifiers no member_max_width 127 show_parameter_dir yes show_parameter_name yes package_name_in_tab yes class_drawing_mode natural drawing_language uml show_context_mode no auto_label_position yes show_relation_modifiers no show_relation_visibility no show_infonote no shadow yes show_stereotype_properties no //use case diagram settings package_name_in_tab no show_context no auto_label_position yes draw_all_relations yes class_drawing_mode actor shadow yes show_stereotype_properties no //sequence diagram settings - show_full_operations_definition no write_horizontally yes class_drawing_mode natural drawing_language uml draw_all_relations yes shadow yes show_stereotype_properties no + show_full_operations_definition no write_horizontally yes class_drawing_mode natural drawing_language uml draw_all_relations yes shadow yes show_stereotype_properties no show_class_context_mode no show_msg_context_mode no //collaboration diagram settings - show_full_operations_definition no show_hierarchical_rank no write_horizontally yes drawing_language uml package_name_in_tab no show_context no draw_all_relations yes shadow yes show_stereotype_properties no + show_full_operations_definition no show_hierarchical_rank no write_horizontally yes drawing_language uml package_name_in_tab no show_context no show_msg_context no draw_all_relations yes shadow yes show_stereotype_properties no //object diagram settings write_horizontally yes package_name_in_tab no show_context no auto_label_position yes draw_all_relations yes shadow yes show_stereotype_properties no //component diagram settings @@ -31,8 +31,8 @@ format 58 package_name_in_tab yes show_context no show_opaque_action_definition yes auto_label_position yes write_flow_label_horizontally no draw_all_relations yes shadow yes show_infonote yes drawing_language uml show_stereotype_properties no - class_color yellow duration_color transparent continuation_color gray note_color blue fragment_color transparent subject_color transparent usecase_color yellow package_color transparent component_color green artifact_color green deploymentnode_color gray state_color yellow stateaction_color transparent activity_color lightgreen activityregion_color transparent activityaction_color mediumgreen parameterpin_color white - font_size 8 + class_color yellow duration_color transparent continuation_color gray note_color blue fragment_color transparent subject_color transparent usecase_color yellow package_color transparent component_color green artifact_color green deploymentnode_color gray state_color yellow stateaction_color transparent activity_color lightgreen activityregion_color transparent activitypartition_color transparent activityaction_color mediumgreen parameterpin_color white + font_size 7 diagram_format A4 mark_for_import diff --git a/uml/lumiera/stereotypes b/uml/lumiera/stereotypes index d127ffd99..6d58da350 100644 --- a/uml/lumiera/stereotypes +++ b/uml/lumiera/stereotypes @@ -36,6 +36,8 @@ activitynode_stereotypes 0 activityaction_stereotypes 0 activityobject_stereotypes 2 "datastore" "centralBuffer" + expansionregion_stereotypes 0 + activitypartition_stereotypes 0 pin_stereotypes 0 component_stereotypes 7 "build" "component" "entity" "implement" "process" "service" "subsystem" deploymentnode_stereotypes 2 "cpu" "device" @@ -45,6 +47,7 @@ deploymentview_stereotypes 0 classdiagram_stereotypes 0 seqdiagram_stereotypes 0 + msg_stereotypes 0 coldiagram_stereotypes 0 usecasediagram_stereotypes 0 statediagram_stereotypes 0 diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 9f4be7f2b..a3d7da184 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1839,14 +1839,18 @@ 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. </pre> </div> -<div title="EngineFaçade" modifier="Ichthyostega" modified="201108141650" created="201105222328" tags="Rendering overview spec" changecount="5"> +<div title="EngineFaçade" modifier="Ichthyostega" modified="201112190142" created="201105222328" tags="Rendering overview spec" changecount="8"> <pre>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 [[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 -</pre> + +!Quality of Service +Within the Facade, there is the definition of the {{{EngineService::Quality}}} tag, alongside with several pre-defined quality settings. +Actually this interface is a strategy, allowing to define quite specific quality levels, in case we need that. Clients can usually just use +these ~QoS-tags like enum values (they are copyable), without caring for the engine implementation related details.</pre> </div> <div title="EntryID" modifier="Ichthyostega" modified="201012300036" created="201012300026" tags="def Types" changecount="5"> <pre>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. @@ -3444,7 +3448,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. </pre> </div> -<div title="OutputSlot" modifier="Ichthyostega" modified="201111042355" created="201106162339" tags="def Concepts Player spec" changecount="40"> +<div title="OutputSlot" modifier="Ichthyostega" modified="201112200358" created="201106162339" tags="def Concepts Player spec img" changecount="49"> <pre>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 (video in GUI window, video fullscreen, sound, Jack, rendering to file) can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement) @@ -3453,17 +3457,76 @@ 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. (in special cases, some 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) +[>img[Outputslot implementation structures|uml/fig151685.png]] 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 are 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 +!!!usage and implementation +Clients retrieve just a reference to an output slot by asking a suitable OutputManager for an output possibility supporting a specific format. Usually, they just "claim" this slot by invoking {{{allocate()}}}, which behind the scenes causes building of the actual output connections and mechanisms. For each such connection -- corresponding to a single channel within the media format handled by this ~OutputSlot -- the client gets a smart-handle {{{DataSink}}}. The concrete ~OutputSlot implementation performs operations quite specific to the kind of output and external interface in question. All tese specific handling is embodied within the concrete connection implementation used by the concrete ~OutputSlot + +!!!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 assumed to be 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. + +!!!Lifecycle and storage +The concrete OutputSlot implementation is owned and managed by the facility actually providing the output possibility in question. For example, the GUI provides viewer widgets, while some sound output backend provides sound ports. The associated OutputSlot implementation object is required to stay alive as long as it's registered with some OutputManager. It needs to be de-registered explicitly prior to destruction -- and this deregistration may block until all clients using this slot did terminate. Beyond that, an output slot implementation is expected to handle all kinds of failures gracefully -- preferably just emitting a signal (callback functor). +{{red{TODO 7/11: Deregistration is an unsolved problem....}}} + +!!!unified data exchange 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. + +!!!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 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; OutputSlotDesign +&rarr; OutputSlotImpl +</pre> +</div> +<div title="OutputSlotDesign" modifier="Ichthyostega" created="201112190322" tags="design decision" changecount="1"> +<pre>The OutputSlot interface describes a point where generated media data can actually be sent to the external world. It is expected to be implemented by adapters and bridges to existing drivers or external interface libraries, like a viewer widget in the GUI, ALSA or Jack sound output, rendering to file, using an external media format library. The design of this core facility was rather difficult and stretched out over quite some time span -- this page documents the considerations and the decisions taken. + +!intention +OutpotSlot is a metaphor to unify 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 (video in GUI window, video fullscreen, sound, Jack, rendering to file) can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement) + +!design possibilities +!!properties as a starting point +* each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output +* an output slot needs to be provided, configured and registered, using an implementation specifically tailored for the kind of media data +* 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. +* 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. +* this allocation is exclusive: at any time, there may be only a single client using a given output slot. +* the calculating process feeds its results into //sink handles//&nbsp; provided by the allocated output slot. +* allocation of an output slot leads to very specific [[timing expectations|Timings]] +* the client is required to comply to these timings and operate according to a strictly defined protocol. +* timing glitches will be detected due to this protocol; the output slot provides mechanisms for failing gracefully in these cases + +!!data exchange models 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 :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 signalles 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, &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. +!!relation to timing +Besides the sink handles, allocation of an output slot defines some timing constraints, which are binding for the client. These include a grid of deadlines for each frame to deliver, plus a fixed //latency.// 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 assumed to be 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. @@ -3498,25 +3561,8 @@ The planned delivery time of a frame is used as an ID throughout that cycle # 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 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</pre> +&rarr; OutputSlotImpl +</pre> </div> <div title="OutputSlotImpl" modifier="Ichthyostega" modified="201109232235" created="201107102343" tags="spec operational" changecount="27"> <pre>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. From d27e3b15a95ce0204854935ae85b6e95261acab7 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 23 Dec 2011 02:22:38 +0100 Subject: [PATCH 314/496] clarify the handling of specific output operation modes (e.g. number of channels) --- doc/devel/uml/fig151685.png | Bin 26732 -> 26030 bytes src/proc/play/output-slot.cpp | 26 ++++---- src/proc/play/output-slot.hpp | 62 +++++++++++++----- src/proc/play/play-service.cpp | 2 +- src/proc/play/render-configurator.cpp | 2 +- .../proc/play/diagnostic-output-slot.hpp | 29 ++++++-- uml/lumiera/151685.diagram | 38 +++++------ uml/lumiera/5.session | 4 +- uml/lumiera/lumiera.prj | 2 +- wiki/renderengine.html | 22 +++++-- 10 files changed, 126 insertions(+), 61 deletions(-) diff --git a/doc/devel/uml/fig151685.png b/doc/devel/uml/fig151685.png index a84679ddfc3db15f873cd102bc00ff9f97c7f5a9..77b41e4461a4ee43906885387f508dac49cac77f 100644 GIT binary patch literal 26030 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�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 F{|60y2E+gW literal 26732 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 KlP^S=`u_)cmFs~3 diff --git a/src/proc/play/output-slot.cpp b/src/proc/play/output-slot.cpp index e93acb278..87eaf5051 100644 --- a/src/proc/play/output-slot.cpp +++ b/src/proc/play/output-slot.cpp @@ -38,14 +38,6 @@ namespace play { - namespace { // hidden local details of the service implementation.... - - } // (End) hidden service impl details - - - - - OutputSlot::~OutputSlot() { } // emit VTables here.... @@ -68,15 +60,26 @@ namespace play { } - /** */ + /** claim this OutputSlot for active use as output sink(s). + * At any point, a given slot can only be used for a single + * ongoing output process (which may serve several channels though). + * The assumption is for the OutputSlot to be picked through a query + * to some OutputManater, so the parameters (resolution, sample rate...) + * should be suited for the intended use. Thus no additional configuration + * is necessary. + * @return Allocation representing the "connected state" from the client's POV. + * The client may retrieve the effectively required Timings from there, + * as well as the actual output sinks, ready for use. + * @remarks calls back into #buildState, where the concrete OutputSlot + * is expected to provide a private Connection implementation, + * subclassing OutputSlot::Allocation + */ OutputSlot::Allocation& OutputSlot::allocate() { if (!isFree()) throw error::Logic ("Attempt to open/allocate an OutputSlot already in use."); - UNIMPLEMENTED ("internal interface to determine the number of channel-connections"); - state_.reset (this->buildState()); return *state_; } @@ -110,7 +113,6 @@ namespace play { connection.discard(data2emit); } - diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index 8c7dd9c86..860232b1f 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -26,9 +26,39 @@ ** 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 + ** all using the same resolution and mapping mechanisms ** - ** @see diagnostic-output-slot.hpp ////TODO + ** 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 sound card output channels), + ** or an output file or similar capability accepting media content. Initially, an output slot + ** needs to be provided, configured and registered, using an implementation suitable for the + ** kind of media data to be output (sound, video) and also suitable for the special circumstances + ** of the output capability (render a file, display video in a GUI widget, send video to some + ** 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 + ** (e.g. stereoscopic video, multichannel sound). + ** + ** In order to be usable as output sink, an output slot needs to be \em allocated: 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 + ** to be fed into the slot. (in special cases, some 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 calculating process feeds its results into those handles. + ** Size and other characteristics of the data frames are assumed to be suitable, which typically + ** won't be verified at that level anymore. Besides that, the allocation of an output slot reveals + ** detailed timing expectations. The client is required to comply to these timings when \em emitting + ** data — he's even required to provide a current time specification, alongside with the data. + ** Based on this information, 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. + ** + ** @see OutputSlotProtocol_test + ** @see diagnostic-output-slot.hpp */ @@ -42,25 +72,18 @@ #include "proc/engine/buffer-provider.hpp" #include "proc/play/timings.hpp" #include "lib/iter-source.hpp" -//#include "lib/sync.hpp" #include <boost/noncopyable.hpp> #include <boost/scoped_ptr.hpp> -//#include <string> -//#include <vector> -//#include <tr1/memory> namespace proc { namespace play { - + using proc::engine::BuffHandle; using proc::engine::BufferProvider; using lib::time::TimeValue; -//using std::string; - -//using std::vector; -//using std::tr1::shared_ptr; + using boost::scoped_ptr; @@ -71,10 +94,16 @@ namespace play { - /******************************************************************** + /******************************************************************************** * Interface: Generic output sink. - * - * @todo write type comment + * An OutputSlot represents the possibility to send data through multiple + * channels to some kind of external sink (video in GUI window, video full screen, + * sound, Jack, rendering to file). Clients are expected to retrieve a suitably + * preconfigured implementation from some OutputManager instance. An OutputSlot + * needs to be \em claimed for output by invoking #allocate, which returns a + * representation of the connection state. This operation is exclusive. + * The actual \link DataSink output sinks \endlink can be retrieved + * through the Allocation object returned from there. */ class OutputSlot : boost::noncopyable @@ -87,6 +116,9 @@ namespace play { scoped_ptr<ConnectionState> state_; + /** build the \em connected state, + * based on the existing configuration + * within this concrete OutputSlot */ virtual ConnectionState* buildState() =0; @@ -133,7 +165,7 @@ namespace play { public: BuffHandle lockBufferFor(FrameID); - void emit(FrameID, BuffHandle const&, TimeValue currentTime = Time::MAX); ///////////////TICKET #855 + void emit(FrameID, BuffHandle const&, TimeValue currentTime = Time::ANYTIME); }; diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index 5ddb4fe3f..0674fd934 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -179,7 +179,7 @@ namespace play { Play::Controller PlayService::connect (ModelPorts dataGenerators, POutputManager outputPossibilities) { - Timings playbackTimings; /////////////////////////////////////////////////////////////TODO + Timings playbackTimings; //////////////////////////////////////////////////////////////////////TICKET #875 return pTable_->establishProcess( PlayProcess::initiate(dataGenerators, diff --git a/src/proc/play/render-configurator.cpp b/src/proc/play/render-configurator.cpp index 42fc31a37..ee0552dfe 100644 --- a/src/proc/play/render-configurator.cpp +++ b/src/proc/play/render-configurator.cpp @@ -107,7 +107,7 @@ namespace play { Timings nominalTimings = activeOutputConnection.getTimingConstraints() .constrainedBy(playbackTimings_); - return EngineService::instance().calculate(port, nominalTimings, activeOutputConnection, renderQuality_); + return EngineService::instance().calculate (port, nominalTimings, activeOutputConnection, renderQuality_); } diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index 4a6f758d2..ff948d814 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -227,20 +227,39 @@ namespace play { /******************************************************************** * Helper for unit tests: Mock output sink. - * - * @todo write type comment + * Complete implementation of the OutputSlot interface, with some + * additional stipulations to support unit testing. + * - the implementation uses a special protocol output buffer, + * which stores each "frame" in memory for later investigation + * - the output data in the buffers handed over from client + * actually hold an TestFrame instance + * - the maximum number of channels and the maximum number + * of acceptable frames is limited to 5 and 100. + * @warning any Captured (test) data from all individual instances + * remains in memory until shutdown of the current executable */ class DiagnosticOutputSlot : public OutputSlot { static const uint MAX_CHANNELS = 5; - + + /** @note a real OutputSlot implementation + * would rely on some kind of embedded + * configuration here */ + uint + getOutputChannelCount() + { + return MAX_CHANNELS; + } + + /** hook into the OutputSlot frontend */ ConnectionState* buildState() { - return new SimulatedOutputSequences(MAX_CHANNELS); + return new SimulatedOutputSequences( + getOutputChannelCount()); } /** @internal is self-managed and non-copyable. @@ -254,7 +273,7 @@ namespace play { accessSequence (uint channel) { REQUIRE (!isFree(), "diagnostic OutputSlot not (yet) connected"); - REQUIRE (channel <= MAX_CHANNELS); + REQUIRE (channel <= getOutputChannelCount()); return static_cast<SimulatedOutputSequences&> (*state_).at(channel); } diff --git a/uml/lumiera/151685.diagram b/uml/lumiera/151685.diagram index 359efa5c4..19335767f 100644 --- a/uml/lumiera/151685.diagram +++ b/uml/lumiera/151685.diagram @@ -2,64 +2,64 @@ format 74 classcanvas 128005 class_ref 176133 // OutputSlot draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default - xyz 157.7 37 2000 + xyz 155.04 38.2 2000 end fragment 128261 "output management" - xyzwh 123 14 1990 244 292 + xyzwh 126.62 17.82 1990 240 288 end classcanvas 129029 class_ref 185221 // Allocation draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default - xyz 260 126 2005 + xyz 247.02 129.02 2005 end classcanvas 129541 class_ref 178565 // DataSink draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default - xyz 14 191 2005 + xyz 13.16 194.12 2005 end classcanvas 129925 class_ref 185349 // Connection draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default - xyz 443 184 2000 + xyz 419.32 189.12 2000 end classcanvas 130309 class_ref 185477 // OutputSlotImpl draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default color green - xyz 149 422 2000 + xyz 149.12 420.84 2000 end classcanvas 130565 class_ref 185605 // ConnectionState draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default - xyz 261 250 2000 + xyz 249.88 248.84 2000 end classcanvas 130949 class_ref 185733 // ConcreteConnection draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default color lightgreen - xyz 432 357 2000 + xyz 408.32 358 2000 end classcanvas 131205 class_ref 185861 // ConnectionStateManager draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default - xyz 240 351 2000 + xyz 228.88 351 2000 end classcanvas 132613 class_ref 178693 // BufferProvider draw_all_relations default hide_attributes default hide_operations default hide_getset_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_context default show_members_multiplicity default show_members_initialization default show_attribute_modifiers 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_relation_modifiers default show_relation_visibility default show_infonote default shadow default show_stereotype_properties default - xyz 534 456 2000 + xyz 484.36 459.64 2000 end fragment 132997 "concrete output driver" - xyzwh 123 309 1995 508 205 + xyzwh 126.68 309.48 1995 464 204 end note 133125 "Client front-end" - xyzwh 15 270 2000 75 43 + xyzwh 15.36 270.88 2000 75 43 note 133381 "Knowledge specific to this external output and driver" - xyzwh 564 338 2000 129 57 + xyzwh 536.04 322.88 2000 138 58 relationcanvas 129157 relation_ref 212485 // <dependency> decenter_begin 726 - from ref 128005 z 2006 stereotype "<<when-in-use>>" xyz 229 73 3000 to point 227 82 - line 132485 z 2006 to point 295 82 + from ref 128005 z 2006 stereotype "<<when-in-use>>" xyz 230 72 3000 to point 227.72 82.12 + line 132485 z 2006 to point 294.96 83.12 line 132357 z 2006 to ref 129029 no_role_a no_role_b no_multiplicity_a no_multiplicity_b end relationcanvas 129669 relation_ref 212613 // <directional composition> geometry HV - from ref 129029 z 2006 stereotype "<<for_each_channel>>" xyz 70 155 3000 to point 52 153 + from ref 129029 z 2006 stereotype "<<for_each_channel>>" xyz 65 158 3000 to point 51 155 line 130821 z 2006 to ref 129541 no_role_a no_role_b no_multiplicity_a no_multiplicity_b @@ -67,7 +67,7 @@ end line 130053 ---+ decenter_end 876 from ref 129029 z 2006 to ref 128005 relationcanvas 130181 relation_ref 212741 // <unidirectional association> - from ref 129541 z 2006 stereotype "<<smart-Handle>>" xyz 92 206 3000 to ref 129925 + from ref 129541 z 2006 stereotype "<<smart-Handle>>" xyz 188 210 3000 to ref 129925 no_role_a no_role_b no_multiplicity_a no_multiplicity_b end @@ -99,13 +99,13 @@ end relationcanvas 131845 relation_ref 213637 // <dependency> geometry HV decenter_end 197 - from ref 130309 z 2001 to point 264 438 + from ref 130309 z 2001 to point 252 433 line 132101 z 2001 to ref 131205 no_role_a no_role_b no_multiplicity_a no_multiplicity_b end relationcanvas 132741 relation_ref 213765 // <unidirectional association> - from ref 130949 z 2001 to point 574 423 + from ref 130949 z 2001 to point 524.68 414 line 133253 z 2001 to ref 132613 no_role_a no_role_b no_multiplicity_a no_multiplicity_b diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session index eff2c4d90..0d5cb5160 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -5,13 +5,13 @@ diagrams objectdiagram_ref 138885 // ModelAssetRelations 730 488 100 4 0 0 classdiagram_ref 143877 // Player Entities - 663 648 100 4 0 0 + 663 654 100 4 0 0 objectdiagram_ref 144005 // Play Process Structure 562 424 100 4 0 0 sequencediagram_ref 145157 // output data exchange 586 416 100 4 0 0 active classdiagram_ref 151685 // Player Output - 723 654 100 4 0 0 + 643 590 92 4 0 0 end show_stereotypes selected diff --git a/uml/lumiera/lumiera.prj b/uml/lumiera/lumiera.prj index b8b727236..341a05212 100644 --- a/uml/lumiera/lumiera.prj +++ b/uml/lumiera/lumiera.prj @@ -1,6 +1,6 @@ format 74 "lumiera" - revision 73 + revision 74 modified_by 5 "hiv" cpp_root_dir "../../src/" diff --git a/wiki/renderengine.html b/wiki/renderengine.html index a3d7da184..9b27ae55f 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3362,7 +3362,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) </pre> </div> -<div title="OutputManagement" modifier="Ichthyostega" modified="201111040000" created="201007090155" tags="Model Rendering Player spec draft" changecount="38"> +<div title="OutputManagement" modifier="Ichthyostega" modified="201112222226" created="201007090155" tags="Model Rendering Player spec draft" changecount="39"> <pre>//writing down some thoughts// * ruled out the system outputs as OutputDesignation. @@ -3397,6 +3397,12 @@ The relation between the central OutputDirector and the peripheral OutputManager 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{open question 11/11: is it possible to retrieve a slot from another peripheral node?}}} + +!!!output modes +Most output connections and drivers embody some kind of //operation mode:// Display is characterised by resolution and colour depth, sound by number of channels and sampling rate, amongst others. There might be a mismatch with the output expectations represented by [[output designations|OutputDesignation]] within the model. Nontheless we limit those actual operation modes strictly to the OutputManager realm. They should not leak out into the model within the session. +In practice, this decision might turn out to be rather rigid, but some additional mechanisms allow for more flexibility +* when [[connecting|ViewerPlayConnection]] timeline to viewer and output, stream type conversions may be added automatically or manually +* since resolution of an output designation into an OutputSlot is initiated by querying an output manager, this query might include additional constraints, which //some// (not all) concrete output implementations might evaluate to provide an more suitably configured output slot variant. </pre> </div> <div title="OutputManager" modifier="Ichthyostega" modified="201106212317" created="201106122359" tags="Player Model def" changecount="7"> @@ -3416,7 +3422,7 @@ Note that an OutputSlot acts as a unit for registration and also for allocating 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) </pre> </div> -<div title="OutputMapping" modifier="Ichthyostega" modified="201106180044" created="201011080238" tags="Model spec draft" changecount="29"> +<div title="OutputMapping" modifier="Ichthyostega" modified="201112222050" created="201011080238" tags="Model spec draft" changecount="32"> <pre>An output mapping serves to //resolve//&nbsp; [[output designations|OutputDesignation]]. !Mapping situations @@ -3428,7 +3434,7 @@ The //registration//&nbsp; of an output slot installs a functor or associati :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 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 +:Thus, in this case we need a backwards resolution at the lower end of the output network, to connect to the model port as selected through the viewer's 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 @@ -3443,12 +3449,12 @@ 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 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. +Thus the mapping is a copyable value object, using an 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. </pre> </div> -<div title="OutputSlot" modifier="Ichthyostega" modified="201112200358" created="201106162339" tags="def Concepts Player spec img" changecount="49"> +<div title="OutputSlot" modifier="Ichthyostega" modified="201112222235" created="201106162339" tags="def Concepts Player spec img" changecount="51"> <pre>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 (video in GUI window, video fullscreen, sound, Jack, rendering to file) can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement) @@ -3468,6 +3474,9 @@ Besides the sink handles, allocation of an output slot defines some timing const The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is assumed to be 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. +!!!output modes +some concrete output connections and drivers embody a specific operation mode (e.g. sample rate or number of channels). The decision and setup of these operational configuration is initiated together with the [[resolution|OutputMapping]] of an OutputDesignation within the OutputManager, finally leading to an output slot (reference), which can be assumed to be suitably configured, before the client allocates this slot for active use. Moreover, an individual output sink (corresponding to a single channel) may just remain unused -- until there is an {{{emit()}}} call and successful data handover, this channel will just feature silence or remain black. (More flexible system, e.g. Jack, allow to generate an arbitrary number of output pins -- Lumiera will support this by allowing to set up additional output slots and attach this information to the current session &rarr; SessionConfigurationAttachment) + !!!Lifecycle and storage The concrete OutputSlot implementation is owned and managed by the facility actually providing the output possibility in question. For example, the GUI provides viewer widgets, while some sound output backend provides sound ports. The associated OutputSlot implementation object is required to stay alive as long as it's registered with some OutputManager. It needs to be de-registered explicitly prior to destruction -- and this deregistration may block until all clients using this slot did terminate. Beyond that, an output slot implementation is expected to handle all kinds of failures gracefully -- preferably just emitting a signal (callback functor). {{red{TODO 7/11: Deregistration is an unsolved problem....}}} @@ -5368,6 +5377,9 @@ The Session object is a singleton &mdash; actually it is a »~PImpl«-Facade &rarr; see [[relation of timeline, sequences and objects|TimelineSequences]] </pre> </div> +<div title="SessionConfigurationAttachment" modifier="Ichthyostega" created="201112222242" tags="SessionLogic draft" changecount="2"> +<pre>While the core of the persistent session state corresponds just to the HighLevelModel, there is additionaly attached state, annotations and specific bindings, which allow to connect the session model to the local application configuration on each system. A typical example would be the actual output channels, connections and drivers to use on a specific system. In a Studio setup, these setup and wiring might be quite complex, it may be specific to just a single project, and the user might want to work on the same project on different systems. This explains why we can't just embody these configuration information right into the actual model.</pre> +</div> <div title="SessionContentsQuery" modifier="Ichthyostega" modified="201006190547" created="201006190526" tags="SessionLogic spec draft" changecount="6"> <pre>Querying and retrieving objects within the session model is always bound to a [[scope|PlacementScope]]. When using the //dedicated API,// this scope is immediately defined by the object used to issue the query, like e.g. when searching the contents of a track. But when using the //generic API,// this scope is rather implicit, because in this case a (stateful) QueryFocus object is used to invoke the queries. Somewhat in-between, the top-level session API itself exposes dedicated query functions working on the whole-session scope (model root). Based on the PlacementIndex, the treelike scope structure can be explored efficiently; each Placement attached to the session knows its parent scope. But any additional filtering currently is implemented on top of this basic scope exploration, which obviously may degenerate when searching large scopes and models. Filtering may happen implicitly; all scope queries are parametrised to a specific kind of MObject, while the PlacementIndex deals with all kinds of {{{Placement<MObject>}}} uniformly. Thus, more specifically typed queries automatically have to apply a type filter based on the RTTI of the discovered placements. The plan is later to add specialised sub-indices and corresponding specific query functions to speed up the most frequently used kinds of queries. From 451b0abec523e1f305531cee9917c2326026708b Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 23 Dec 2011 03:55:01 +0100 Subject: [PATCH 315/496] spelling and typos --- src/backend/media-access-facade.hpp | 4 +- src/gui/notification-interface-proxy.hpp | 2 +- src/gui/notification-service.cpp | 2 +- src/gui/notification-service.hpp | 2 +- .../widgets/timeline/timeline-view-window.hpp | 2 +- src/include/guinotification-facade.h | 4 +- src/lib/diagnostic-context.hpp | 20 ++--- src/lib/meta/typelist-util.hpp | 2 +- src/proc/asset/buildinstruct.hpp | 2 +- src/proc/asset/struct-factory-impl.hpp | 2 +- src/proc/control/command-def.hpp | 2 +- src/proc/engine/nodewiring-def.hpp | 6 +- .../components/backend/media-access-mock.cpp | 2 +- wiki/renderengine.html | 78 +++++++++---------- 14 files changed, 66 insertions(+), 64 deletions(-) diff --git a/src/backend/media-access-facade.hpp b/src/backend/media-access-facade.hpp index 3e51d96fa..9604360af 100644 --- a/src/backend/media-access-facade.hpp +++ b/src/backend/media-access-facade.hpp @@ -83,8 +83,8 @@ namespace backend { /** - * Descriptor holding the global informations, - * needed for further handling this media within Lumiera. + * Descriptor holding the global information record + * required for further handling this kind of media within Lumiera. */ struct MediaDesc { diff --git a/src/gui/notification-interface-proxy.hpp b/src/gui/notification-interface-proxy.hpp index 79500e8df..2269513e5 100644 --- a/src/gui/notification-interface-proxy.hpp +++ b/src/gui/notification-interface-proxy.hpp @@ -1,5 +1,5 @@ /* - Notification(Proxy) - public service allowing to push informations into the GUI + Notification(Proxy) - public service allowing to push information into the GUI Copyright (C) Lumiera.org 2008, Hermann Vosseler <Ichthyostega@web.de> diff --git a/src/gui/notification-service.cpp b/src/gui/notification-service.cpp index 0cfd3c826..a27d5a641 100644 --- a/src/gui/notification-service.cpp +++ b/src/gui/notification-service.cpp @@ -1,5 +1,5 @@ /* - NotificationService - public service allowing to push informations into the GUI + NotificationService - public service allowing to push information into the GUI Copyright (C) Lumiera.org 2008, Hermann Vosseler <Ichthyostega@web.de> diff --git a/src/gui/notification-service.hpp b/src/gui/notification-service.hpp index d7e2e218b..427ec045d 100644 --- a/src/gui/notification-service.hpp +++ b/src/gui/notification-service.hpp @@ -1,5 +1,5 @@ /* - NOTIFICATION-SERVICE.hpp - public service allowing to push informations into the GUI + NOTIFICATION-SERVICE.hpp - public service allowing to push information into the GUI Copyright (C) Lumiera.org 2008, Hermann Vosseler <Ichthyostega@web.de> diff --git a/src/gui/widgets/timeline/timeline-view-window.hpp b/src/gui/widgets/timeline/timeline-view-window.hpp index f4c6fa558..1b4b19888 100644 --- a/src/gui/widgets/timeline/timeline-view-window.hpp +++ b/src/gui/widgets/timeline/timeline-view-window.hpp @@ -78,7 +78,7 @@ public: * from the session / current timeline to be displayed. It doesn't * make sense to display raw time values here, as each timeline might * turn out to have a different origin; this is the result of resolving - * a placement, and only the session has the necessary informations... + * a placement, and only the session has the necessary information... */ Offset get_time_offset() const; diff --git a/src/include/guinotification-facade.h b/src/include/guinotification-facade.h index 8c0834d2c..7e10bcbd0 100644 --- a/src/include/guinotification-facade.h +++ b/src/include/guinotification-facade.h @@ -1,5 +1,5 @@ /* - GUINOTIFICATION-FACADE.h - access point for pushing informations into the GUI + GUINOTIFICATION-FACADE.h - access point for pushing information into the GUI Copyright (C) Lumiera.org 2008, Hermann Vosseler <Ichthyostega@web.de> @@ -24,7 +24,7 @@ ** 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 ** usable by scripts or the two lower layers. The main purpose of these services - ** is to push informations and status updates into the GUI. + ** is to push status updates and similar information up into the GUI. ** ** @see notification-service.hpp implementation ** @see gui::GuiFacade diff --git a/src/lib/diagnostic-context.hpp b/src/lib/diagnostic-context.hpp index 28f97c5c1..1fc658e69 100644 --- a/src/lib/diagnostic-context.hpp +++ b/src/lib/diagnostic-context.hpp @@ -21,15 +21,15 @@ */ /** @file diagnostic-context.hpp - ** Facility for collecting diagnostic informations explicitly. + ** Facility for collecting diagnostic context information explicitly. ** Unlike a trace logging run, this facility is intended to be fed explicitly with ** diagnostic information describing the currently ongoing operation in a semantic - ** high-level manner. The rationale is to pinpoint \em those informations which aren't - ** obvious when just looking at a callstack with the debugger. Instances of the class - ** DiagnosticContext should be placed explicitly as automatic (stack) variable - ** into selected relevant scopes; these "information frames" can be accessed - ** from an enclosed scope as a per-thread stack. DiagnosticContext provides - ** an controlled environment for adding diagnostic code on demand; typically + ** high-level manner. The rationale is to pinpoint \em those pieces of information, + ** which aren't obvious when just looking at a callstack with the debugger. + ** Instances of the class DiagnosticContext should be placed explicitly as automatic + ** (stack) variable into selected relevant scopes; these "information frames" could + ** be accessed from an enclosed scope as a per-thread stack. DiagnosticContext + ** provides an controlled environment for adding diagnostic code on demand; typically ** to be configured such as to resolve into an empty class for release builds. ** ** As of 2/10, this is an experimental feature in evaluation. To start with, @@ -60,15 +60,17 @@ namespace lib { #ifdef NOBUG_MODE_ALPHA ////////////////////////TODO don't we need the handle in BETA builds for resource logging? /** - * Diagnostic data frame to collect specific informations concerning a scope. + * Diagnostic data frame to collect specific information concerning a scope. * To be placed explicitly as an automatic (stack) variable. Provides a controlled * environment for hooking up diagnostic code. Within each thread, a stack of * such information frames concerning nested scopes is maintained automatically. * It can be accessed via static API. - * @warning never store this into global data structures. + * @warning relies on thread-local access; never use this within global data structures. * @note as of 2/10 used for housing the NoBug resource tracker handle * in conjunction with object monitor based locking * @see sync.hpp + * @todo 12/2011 : not in use currently, but the concept seems useful + * and should be generalised. //////////////////////////////////////TICKET #687 */ class DiagnosticContext : boost::noncopyable diff --git a/src/lib/meta/typelist-util.hpp b/src/lib/meta/typelist-util.hpp index ff8086cb3..812aa2487 100644 --- a/src/lib/meta/typelist-util.hpp +++ b/src/lib/meta/typelist-util.hpp @@ -24,7 +24,7 @@ /** @file typelist-util.hpp ** Metaprogramming: simple helpers for working with lists-of-types. ** This header provides some very basic "meta functions" for extracting - ** some informations from a list-of-types. In Lumiera, we use template + ** pieces of information 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. diff --git a/src/proc/asset/buildinstruct.hpp b/src/proc/asset/buildinstruct.hpp index 1408037c2..73ee094f2 100644 --- a/src/proc/asset/buildinstruct.hpp +++ b/src/proc/asset/buildinstruct.hpp @@ -22,7 +22,7 @@ /** @file buildinstruct.hpp - ** Helper classes used by asset::ProcPatt to represent the processing informations. + ** Helper classes used by asset::ProcPatt to represent the processing information. ** Consider these classes as owned by ProcPatt. Non-inline functions go to procpatt.cpp ** */ diff --git a/src/proc/asset/struct-factory-impl.hpp b/src/proc/asset/struct-factory-impl.hpp index 392cbfed3..b25a94ef1 100644 --- a/src/proc/asset/struct-factory-impl.hpp +++ b/src/proc/asset/struct-factory-impl.hpp @@ -24,7 +24,7 @@ /** @file struct-factory-impl.hpp ** Private implementation details of creating various structural assets. ** Details how to fabricate specific kinds of structural assets, based - ** on capability informations encoded into a query (goal). + ** on capability information encoded into a query (goal). ** @internal to be used by struct.cpp ** ** @see ConfigQuery diff --git a/src/proc/control/command-def.hpp b/src/proc/control/command-def.hpp index 292c8f27c..5f0ac67b4 100644 --- a/src/proc/control/command-def.hpp +++ b/src/proc/control/command-def.hpp @@ -31,7 +31,7 @@ ** ** For actually providing these operations, the client is expected to call the ** definition functions in a chained manner ("fluent interface"). When finally all the - ** required informations are available, a \em prototype object is built and registered + ** required information is available, a \em prototype object is built and registered ** with the CommandRegistry. From this point on, the corresponding Command (frontend object) ** can be accessed directly by ID (and only relying on the header command.hpp). ** diff --git a/src/proc/engine/nodewiring-def.hpp b/src/proc/engine/nodewiring-def.hpp index 20477aa00..d9827b72e 100644 --- a/src/proc/engine/nodewiring-def.hpp +++ b/src/proc/engine/nodewiring-def.hpp @@ -25,9 +25,9 @@ ** During the Builder run, the render nodes network is wired up starting from the ** source (generating) nodes up to the exit nodes. As the wiring is implemented through ** a const engine::WiringDescriptor, when a new node gets fabricated, all of the connections - ** to its predecessors need to be completely settled; similarly, any informations pertaining + ** to its predecessors need to be completely settled; similarly, any information pertaining ** the desired operation mode of this node need to be available. Thus we use this temporary - ** information record to assemble all these informations. + ** information record to assemble all these pieces of information. ** ** @see proc::engine::NodeFactory ** @see nodewiring.hpp @@ -109,7 +109,7 @@ namespace engine { /** A default WiringSituation doesn't specify any connections. * It can be used as-is for building a source node, or augmented - * with connection informations later on. + * with connection information later on. */ WiringSituation() : flags_(0) diff --git a/tests/components/backend/media-access-mock.cpp b/tests/components/backend/media-access-mock.cpp index 837b27af4..4a50542b9 100644 --- a/tests/components/backend/media-access-mock.cpp +++ b/tests/components/backend/media-access-mock.cpp @@ -23,7 +23,7 @@ /** @file mediaacessmock.cpp ** Mock implementation of the Interface normally used to query media file - ** informations from the data backend. The Mock implementation instead holds + ** information from the data backend. The Mock implementation instead holds ** a map of fixed response which will be delivered when querying some magic ** filenames. ** diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 848bfeafd..28a88c08a 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -747,10 +747,10 @@ Even if the low-level memory manager(s) may use raw storage, we require that the &rarr; see MemoryManagement </pre> </div> -<div title="Asset" modifier="Ichthyostega" modified="201012292209" created="200708100337" tags="def classes img" changecount="20"> +<div title="Asset" modifier="Ichthyostega" modified="201112222243" created="200708100337" tags="def classes img" changecount="21"> <pre>Asset management is a subsystem on its own. Assets are "things" that can be loaded into a session, like Media, Clips, Effects, Transitions. It is the "bookkeeping view", while the Objects in the Session relate to the "manipulation and process view". Some Assets can be //loaded// and a collection of Assets is saved with each Session. Besides, there is a collection of basic Assets always available by default. -The Assets are important reference points holding the information needed to access external resources. For example, an Clip asset can reference a Media asset, which in turn holds the external filename from which to get the media stream. For Effects, the situation is similar. Assets thus serve two quite distinct purposes. One is to load, list, group search and browse them, and to provide an entry point to create new or get at existing MObject in the Session, while the other purpose is to provide attribute and property informations to the inner parts of the engine, while at the same time isolating and decoupling them from environmental details. +The Assets are important reference points holding the information needed to access external resources. For example, an Clip asset can reference a Media asset, which in turn holds the external filename from which to get the media stream. For Effects, the situation is similar. Assets thus serve two quite distinct purposes. One is to load, list, group search and browse them, and to provide an entry point to create new or get at existing MObject in the Session, while the other purpose is to provide attribute and property information to the inner parts of the engine, while at the same time isolating and decoupling them from environmental details. We can distinguish several different Kinds of Assets, each one with specific properties. While all these Kinds of Assets implement the basic Asset interface, they in turn are the __key abstractions__ of the asset management view. Mostly, their interfaces will be used directly, because they are quite different in behaviour. Thus it is common to see asset related operations being templated on the Asset Kind. &rarr; see also [[Creating and registering Assets|AssetCreation]] @@ -759,7 +759,7 @@ We can distinguish several different Kinds of Assets, each one with specific pro !Media Asset Some piece of Media Data accessible at some external Location and able to be processed by Lumiera. A Media File on Harddisk can be considered as the most basic form of Media Asset, with some important derived flavours, like a Placeholder for a currently unavailable Source, or Media available in different Resolutions or Formats. * __outward interface operations__ include querying properties, creating an Clip MObject, controlling processing policy (low res proxy placeholders, interlacing and other generic pre- and postprocessing) -* __inward interface operations__ include querying filename, codec, offset and any other informations necessary for creating a source render node, getting additional processing policy decisions (handling of interlacing, aspect ratio). +* __inward interface operations__ include querying filename, codec, offset and any other information necessary for creating a source render node, getting additional processing policy decisions (handling of interlacing, aspect ratio). &rarr; MediaAsset !Processing Asset @@ -1405,8 +1405,8 @@ Commands are //defined// using a [[fluent API|http://en.wikipedia.org/wiki/Fluen &rarr; see CommandUsage </pre> </div> -<div title="CommandDefinition" modifier="Ichthyostega" modified="200907220310" created="200906140124" tags="SessionLogic spec draft decision design" changecount="10"> -<pre>Commands can be identified and accessed //by name// &mdash; consequently there needs to be an internal command registry, including a link to the actual implementing function, thus allowing to re-establish the connection between command and implementing functions when de-serialising a persisted command. To create a command, we need to provide the following informations +<div title="CommandDefinition" modifier="Ichthyostega" modified="201112222243" created="200906140124" tags="SessionLogic spec draft decision design" changecount="11"> +<pre>Commands can be identified and accessed //by name// &mdash; consequently there needs to be an internal command registry, including a link to the actual implementing function, thus allowing to re-establish the connection between command and implementing functions when de-serialising a persisted command. To create a command, we need to provide the following information * operation function actually implementing the command * function to [[undo|UndoManager]] the effect of the command * function to capture state to be used by UNDO. @@ -1472,11 +1472,11 @@ To support this handling scheme, some infrastructure is in place: * performing the actual execution is delegated to a handling pattern object, accessed by name. </pre> </div> -<div title="CommandLifecycle" modifier="Ichthyostega" modified="200910090043" created="200907210135" tags="SessionLogic spec draft design img" changecount="19"> +<div title="CommandLifecycle" modifier="Ichthyostega" modified="201112222244" created="200907210135" tags="SessionLogic spec draft design img" changecount="20"> <pre>[<img[Structure of Commands|uml/fig135173.png]] While generally the command framework was designed to be flexible and allow a lot of different use cases, execution paths and to serve various goals, there is an ''intended lifecycle'' &mdash; commands are expected to go through several distinct states. -The handling of a command starts out with a ''command ID'' provided by the client code. Command ~IDs are unique (human readable) identifiers and should be organised in a hierarchical fashion. When provided with an ID, the CommandRegistry tries to fetch an existing command definition. In case this fails, we enter the [[command definition stage|CommandDefinition]], which includes specifying functions to implement the operation, state capturing and UNDO. When all these informations are available, the entity is called a ''command definition''. Conceptually, it is comparable to a //class// or //meta object.// +The handling of a command starts out with a ''command ID'' provided by the client code. Command ~IDs are unique (human readable) identifiers and should be organised in a hierarchical fashion. When provided with an ID, the CommandRegistry tries to fetch an existing command definition. In case this fails, we enter the [[command definition stage|CommandDefinition]], which includes specifying functions to implement the operation, state capturing and UNDO. When all of this information is available, the entity is called a ''command definition''. Conceptually, it is comparable to a //class// or //meta object.// By ''binding'' to specific operation arguments, the definition is //armed up//&nbsp; and becomes a real ''command''. This is similar to creating an instance from a class. Behind the scenes, storage is allocated to hold the argument values and any state captured to create the ability to UNDO the command's effect later on. @@ -1782,7 +1782,7 @@ Cinelerra uses this term in a related manner but with a somewhat shifted focus: In this usage, the EDL in most cases will be almost synonymous to &raquo;the session&laquo;, just the latter emphasizes more the state aspect. While the Lumiera project started out using the same terminology, later on, when support for multiple "containers" within the session and for [[meta-clips|VirtualClip]] was determined to be of much importance, the new term &raquo;[[Sequence]]&laquo; was preferred. </pre> </div> -<div title="EditingOperations" modifier="Ichthyostega" modified="200710111508" created="200709251610" tags="design decision" changecount="5"> +<div title="EditingOperations" modifier="Ichthyostega" modified="201112222244" created="200709251610" tags="design decision" changecount="6"> <pre>These are the tools provided to any client of the Proc layer for handling and manipulating the entities in the Session. When defining such operations, //the goal should be to arrive at some uniformity in the way things are done.// Ideally, when writing client code, one should be able to guess how to achieve some desired result. !guiding principle @@ -1806,7 +1806,7 @@ This has some obvious and some subtle consequences. Of course, manipulating //ta Consequently, sometimes there needs to be done sort of a ''read-copy-update'', i.e. self-replacement by copy followed by manipulation of the new copy, while ongoing processes use the unaltered original object until they receive some sort of reset. !!undo -Basically, each elementary operation has to record the informations necessary to be undone. It does so by registering a Memento with some central UndoManager facility. This Memento object contains a functor pre-bound with the needed parameter values. (Besides, the UndoManager is free to implement a second level of security by taking independent state snapshots). +Basically, each elementary operation has to record the information necessary to be undone. It does so by registering a Memento with some central UndoManager facility. This Memento object contains a functor pre-bound with the needed parameter values. (Besides, the UndoManager is free to implement a second level of security by taking independent state snapshots). {{red{to be defined in more detail later...}}} </pre> </div> @@ -2790,8 +2790,8 @@ The ''pull up'' sequence performs basic initialisation of the session facilities The ''shut down'' sequence does exactly that: halt processing and rendering, disconnect an existing session, if any, get back into initial state. It doesn't care for unwinding session contents, which is assumed to happen automatically when references to previous session contents go out of scope. </pre> </div> -<div title="LoadingMedia" modifier="Ichthyostega" modified="200806030203" created="200709220005" tags="design spec" changecount="4"> -<pre>Opening and accessing media files on disk poses several problems, most of which belong to the domain of Lumiera's data backend. Here, we focus on the questions related to making media data available to the session and the render engine. Each media will be represented by an MediaAsset object, which indeed could be a compound object (in case of MultichannelMedia). Building this asset object thus includes getting informations from the real file on disk. For delegating this to the backend, we use the following query interface: +<div title="LoadingMedia" modifier="Ichthyostega" modified="201112222244" created="200709220005" tags="design spec" changecount="5"> +<pre>Opening and accessing media files on disk poses several problems, most of which belong to the domain of Lumiera's data backend. Here, we focus on the questions related to making media data available to the session and the render engine. Each media will be represented by an MediaAsset object, which indeed could be a compound object (in case of MultichannelMedia). Building this asset object thus includes getting information from the real file on disk. For delegating this to the backend, we use the following query interface: * {{{queryFile(char* name)}}} requests accessing the file and yields some (opaque) handle when successful. * {{{queryChannel(fHandle, int)}}} will then be issued in sequence with ascending index numbers, until it returns {{{NULL}}}. * the returned struct (pointer) will provide the following information: @@ -3271,15 +3271,15 @@ But because I know the opinions on this topc are varying (users tend to be delig My proposed aproach is to treat OpenGL as a separate video raw data type, requiring separete and specialized [[Processing Nodes|ProcNode]] for all calculations. Thus the Builder could connect OpenGL nodes if it is possible to cover the render path in whole or partially or maybe even just for preview. </pre> </div> -<div title="OperationPoint" modifier="Ichthyostega" modified="201011071905" created="200805270334" tags="def impl Builder" changecount="9"> +<div title="OperationPoint" modifier="Ichthyostega" modified="201112222245" created="200805270334" tags="def impl Builder" changecount="10"> <pre>A low-level abstraction within the [[Builder]] &mdash; it serves to encapsulate the details of making multi-channel connections between the render nodes: In some cases, a node can handle N channels internally, while in other cases we need to replicate the node N times and wire each channel individually. As it stands, the OperationPoint marks the ''borderline between high-level and low-level model'': it is invoked in terms of ~MObjects and other entities of the high-level view, but internally it manages to create ProcNode and similar entities of the low-level model. The operation point is provided by the current BuilderMould and used by the [[processing pattern|ProcPatt]] executing within this mould and conducting the current build step. The operation point's interface allows //to abstract//&nbsp; these details, as well as to //gain additional control//&nbsp; if necessary (e.g. addressing only one of the channels). The most prominent build instruction used within the processing patterns (which is the instruction {{{"attach"}}}) relies on the aforementioned //approach of abstracted handling,// letting the operation point determine automatically how to make the connection. -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. +This is possible because the operation point has been provided (by the mould) with information 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. </pre> </div> -<div title="OutputDesignation" modifier="Ichthyostega" modified="201106162303" created="201006220126" tags="Model draft design discuss" changecount="59"> +<div title="OutputDesignation" modifier="Ichthyostega" modified="201112222245" created="201006220126" tags="Model draft design discuss" changecount="60"> <pre>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. @@ -3291,7 +3291,7 @@ Wiring the flexible interconnections between the [[pipes|Pipe]] should take into ** as Timeline is just a façade, BindingMO has to expose something which can be referred for attaching effects (to global pipes) ** when used as VirtualClip, there is somehow a channel configuration, either as asset, or exposed by the BindingMO * Placements always resolve at least two dimensions: time and output. The latter means that a [[Placement]] can figure out to where to connect -* the resolution ability of Placements could help to overcome the problems in conjunction with a VirtualClip: missing output destination informations could be inherited down.... +* the resolution ability of Placements could help to overcome the problems in conjunction with a VirtualClip: missing output destination information could be inherited down.... * expanding on the basic concept of a Placement in N-dimensional configuration space, this //figuring out// would denote the ability to resolve the final output destination * this resolution to a final destination is explicitly context dependent. We engage into quite some complexities to make this happen (&rarr; BindingScopeProblem) * [[processing patterns|ProcPatt]] are used for creating nodes on the source network of a clip, and similarly for fader, overlay and mixing into a summation pipe @@ -4185,7 +4185,7 @@ Placements have //value semantics,// i.e. we don't stress the identity of a plac </pre> </div> -<div title="PlacementIndex" modifier="Ichthyostega" modified="201001112323" created="200905090053" tags="SessionLogic spec impl" changecount="22"> +<div title="PlacementIndex" modifier="Ichthyostega" modified="201112222246" created="200905090053" tags="SessionLogic spec impl" changecount="24"> <pre>An implementation facility used to keep track of individual Placements and their relations. Especially, the [[Session]] maintains such an index, allowing to use the (opaque) PlacementRef tags for referring to a specific "instance" of an MObject, //placed// in a unique way into the current session. And, moreover, this index allows for one placement referring to another placement, so to implement a //relative// placement mode. Because there is an index behind the scenes, it is possible to actually access such a referral in the reverse direction, which is necessary for implementing the desired placement behaviour (if an object instance used as anchor is moved, all objects placed relatively to it have to move accordingly, which necessitates finding those other objects). @@ -4199,13 +4199,13 @@ Using a ''flat hashtable'' allows to access a Placement denoted by ID in O(1). T * allowing to create a path "up" from this scope, which is used for resolving any queries * (maybe/planned) relations to other placements -Alternatively, we could try to use a ''structure based index'', thereby avoiding the mentioned description record by folding any of the contained informations into the surrounding data structure: +Alternatively, we could try to use a ''structure based index'', thereby avoiding the mentioned description record by folding any of the contained information into the surrounding data structure: * the scope would be obvious from the index, resp. from the path used to resolve this index -* any other informations, especially the relations, would be folded into the placement +* any other information, especially the relations, would be folded into the placement * this way, the "index" could be reduced to being the session data structure itself. //does a placement need to know it's own ID?//&nbsp; Obviously, there needs to be a way to find out the ID for a given placement, especially in the following situations: -* for most of the operations above, when querying additional informations from index for a given placement +* for most of the operations above, when querying additional information from index for a given placement * to create a PlacementRef (this is a variant of the "shared ptr from this"-problem) On second sight, this problem turns out to be more involved, because either we have to keep a second index table for the reverse lookup (memory address -> ID), or have to tie the placement by a back-link when adding it to the index/session data structure, or (alternatively) it forces us to store a copy of the ID //within// the placement itself. The last possibility seems to create the least impact; but implementing it this way effectively gears the implementation towards a hashtable based approach. @@ -4425,7 +4425,7 @@ This is the core service provided by the player subsystem. The purpose is to cre :any details of this processing remain opaque for the clients; even the player subsystem just accesses the EngineFaçade </pre> </div> -<div title="Player" modifier="Ichthyostega" modified="201106131443" created="201012181700" tags="def overview" changecount="10"> +<div title="Player" modifier="Ichthyostega" modified="201112222246" created="201012181700" tags="def overview" changecount="14"> <pre>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: @@ -4434,7 +4434,7 @@ Yet it adresses some central concerns: :all playback and render processes are on equal footing, handled in a similar way. ;integration :the player cares for the necessary integration with the other subsystems -:it consults the OutputManagement, retrieves the necessary informations from the [[Session]] and coordinates [[Backend]] calls. +:it consults the OutputManagement, retrieves the necessary information from the [[Session]] and coordinates the forwarding of [[Backend]] calls. ;time quantisation :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. @@ -4574,7 +4574,7 @@ The system is ''open'' inasmuch every part mirrors the structure of correspondin &rarr; see RenderProcess </pre> </div> -<div title="ProcPatt" modifier="Ichthyostega" modified="200805260329" created="200709212315" tags="def design" changecount="9"> +<div title="ProcPatt" modifier="Ichthyostega" modified="201112222247" created="200709212315" tags="def design" changecount="10"> <pre>This special type of [[structural Asset|StructAsset]] represents information how to build some part of the render engine's processing nodes network. Processing patterns can be thought of as a blueprint or micro program for construction. Most notably, they are used for creating nodes reading, decoding and delivering source media material to the render network, and they are used for building the output connection via faders, summation or overlay nodes to the global pipes (busses). Each [[media Asset|MediaAsset]] has associated processing patterns describing the codecs and other transformations needed to get at the media data of this asset. (and because media assets are typically compound objects, the referred ~ProcPatt will be compound too). Similarily, for each stream kind, we can retrieve a processing pattern for making output connections. Obviously, the possibilities opened by using processing patterns go far beyond. Technically, a processing pattern is a list of building instructions, which will be //executed// by the [[Builder]] on the render node network under construction. This implies the possibility to define further instruction kinds when needed in future; at the moment the relevant sorts of instructions are @@ -4588,7 +4588,7 @@ Like all [[structural assets|StructAsset]], ~ProcPatt employs a special naming s The basic working set of processing patterns can be expected to be just there (hard wired or default configuration, mechanism for creating an sensible fallback). Besides, the idea is that new processing patterns can be added via rules in the session and then referred to by other rules controlling the build process. Any processing pattern is assembled by adding individual build instructions, or by including another (nested) processing pattern. !!retrieving a suitable Processing Pattern -For a given situation, the necessary ProcPatt can be retrieved by issuing a [[configuration query|ConfigQuery]]. This query should include the needed capabilities in predicate form (technically this query is a Prolog goal), but it can leave out informations by just requesting "the default" &rarr; see DefaultsManagement +For a given situation, the necessary ProcPatt can be retrieved by issuing a [[configuration query|ConfigQuery]]. This query should include the needed capabilities in predicate form (technically this query is a Prolog goal), but it can leave out some pieces of information by just requesting "the default" &rarr; see DefaultsManagement !!how does this actually work? Any processing pattern needs the help of a passive holder tool suited for a specific [[building situation|BuilderPrimitives]]; we call these holder tools [[building moulds|BuilderMould]]. Depending on the situation, the mould has been armed up by the builder with the involved objects to be connected and extended. So, just by issuing the //location ID// defined within the individual build instruction (in most cases simply {{{"current"}}}), the processing pattern can retrieve the actual render object to use for building from the mould it is executed in. @@ -5004,10 +5004,10 @@ config.macros.rssFeedUpdate = { //}}} </pre> </div> -<div title="RelationClipAsset" modifier="Ichthyostega" modified="200710212327" created="200710191541" tags="design decision img" changecount="7"> +<div title="RelationClipAsset" modifier="Ichthyostega" modified="201112222247" created="200710191541" tags="design decision img" changecount="8"> <pre>What is the Role of the asset::Clip and how exactly are Assets and (Clip)-MObjects related? -First of all: ~MObjects are the dynamic/editing/manipulation view, while Assets are the static/bookkeeping/searching/information view of the same entities. Thus, the asset::Clip contains the general configuration, the ref to the media and descriptive properties, while all parameters being "manipulated" belong to the session::Clip (MObject). Besides that, the practical purpose of asset::Clip is that you can save and remember some selection as a Clip (Asset), maybe even attach some informations or markup to it, and later be able to (re)create a editable representation in the Session (the GUI could implement this by allowing to drag from the asset::Clip GUI representation to the timeline window) +First of all: ~MObjects are the dynamic/editing/manipulation view, while Assets are the static/bookkeeping/searching/information view of the same entities. Thus, the asset::Clip contains the general configuration, the ref to the media and descriptive properties, while all parameters being "manipulated" belong to the session::Clip (MObject). Besides that, the practical purpose of asset::Clip is that you can save and remember some selection as a Clip (Asset), maybe even attach some information or markup to it, and later be able to (re)create a editable representation in the Session (the GUI could implement this by allowing to drag from the asset::Clip GUI representation to the timeline window) !!dependencies The session::Clip (frequently called "clip-MO", i.e. the MObject) //depends on the Asset.// It can't exist without the Asset, because the Asset is needed for rendering. The other direction is different: the asset::Clip knows that there is a dependant clip-MO, there could be //at most one// such clip-MO depending on the Asset, but the Asset can exist without the clip-MO (it gives the possibility to re-create the clip-MO). @@ -5283,8 +5283,8 @@ A sequence is always tied to a root-placed track, it can't exist without such. W &rarr; see detailed [[discussion of dependent objects' behaviour|ModelDependencies]] </pre> </div> -<div title="Session" modifier="Ichthyostega" modified="200911071800" created="200712100525" tags="def SessionLogic" changecount="8"> -<pre>The Session contains all informations, state and objects to be edited by the User. From a users view, the Session is synonymous to the //current Project//. It can be [[saved and loaded|SessionLifecycle]]. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one (or several) collections within the Session, which we call [[Sequence]]. +<div title="Session" modifier="Ichthyostega" modified="201112222247" created="200712100525" tags="def SessionLogic" changecount="9"> +<pre>The Session contains all information, state and objects to be edited by the User. From a users view, the Session is synonymous to the //current Project//. It can be [[saved and loaded|SessionLifecycle]]. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one (or several) collections within the Session, which we call [[Sequence]]. &rarr; [[Session design overview|SessionOverview]] !Session structure @@ -5413,7 +5413,7 @@ Currently, I'm planning to modify MObjectRef to return only a const ref to the u <<< </pre> </div> -<div title="SessionLifecycle" modifier="Ichthyostega" modified="201010232310" created="200911070329" tags="SessionLogic spec" changecount="26"> +<div title="SessionLifecycle" modifier="Ichthyostega" modified="201112222248" created="200911070329" tags="SessionLogic spec" changecount="27"> <pre>The current [[Session]] is the root of any state found within Proc-Layer. Thus, events defining the session's lifecycle influence and synchronise the cooperative behaviour of the entities within the model, the ProcDispatcher, [[Fixture]] and any facility below. * when ''starting'', on first access an empty session is created, which puts any related facility into a defined initial state. * when ''closing'' the session, any dependent facilities are disabled, disconnected, halted or closed @@ -5426,7 +5426,7 @@ The SessionManager is responsible for conducting the session lifecycle. Accessib Beyond that, client code usually doesn't interact much with the lifecycle, which mostly is a pattern of events to happen in a well-defined sequence. So the //implementation// of the session management operations has to comply to this lifecycle, and does so by relying on a self-contained implementation service, the LifecycleAdvisor. But (contrary to an application framework) the lifecycle of the Lumiera session is rather fixed, the only possibility for configuration or extension being the [[lifecycle hooks|LifecycleEvent]], where other parts of the system (and even plug-ins) may install some callback methods. !Synchronising access to session's implementation facilities -Some other parts and subsystems within the ~Proc-Layer need specialised access to implementation facilities within the session. Informations about some conditions and configurations might be retrieved through [[querrying the session|Query]], and especially default configurations for many objects are [[bound to the session|DefaultsImplementation]]. The [[discovery of session contents|SessionStructureQuery]] relies on an [[index facility|PlacementIndex]] embedded within the session implementation. Moreover, some "properties" of the [[media objects|MObject]] are actually due to the respective object being [[placed|Placement]] in some way into the session; consequently, there might be an dependency on the actual [[location as visible to the placement|PlacementScope]], which in turn is constituted by [[querying the index|QueryFocus]]. +Some other parts and subsystems within the ~Proc-Layer need specialised access to implementation facilities within the session. Information about some conditions and configurations might be retrieved through [[querrying the session|Query]], and especially default configurations for many objects are [[bound to the session|DefaultsImplementation]]. The [[discovery of session contents|SessionStructureQuery]] relies on an [[index facility|PlacementIndex]] embedded within the session implementation. Moreover, some "properties" of the [[media objects|MObject]] are actually due to the respective object being [[placed|Placement]] in some way into the session; consequently, there might be an dependency on the actual [[location as visible to the placement|PlacementScope]], which in turn is constituted by [[querying the index|QueryFocus]]. Each of these facilities relies on a separate access point to session services, corresponding to distinct service interfaces. But &mdash; on the implementation side &mdash; all these services are provided by a (compound) SessionServices implementation object. This approach allows to switch the actual implementation of all these services simply by swapping the ~PImpl maintained by the session manager. A new implementation level service can thus be added to the ~SessionImpl just by hooking it into the ~SessionServices compound object. But note, this mechanism as such is ''not thread safe'', unless the //implementation// of the invoked functions is synchronised in some way to prevent switching to a new session implementation while another thread is still executing session implementation code. @@ -5458,8 +5458,8 @@ As detailed above, {{{Session::current}}} exposes the management / lifecycle API {{red{none of the above is implemented as of 11/09}}} </pre> </div> -<div title="SessionLogic" modifier="Ichthyostega" modified="201003210012" created="200904242110" tags="overview" changecount="21"> -<pre>The Session contains all informations, state and objects to be edited by the User (&rarr;[[def|Session]]). +<div title="SessionLogic" modifier="Ichthyostega" modified="201112222248" created="200904242110" tags="overview" changecount="22"> +<pre>The Session contains all information, state and objects to be edited by the User (&rarr;[[def|Session]]). As such, the SessionInterface is the main entrance point to Proc-Layer functionality, both for the primary EditingOperations and for playback/rendering processes. Proc-Layer state is rooted within the session and guided by the [[session's lifecycle events|SessionLifecycle]]. Implementation facilities within the Proc-Layer may access a somewhat richer [[session service API|SessionServices]]. @@ -5494,14 +5494,14 @@ The answer is simple: the one who needs to know about their existence. Because b Interestingly, there seems to be an alternative answer to this question. We could locate the setup and definition of all commands into a central administrative facility. Everyone in need of a command then ought to know the name and retrieve this command. Sounds like bureaucracy. </pre> </div> -<div title="SessionOverview" modifier="Ichthyostega" modified="200911071816" created="200709272105" tags="design img" changecount="38"> +<div title="SessionOverview" modifier="Ichthyostega" modified="201112222248" created="200709272105" tags="design img" changecount="40"> <pre><<< {{red{WARNING: Naming was discussed (11/08) and decided to be changed....}}} * the term [[EDL]] was phased out in favour of ''Sequence'' * [[Session]] is largely synonymous to ''Project'' * there seems to be a new entity called [[Timeline]] which holds the global Pipes <<< -The [[Session]] (sometimes also called //Project// ) contains all informations and objects to be edited by the User. Any state within the Proc-Layer is directly or indirectly rooted in the session. It can be saved and loaded. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one or multiple collections within the Session, which we call [[sequence(s)|Sequence]]. Moreover, the sesion contains references to all the Media files used, and it contains various default or user defined configuration, all being represented as [[Asset]]. At any given time, there is //only one current session// opened within the application. The [[lifecycle events|SessionLifecycle]] of the session define the lifecycle of ~Proc-Layer as a whole. +The [[Session]] (sometimes also called //Project// ) contains all information and objects to be edited by the User. Any state within the Proc-Layer is directly or indirectly rooted in the session. It can be saved and loaded. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one or multiple collections within the Session, which we call [[sequence(s)|Sequence]]. Moreover, the sesion contains references to all the Media files used, and it contains various default or user defined configuration, all being represented as [[Asset]]. At any given time, there is //only one current session// opened within the application. The [[lifecycle events|SessionLifecycle]] of the session define the lifecycle of ~Proc-Layer as a whole. The Session is close to what is visible in the GUI. From a user's perspective, you'll find a [[Timeline]]-like structure, containing an [[Sequence]], where various Media Objects are arranged and placed. The available building blocks and the rules how they can be combined together form Lumiera's [[high-level data model|HighLevelModel]]. Basically, besides the [[media objects|MObjects]] there are data connections and all processing is organized around processing chains or [[pipes|Pipe]], which can be either global (in the Session) or local (in real or virtual clips). @@ -7144,7 +7144,7 @@ Thus the possibly mutalble time entities get an {{{accept(time::Mutation&)}} 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. </pre> </div> -<div title="TimeQuant" modifier="Ichthyostega" modified="201106080000" created="201012181753" tags="Concepts Player spec img discuss draft" changecount="52"> +<div title="TimeQuant" modifier="Ichthyostega" modified="201112222249" created="201012181753" tags="Concepts Player spec img discuss draft" changecount="53"> <pre>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. @@ -7174,7 +7174,7 @@ Based on this time::Mutation design, we provide a specialised element for dealin :This implies the presence of a //running synchronisation process,// with the authority to adjust the time base; :contrast this to the internal time, which is static and unconnected -- ;quantised time -:The ''act of quantisation'' transforms a continuous property into a ''discrete'' structure. Prominent examples can be found in the domain of micro physics and with digital information processing. In a broader sense, any measurement or //quantification// also encompasses a quantisation. Regarding time and time measurement, quantisation means alignment to a predefined ''time grid''. Quantisation necessarily is an //irreversible process// -- possible additional informations get discarded. +:The ''act of quantisation'' transforms a continuous property into a ''discrete'' structure. Prominent examples can be found in the domain of micro physics and with digital information processing. In a broader sense, any measurement or //quantification// also encompasses a quantisation. Regarding time and time measurement, quantisation means alignment to a predefined ''time grid''. Quantisation necessarily is an //irreversible process// -- possible additional information is discarded. :Note that quantisation introduces an ''time origin'' and a ''reference scale'' ;frame count :within the context of film and media editing, the specification of a ''frame number'' is an especially important instance of quantisation. @@ -7445,7 +7445,7 @@ Using transitions is a very basic task and thus needs viable support by the GUI. Because of this experience, ichthyo wants to support a more general case of transitions, which have N output connections, behave similar to their "simple" counterpart, but leave out the mixing step. As a plus, such transitions can be inserted at the source ports of N clips or between any intermediary or final output pipes as well. Any transition processor capable of handling this situation should provide some flag, in order to decide if he can be placed in such a manner. (wichin the builder, encountering a inconsistently placed transition is just an [[building error|BuildingError]]) </pre> </div> -<div title="TypedID" modifier="Ichthyostega" modified="201012292351" created="201003200157" tags="Model Types Rules design draft" changecount="33"> +<div title="TypedID" modifier="Ichthyostega" modified="201112222250" created="201003200157" tags="Model Types Rules design draft" changecount="34"> <pre>//drafted service as of 4/10 &mdash; &rarr;[[implementation plans|TypedLookup]]// A registration service to associate object identities, symbolic identifiers and types. @@ -7471,7 +7471,7 @@ A registration service backed by an index table can be used to //translate//& !!!{{red{WIP}}}Analysis and discussion Still some contradictions &mdash; and rather seems helpful, not so much necessary. -We //already have an registration service,// both for Assets (AssetManager) and for Placements (PlacementIndex). These facilities maintain not only a raw ID &harr; object association, but also structuring informations, albeit bound to more specific circumstances (the system of placement scopes, and the asset category). The lookup uniqueID &rArr; object could be implemented by sequentially querying this small number of central registration facilities. Thus, still lacking is a ''system of sub index tables''. +We //already have an registration service,// both for Assets (AssetManager) and for Placements (PlacementIndex). These facilities maintain not only a raw ID &harr; object association, but also structuring information, albeit bound to more specific circumstances (the system of placement scopes, and the asset category). The lookup uniqueID &rArr; object could be implemented by sequentially querying this small number of central registration facilities. Thus, still lacking is a ''system of sub index tables''. As mentioned above, an ID &harr; type association plays a crucial role when it comes to implementing any kind of rules based configuration. It would allow to bridge from our session objects to rules and resolution working entirely symbolic. (&rarr; [[more|ConfigQueryIntegration]]). But, as of 3/2010 this is a //planned feature and not required to get the initial pipeline working.// Thus, according to the YAGNI principle, we shouldn't engage into any implementation details right now and just create the extension points. @@ -7492,7 +7492,7 @@ Just an ''registration scheme'' should be implemented right now, working complet see [[implementation planning|TypedLookup]] </pre> </div> -<div title="TypedLookup" modifier="Ichthyostega" modified="201012292351" created="201004031607" tags="Rules Types spec impl draft" changecount="22"> +<div title="TypedLookup" modifier="Ichthyostega" modified="201112222250" created="201004031607" tags="Rules Types spec impl draft" changecount="23"> <pre>TypedID is a registration service to associate object identities, symbolic identifiers and types. It acts as frontend to the TypedLookup service within Proc-Layer, at the implementation level. While TypedID works within a strictly typed context, this type information is translated into an internal index on passing over to the implementation, which manages a set of tables containing base entries with an combined symbolic+hash ID, plus an opaque buffer. Thus, the strictly typed context is required to re-access the stored data. But the type information wasn't erased entirely, so this typed context can be re-gained with the help of an internal type index. All of this is considered implementation detail and may be subject to change without further notice; any access is assumed to happen through the TypedID frontend. Besides, there are two more specialised frontends. !Front-ends @@ -7503,7 +7503,7 @@ see [[implementation planning|TypedLookup]] !Tables and index The Table consists of several registration groups, each of which contains a hashtable and deals with one specific type. Groups are created on demand, but there is initially one group holding the internal type index (translation table). There may be even sub-groups, usable to create clusterings within one group. -__Individual entries__ are comprised of a EntryID as key (actually a ~BareEntryID, without the typing) and a payload, which //doesn't store data,// but informations necessary to ''lookup and access'' the registered object. Obviously, this information is type specific, and thus the ~TypedLookup implementation can't know how to deal with it directly. Rather, we store a ''functor'' in the payload of the type index group. This functor is directly linked to the TypeHandler, i.e. any type wanting to be a primary type within Lumiera, so as to be directly usable within the ConfigRules, needs to provide a suitable functor implementation through its ~TypeHandler. These functors are then invoked by the ~TypedID frontend, when it comes to re-accessing a registered entity by ID +__Individual entries__ are comprised of a EntryID as key (actually a ~BareEntryID, without the typing) and a payload, which //doesn't store data,// but information necessary to ''lookup and access'' the registered object. Obviously, this information is type specific, and thus the ~TypedLookup implementation can't know how to deal with it directly. Rather, we store a ''functor'' in the payload of the type index group. This functor is directly linked to the TypeHandler, i.e. any type wanting to be a primary type within Lumiera, so as to be directly usable within the ConfigRules, needs to provide a suitable functor implementation through its ~TypeHandler. These functors are then invoked by the ~TypedID frontend, when it comes to re-accessing a registered entity by ID !link for automatic registration An entity can be linked with the TypedLookup system to be registered and deregistered automatically. This is achieved by mixing in the {{{TypedID::Link}}}. On creation, this will set up an EntryID for this individual instance and cause creation of an empty entry within the suitable registration group. As a side-effect, uniqueness of any symbolic-ID within one group (type) is enforced. Obviously, the dtor of this registration Link cares for de-registration automatically. Be forwarned though, by creating an unique identity, this mechanism will interfere with copying and cloning of the registered entity. From 2bdf06829acbb732f3dee953a29cdfadc798ccd0 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 24 Dec 2011 05:33:56 +0100 Subject: [PATCH 316/496] make thread(pool) less chatty --- src/backend/threads.c | 12 ++++++------ src/backend/threads.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/backend/threads.c b/src/backend/threads.c index 3ed36be84..7f580f712 100644 --- a/src/backend/threads.c +++ b/src/backend/threads.c @@ -37,7 +37,7 @@ #include <errno.h> /** - * @file + * @file threads.c * */ @@ -93,17 +93,17 @@ thread_loop (void* thread) do { lumiera_threadpool_release_thread(t); LUMIERA_CONDITION_WAIT (t->state != LUMIERA_THREADSTATE_IDLE); - INFO (threads, "Thread awaken with state %s", lumiera_threadstate_names[t->state]); + TRACE (threads, "Thread awaken with state %s", lumiera_threadstate_names[t->state]); // NULL function means: no work to do - INFO (threads, "function %p", t->function); + TRACE (threads, "function %p", t->function); if (t->function) t->function (t->arguments); TRACE (threads, "function done"); if (t->kind & LUMIERA_THREAD_JOINABLE) { - INFO (threads, "Thread zombified"); + TRACE (threads, "Thread zombified"); /* move error state to data the other thread will it pick up from there */ t->arguments = (void*)lumiera_error (); t->state = LUMIERA_THREADSTATE_ZOMBIE; @@ -111,14 +111,14 @@ thread_loop (void* thread) LUMIERA_CONDITION_SIGNAL; LUMIERA_CONDITION_WAIT (t->state == LUMIERA_THREADSTATE_JOINED); - INFO (threads, "Thread joined"); + TRACE (threads, "Thread joined"); } } while (t->state != LUMIERA_THREADSTATE_SHUTDOWN); // SHUTDOWN state - INFO (threads, "Thread Shutdown"); + TRACE (threads, "Thread done."); } //////////////////////////////////////////////////////////////////////TICKET #844 no error must be pending here, else do app shutdown return 0; diff --git a/src/backend/threads.h b/src/backend/threads.h index 6e755be48..d539a3944 100644 --- a/src/backend/threads.h +++ b/src/backend/threads.h @@ -37,7 +37,7 @@ /** - * @file + * @file threads.h * */ From 87f7a8f6e8f4bb10c13a5a2366a66b3bfdeef01d Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 23 Dec 2011 05:00:27 +0100 Subject: [PATCH 317/496] unit test to cover thread-local helper --- src/backend/thread-wrapper.hpp | 2 +- src/lib/thread-local.hpp | 2 +- tests/40components.tests | 5 ++ tests/lib/thread-local-test.cpp | 112 ++++++++++++++++++++++++++++++++ wiki/renderengine.html | 5 +- 5 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 tests/lib/thread-local-test.cpp diff --git a/src/backend/thread-wrapper.hpp b/src/backend/thread-wrapper.hpp index 699bfd11d..6ce1aaa02 100644 --- a/src/backend/thread-wrapper.hpp +++ b/src/backend/thread-wrapper.hpp @@ -188,7 +188,7 @@ namespace backend { /** @note by design there is no possibility to find out - * just based on the thread handle, if the thread is alive. + * just based on the thread handle if some thread is alive. * We define our own accounting here based on the internals * of the thread wrapper. This will break down, if you mix * uses of the C++ wrapper with the raw C functions. */ diff --git a/src/lib/thread-local.hpp b/src/lib/thread-local.hpp index cfb854ae8..613807278 100644 --- a/src/lib/thread-local.hpp +++ b/src/lib/thread-local.hpp @@ -95,7 +95,7 @@ namespace lib { private: TAR* - accessChecked() + accessChecked() const { TAR *p(get()); if (!p) diff --git a/tests/40components.tests b/tests/40components.tests index 9baa7e6b5..fc23dc900 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -648,6 +648,11 @@ return: 0 END +TEST "Wrapper thread-local pointers" ThreadLocal_test <<END +return: 0 +END + + TEST "Wait/Notify on Object Monitor" SyncWaiting_test <<END return: 0 END diff --git a/tests/lib/thread-local-test.cpp b/tests/lib/thread-local-test.cpp new file mode 100644 index 000000000..562ec64e1 --- /dev/null +++ b/tests/lib/thread-local-test.cpp @@ -0,0 +1,112 @@ +/* + ThreadLocal(Test) - verify wrapper for using thread-local data + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" + +#include "backend/thread-wrapper.hpp" +#include "lib/thread-local.hpp" + +#include <cstdlib> + +using test::Test; +using backend::ThreadJoinable; +using std::rand; + + +namespace lib { +namespace test{ + + namespace { // private test data... + + const uint NUM_THREADS = 100; + const uint MAX_RAND = 5*1000*1000; + + + ThreadLocalPtr<uint> privateValue; + + + + + struct TestThread + : ThreadJoinable + { + TestThread() + : ThreadJoinable("test Thread-local storage" + ,verifyThreadLocal) + { } + + + /** the actual test operation running in a separate thread */ + static void + verifyThreadLocal() + { + uint secret (1 + rand() % MAX_RAND); + privateValue.set (&secret); + + usleep (secret); // sleep for a random period + + if (secret != *privateValue) + throw error::Fatal ("thread-local value access broken"); + } + }; + + } // (End) test data.... + + + + + + + + + + + /************************************************************************** + * @test use a wrapper to simplify handling of thread-local data. + * Create some threads, each referring to another piece of data + * through the "same" wrapper instance. + * + * @see backend::Thread + * @see lib::ThreadLocal + */ + class ThreadLocal_test : public Test + { + + virtual void + run (Arg) + { + TestThread testcase[NUM_THREADS] SIDEEFFECT; + + for (uint i=0; i < NUM_THREADS; ++i) + CHECK (testcase[i].join().isValid() ); + } + }; + + + + /** Register this test class... */ + LAUNCHER (ThreadLocal_test, "function common"); + + + +}} // namespace backend::test diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 28a88c08a..aeefa01c1 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3425,7 +3425,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. </pre> </div> -<div title="OutputSlot" modifier="Ichthyostega" modified="201111042355" created="201106162339" tags="def Concepts Player spec" changecount="40"> +<div title="OutputSlot" modifier="Ichthyostega" modified="201112230113" created="201106162339" tags="def Concepts Player spec img" changecount="52"> <pre>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 (video in GUI window, video fullscreen, sound, Jack, rendering to file) can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement) @@ -3434,7 +3434,8 @@ 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. (in special cases, some 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 calculating process feeds its results into those handles. Size and other characteristics of the data frames are 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. +[>img[Outputslot implementation structures|uml/fig151685.png]] +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 are 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. Based on this information, 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. 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 From 5498ace9fc6024fed011408b5b9f3772e9f9f43d Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 24 Dec 2011 03:47:50 +0100 Subject: [PATCH 318/496] WIP define a unit-test for diagnostic context stack --- src/lib/nobug-resource-handle-context.hpp | 158 +++++++++++++++++ tests/lib/diagnostic-context-test.cpp | 199 ++++++++++++++++++++++ tests/lib/thread-local-test.cpp | 5 +- 3 files changed, 360 insertions(+), 2 deletions(-) create mode 100644 src/lib/nobug-resource-handle-context.hpp create mode 100644 tests/lib/diagnostic-context-test.cpp diff --git a/src/lib/nobug-resource-handle-context.hpp b/src/lib/nobug-resource-handle-context.hpp new file mode 100644 index 000000000..1fc658e69 --- /dev/null +++ b/src/lib/nobug-resource-handle-context.hpp @@ -0,0 +1,158 @@ +/* + DIAGNOSTIC-CONTEXT.hpp - thread local stack for explicitly collecting diagnostic context info + + Copyright (C) Lumiera.org + 2010, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file diagnostic-context.hpp + ** Facility for collecting diagnostic context information explicitly. + ** Unlike a trace logging run, this facility is intended to be fed explicitly with + ** diagnostic information describing the currently ongoing operation in a semantic + ** high-level manner. The rationale is to pinpoint \em those pieces of information, + ** which aren't obvious when just looking at a callstack with the debugger. + ** Instances of the class DiagnosticContext should be placed explicitly as automatic + ** (stack) variable into selected relevant scopes; these "information frames" could + ** be accessed from an enclosed scope as a per-thread stack. DiagnosticContext + ** provides an controlled environment for adding diagnostic code on demand; typically + ** to be configured such as to resolve into an empty class for release builds. + ** + ** As of 2/10, this is an experimental feature in evaluation. To start with, + ** I'll use it to solve the problem of providing a NoBug resource tracker handle + ** without tangling the object monitor code (sync.hpp) with low level, NoBug related + ** implementation details. + ** + ** @todo add the actual diagnostic content + **/ + + +#ifndef LIB_DIAGNOSTIC_CONTEXT_H +#define LIB_DIAGNOSTIC_CONTEXT_H + + +#include "lib/error.hpp" +#include "lib/thread-local.hpp" + +#include <boost/noncopyable.hpp> +#include <nobug.h> + + + +namespace lib { + + + +#ifdef NOBUG_MODE_ALPHA ////////////////////////TODO don't we need the handle in BETA builds for resource logging? + + /** + * Diagnostic data frame to collect specific information concerning a scope. + * To be placed explicitly as an automatic (stack) variable. Provides a controlled + * environment for hooking up diagnostic code. Within each thread, a stack of + * such information frames concerning nested scopes is maintained automatically. + * It can be accessed via static API. + * @warning relies on thread-local access; never use this within global data structures. + * @note as of 2/10 used for housing the NoBug resource tracker handle + * in conjunction with object monitor based locking + * @see sync.hpp + * @todo 12/2011 : not in use currently, but the concept seems useful + * and should be generalised. //////////////////////////////////////TICKET #687 + */ + class DiagnosticContext + : boost::noncopyable + { + typedef nobug_resource_user* Handle; + typedef ThreadLocalPtr<DiagnosticContext> ThreadLocalAccess; + + Handle handle_; + DiagnosticContext * const prev_; + + /** embedded thread local pointer + * to the innermost context encountered */ + static ThreadLocalAccess& + current() + { + static ThreadLocalAccess accessPoint; + return accessPoint; + } + + + public: + DiagnosticContext() + : handle_(0) + , prev_(current().get()) + { + current().set (this); + } + + ~DiagnosticContext() + { + ASSERT (this == current().get()); + current().set (prev_); + } + + + operator Handle* () ///< payload: NoBug resource tracker user handle + { + return &handle_; + } + + /** accessing the innermost diagnostic context created */ + static DiagnosticContext& + access () + { + DiagnosticContext* innermost = current().get(); + if (!innermost) + throw lumiera::error::Logic ("Accessing Diagnostic context out of order; " + "an instance should have been created in " + "an enclosing scope"); + return *innermost; + } + }; + + +#else /* not NOBUG_ALPHA */ + + + /** + * Disabled placeholder for the Diagnostic context, not used in release builds. + */ + class DiagnosticContext + : boost::noncopyable + { + + + public: + + operator Handle* () ///< payload: NoBug resource tracker user handle + { + return 0; + } + + /** accessing the innermost diagnostic context created */ + static DiagnosticContext& + access () + { + UNIMPLEMENTED ("how to disable DiagnosticContext with minimum overhead"); + } + }; +#endif /* NOBUG_ALPHA? */ + + + +} // namespace lib +#endif diff --git a/tests/lib/diagnostic-context-test.cpp b/tests/lib/diagnostic-context-test.cpp new file mode 100644 index 000000000..a4784319d --- /dev/null +++ b/tests/lib/diagnostic-context-test.cpp @@ -0,0 +1,199 @@ +/* + DiagrnsticContext(Test) - verify thread local stack for collecting diagnostics + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" + +#include "backend/thread-wrapper.hpp" +#include "lib/diagnostic-context.hpp" + +#include <cstdlib> +#include <vector> + + + +namespace lib { +namespace test{ + + namespace error = lumiera::error; + + namespace { // private test setup... + + const uint NUM_THREADS = 100; + const uint MAX_RAND = 1000*1000; + + } // (End) test setup.... + + using test::Test; + using backend::ThreadJoinable; + using error::LUMIERA_ERROR_LOGIC; + using std::rand; + + + /** + * Subject of this test: + * a thread-local stack of int values + */ + typedef DiagnosticContext<uint> Marker; + + + + + + + /********************************************************************************** + * @test verify a diagnostic facility to collect and access contextual information. + * DiagnosticContext frames are placed into automatic storage (as local + * variable within some function scope). Based on thread-local storage, + * the next reachable frame can be accessed from anywhere within + * the callstack. This feature is useful for collecting + * information regarding features cross-cutting + * the usual dependency hierarchy. + * + * @see lib::DiagnosticContext + * @see lib::ThreadLocal + * @see backend::Thread + */ + class DiagrnsticContext_test : public Test + { + + virtual void + run (Arg) + { + verify_simpleProperties(); + verify_heavilyParallelUsage(); + } + + + + /** @test create nested scopes and place a + * DiagnosticContext frame into each. + * Access the next reachable frame. + */ + void + verify_simpleAccess() + { + VERIFY_ERROR (LOGIC, Marker.access()); + + Marker zero(0); + CHECK (0 == zero); + CHECK (0 == Marker.access()); + + { // nested scope + CHECK (0 == Marker.access()); + + Marker one(1); + CHECK (1 == Marker.access()); + CHECK (1 == one); + CHECK (0 == zero); + + { // nested scope + CHECK (1 == Marker.access()); + + Marker two(2); + CHECK (2 == Marker.access()); + CHECK (2 == two); + CHECK (1 == one); + CHECK (0 == zero); + } + CHECK (1 == Marker.access()); + } + CHECK (0 == Marker.access()); + } + + + + + /** @test verify the thread local property of ContextStack. + * Create several threads, each of which processes a sequence + * of numbers starting from a random initial value down to one. + * Whenever a simple division by two leads to an odd value, this + * value is placed onto the ContextStack. At recursion end, we + * take a snapshot of the full ContextStack and then unwind. + * Thus the captured numbers must from a decreasing sequence + * of odd values. + */ + void + verify_heavilyParallelUsage() + { + TestThread testcase[NUM_THREADS] SIDEEFFECT; + + for (uint i=0; i < NUM_THREADS; ++i) + CHECK (testcase[i].join().isValid() ); + } + + + struct TestThread + : ThreadJoinable + { + TestThread() + : ThreadJoinable("test diagnostic context stack" + ,verifyDiagnosticStack) + { } + }; + + + typedef std::vector<uint> VecI; + + /** the actual test operation running in a separate thread */ + static void + verifyDiagnosticStack() + { + uint seed (1 + rand() % MAX_RAND); + + VecI sequence = descend (seed); + + uint prev = seed; + for (uint i=0; i < sequence.size(); ++i) + { + uint val = sequence[i]; + if (! (isOdd(val) && val < prev)) + throw error::Fatal ("thread-local diagnostic stack"); + prev = val; + } + } + + static VecI + descend (uint current) + { + if (current < 2) + return Marker.extractStack(); + + if (isOdd(current)) + { + Marker remember(current); + return descend (current+1); + } + else + return descend (current/2); + } + }; + + + + /** Register this test class... */ + LAUNCHER (DiagrnsticContext_test, "function common"); + + + +}} // namespace backend::test diff --git a/tests/lib/thread-local-test.cpp b/tests/lib/thread-local-test.cpp index 562ec64e1..45465045d 100644 --- a/tests/lib/thread-local-test.cpp +++ b/tests/lib/thread-local-test.cpp @@ -36,12 +36,13 @@ using std::rand; namespace lib { namespace test{ - namespace { // private test data... + namespace { // private test setup... const uint NUM_THREADS = 100; const uint MAX_RAND = 5*1000*1000; + /** Subject of the test */ ThreadLocalPtr<uint> privateValue; @@ -70,7 +71,7 @@ namespace test{ } }; - } // (End) test data.... + } // (End) test setup.... From 24a8d6a926b89935a455c0a75c2167ca300abd7b Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 24 Dec 2011 05:38:54 +0100 Subject: [PATCH 319/496] generalised diagnostic context passes unit test --- src/lib/diagnostic-context.hpp | 63 ++++++------- src/lib/nobug-resource-handle-context.hpp | 106 ++++++---------------- src/proc/control/command-closure.hpp | 12 +-- tests/40components.tests | 5 + tests/lib/diagnostic-context-test.cpp | 46 ++++++---- 5 files changed, 93 insertions(+), 139 deletions(-) diff --git a/src/lib/diagnostic-context.hpp b/src/lib/diagnostic-context.hpp index 1fc658e69..221f59fad 100644 --- a/src/lib/diagnostic-context.hpp +++ b/src/lib/diagnostic-context.hpp @@ -57,7 +57,6 @@ namespace lib { -#ifdef NOBUG_MODE_ALPHA ////////////////////////TODO don't we need the handle in BETA builds for resource logging? /** * Diagnostic data frame to collect specific information concerning a scope. @@ -66,19 +65,15 @@ namespace lib { * such information frames concerning nested scopes is maintained automatically. * It can be accessed via static API. * @warning relies on thread-local access; never use this within global data structures. - * @note as of 2/10 used for housing the NoBug resource tracker handle - * in conjunction with object monitor based locking - * @see sync.hpp - * @todo 12/2011 : not in use currently, but the concept seems useful - * and should be generalised. //////////////////////////////////////TICKET #687 */ + template<typename VAL> class DiagnosticContext : boost::noncopyable { - typedef nobug_resource_user* Handle; typedef ThreadLocalPtr<DiagnosticContext> ThreadLocalAccess; + typedef std::vector<VAL> ValSequence; - Handle handle_; + const VAL value_; DiagnosticContext * const prev_; /** embedded thread local pointer @@ -92,8 +87,8 @@ namespace lib { public: - DiagnosticContext() - : handle_(0) + DiagnosticContext(VAL const& value_to_log = VAL()) + : value_(value_to_log) , prev_(current().get()) { current().set (this); @@ -106,9 +101,9 @@ namespace lib { } - operator Handle* () ///< payload: NoBug resource tracker user handle + operator VAL const&() ///< access the payload by conversion { - return &handle_; + return value_; } /** accessing the innermost diagnostic context created */ @@ -118,39 +113,33 @@ namespace lib { DiagnosticContext* innermost = current().get(); if (!innermost) throw lumiera::error::Logic ("Accessing Diagnostic context out of order; " - "an instance should have been created in " + "an instance should have been created within " "an enclosing scope"); return *innermost; } - }; - - -#else /* not NOBUG_ALPHA */ - - - /** - * Disabled placeholder for the Diagnostic context, not used in release builds. - */ - class DiagnosticContext - : boost::noncopyable - { - public: - - operator Handle* () ///< payload: NoBug resource tracker user handle + /** snapshot of the current stack of diagnostic frames + * @return vector with all the payload values currently + * on the thread-local diagnostic stack. Might + * be empty. Values start with frame next to + * the current scope and end with outermost. + */ + static ValSequence + extractStack() { - return 0; - } - - /** accessing the innermost diagnostic context created */ - static DiagnosticContext& - access () - { - UNIMPLEMENTED ("how to disable DiagnosticContext with minimum overhead"); + ValSequence loggedValues; + DiagnosticContext* next = current().get(); + while (next) + { + loggedValues.push_back (*next); + next = next->prev_; + } + return loggedValues; } }; -#endif /* NOBUG_ALPHA? */ + + diff --git a/src/lib/nobug-resource-handle-context.hpp b/src/lib/nobug-resource-handle-context.hpp index 1fc658e69..ac4ccca52 100644 --- a/src/lib/nobug-resource-handle-context.hpp +++ b/src/lib/nobug-resource-handle-context.hpp @@ -1,5 +1,5 @@ /* - DIAGNOSTIC-CONTEXT.hpp - thread local stack for explicitly collecting diagnostic context info + NOBUG-RESOURCE-HANDLE-CONTEXT.hpp - thread local stack to manage NoBug resource handles Copyright (C) Lumiera.org 2010, Hermann Vosseler <Ichthyostega@web.de> @@ -20,29 +20,28 @@ */ -/** @file diagnostic-context.hpp - ** Facility for collecting diagnostic context information explicitly. - ** Unlike a trace logging run, this facility is intended to be fed explicitly with - ** diagnostic information describing the currently ongoing operation in a semantic - ** high-level manner. The rationale is to pinpoint \em those pieces of information, - ** which aren't obvious when just looking at a callstack with the debugger. - ** Instances of the class DiagnosticContext should be placed explicitly as automatic - ** (stack) variable into selected relevant scopes; these "information frames" could - ** be accessed from an enclosed scope as a per-thread stack. DiagnosticContext - ** provides an controlled environment for adding diagnostic code on demand; typically - ** to be configured such as to resolve into an empty class for release builds. +/** @file nobug-resource-handle-context.hpp + ** Thread-local stack of NoBug resource handles. + ** This helper allows to access the resource handle in the nearest enclosing scope. + ** The motivation for this approach was to avoid passing the resource handle over + ** several intermediary function calls when using a scoped variable to control + ** object monitor locking. Within this usage context, the necessity of passing + ** a NoBug resource handle seems to be a cross-cutting concern, and not directly + ** related to the core concern (controlling a mutex). ** - ** As of 2/10, this is an experimental feature in evaluation. To start with, - ** I'll use it to solve the problem of providing a NoBug resource tracker handle - ** without tangling the object monitor code (sync.hpp) with low level, NoBug related - ** implementation details. + ** @remarks as of 8/2011, this feature is not used anymore. In 12/2011, the concept + ** of a diagnostic context stack was generalised. At that point, this header was + ** created as an off-spin, now based on the generalised feature, to document this + ** usage possibility, which might be required again at some point in the future. + ** + ** @see lib::DiagnosticContext + ** @see diagnostic-context-test.hpp ** - ** @todo add the actual diagnostic content **/ -#ifndef LIB_DIAGNOSTIC_CONTEXT_H -#define LIB_DIAGNOSTIC_CONTEXT_H +#ifndef LIB_NOBUG_RESOURCE_HANDLE_CONTEXT_H +#define LIB_NOBUG_RESOURCE_HANDLE_CONTEXT_H #include "lib/error.hpp" @@ -60,68 +59,14 @@ namespace lib { #ifdef NOBUG_MODE_ALPHA ////////////////////////TODO don't we need the handle in BETA builds for resource logging? /** - * Diagnostic data frame to collect specific information concerning a scope. - * To be placed explicitly as an automatic (stack) variable. Provides a controlled - * environment for hooking up diagnostic code. Within each thread, a stack of - * such information frames concerning nested scopes is maintained automatically. - * It can be accessed via static API. + * Diagnostic data frame to hold a NoBug resource handle. + * This way, code in nested function calls may pick up the nearest available handle. * @warning relies on thread-local access; never use this within global data structures. - * @note as of 2/10 used for housing the NoBug resource tracker handle - * in conjunction with object monitor based locking - * @see sync.hpp - * @todo 12/2011 : not in use currently, but the concept seems useful - * and should be generalised. //////////////////////////////////////TICKET #687 */ - class DiagnosticContext - : boost::noncopyable + class NobugResourceHandleContext + : DiagnosticContext<nobug_resource_user*> { - typedef nobug_resource_user* Handle; - typedef ThreadLocalPtr<DiagnosticContext> ThreadLocalAccess; - - Handle handle_; - DiagnosticContext * const prev_; - - /** embedded thread local pointer - * to the innermost context encountered */ - static ThreadLocalAccess& - current() - { - static ThreadLocalAccess accessPoint; - return accessPoint; - } - - public: - DiagnosticContext() - : handle_(0) - , prev_(current().get()) - { - current().set (this); - } - - ~DiagnosticContext() - { - ASSERT (this == current().get()); - current().set (prev_); - } - - - operator Handle* () ///< payload: NoBug resource tracker user handle - { - return &handle_; - } - - /** accessing the innermost diagnostic context created */ - static DiagnosticContext& - access () - { - DiagnosticContext* innermost = current().get(); - if (!innermost) - throw lumiera::error::Logic ("Accessing Diagnostic context out of order; " - "an instance should have been created in " - "an enclosing scope"); - return *innermost; - } }; @@ -131,20 +76,21 @@ namespace lib { /** * Disabled placeholder for the Diagnostic context, not used in release builds. */ - class DiagnosticContext + class NobugResourceHandleContext : boost::noncopyable { + typedef nobug_resource_user* Handle; public: - operator Handle* () ///< payload: NoBug resource tracker user handle + operator Handle () ///< payload: NoBug resource tracker user handle { return 0; } /** accessing the innermost diagnostic context created */ - static DiagnosticContext& + static NobugResourceHandleContext& access () { UNIMPLEMENTED ("how to disable DiagnosticContext with minimum overhead"); diff --git a/src/proc/control/command-closure.hpp b/src/proc/control/command-closure.hpp index a2246125c..b03f88405 100644 --- a/src/proc/control/command-closure.hpp +++ b/src/proc/control/command-closure.hpp @@ -25,10 +25,10 @@ ** A closure enabling self-contained execution of commands within the ProcDispatcher. ** After defining a proc-layer command, at some point the function arguments ** of the contained operation are "closed" by storing concrete argument values. - ** These values will later on be fed to the operation when the command is invoked. + ** These values will be fed later on to the operation when the command is invoked. ** - ** Most of the command machinery accesses this function closure through the interface - ** CmdClosure, while, when defining a command, subclasses typed to the specific + ** Most of the command machinery accesses this function closure through the generic + ** interface CmdClosure, while, when defining a command, subclasses typed to the specific ** function arguments are created. Especially, there is an ArgumentHolder template, ** which is used to define the storage for the concrete arguments. This ArgumentHolder ** internally contains an Closure<SIG> instance (where SIG is the signature of the @@ -43,10 +43,10 @@ ** ** Later on, any command needs to be made ready for execution by binding it to a specific ** execution environment, which especially includes the target objects to be mutated by the - ** command. Effectively, this means "closing" the Mutation (and UNDO) functor(s) with the + ** command. Effectively, this means "closing" the Mutation (and UNDO) functor(s)) with the ** actual function arguments. These arguments are stored embedded within an ArgumentHolder, ** which thereby acts as closure. Besides, the ArgumentHolder also has to accommodate for - ** storage holding the captured UNDO state (memento). Thus, internally the ArgumentHolder + ** storage holding the captured UNDO state (memento). Internally the ArgumentHolder ** has to keep track of the actual types, thus allowing to re-construct the concrete ** function signature when closing the Mutation. ** @@ -214,7 +214,7 @@ namespace control { friend bool compare (ParamAccessor const&, ParamAccessor const&) { - return true;; + return true; } }; diff --git a/tests/40components.tests b/tests/40components.tests index fc23dc900..54862528f 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -653,6 +653,11 @@ return: 0 END +TEST "Thread-local diagnostic context" DiagrnsticContext_test <<END +return: 0 +END + + TEST "Wait/Notify on Object Monitor" SyncWaiting_test <<END return: 0 END diff --git a/tests/lib/diagnostic-context-test.cpp b/tests/lib/diagnostic-context-test.cpp index a4784319d..07f3f43ed 100644 --- a/tests/lib/diagnostic-context-test.cpp +++ b/tests/lib/diagnostic-context-test.cpp @@ -42,9 +42,14 @@ namespace test{ const uint NUM_THREADS = 100; const uint MAX_RAND = 1000*1000; + inline bool + isOdd (uint val) + { + return bool (val % 2); + } + } // (End) test setup.... - using test::Test; using backend::ThreadJoinable; using error::LUMIERA_ERROR_LOGIC; using std::rand; @@ -56,6 +61,7 @@ namespace test{ */ typedef DiagnosticContext<uint> Marker; + typedef std::vector<uint> VecI; @@ -80,7 +86,7 @@ namespace test{ virtual void run (Arg) { - verify_simpleProperties(); + verify_simpleAccess(); verify_heavilyParallelUsage(); } @@ -93,32 +99,41 @@ namespace test{ void verify_simpleAccess() { - VERIFY_ERROR (LOGIC, Marker.access()); + VERIFY_ERROR (LOGIC, Marker::access()); + + VecI loggedValues; Marker zero(0); CHECK (0 == zero); - CHECK (0 == Marker.access()); + CHECK (0 == Marker::access()); { // nested scope - CHECK (0 == Marker.access()); + CHECK (0 == Marker::access()); Marker one(1); - CHECK (1 == Marker.access()); + CHECK (1 == Marker::access()); CHECK (1 == one); CHECK (0 == zero); { // nested scope - CHECK (1 == Marker.access()); + CHECK (1 == Marker::access()); Marker two(2); - CHECK (2 == Marker.access()); + CHECK (2 == Marker::access()); CHECK (2 == two); CHECK (1 == one); CHECK (0 == zero); + + loggedValues = Marker::extractStack(); } - CHECK (1 == Marker.access()); + CHECK (1 == Marker::access()); } - CHECK (0 == Marker.access()); + CHECK (0 == Marker::access()); + + CHECK (3 == loggedValues.size()); + CHECK (2 == loggedValues[0]); + CHECK (1 == loggedValues[1]); + CHECK (0 == loggedValues[2]); } @@ -152,8 +167,6 @@ namespace test{ { } }; - - typedef std::vector<uint> VecI; /** the actual test operation running in a separate thread */ static void @@ -163,11 +176,11 @@ namespace test{ VecI sequence = descend (seed); - uint prev = seed; + uint prev = 0; for (uint i=0; i < sequence.size(); ++i) { uint val = sequence[i]; - if (! (isOdd(val) && val < prev)) + if (! (isOdd(val) && seed >= val && val > prev )) throw error::Fatal ("thread-local diagnostic stack"); prev = val; } @@ -177,8 +190,9 @@ namespace test{ descend (uint current) { if (current < 2) - return Marker.extractStack(); - + return Marker::extractStack(); + + usleep (500); if (isOdd(current)) { Marker remember(current); From 2791339841fb5673c6a9df088dc14e8ceb2ab9a1 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 26 Dec 2011 05:54:56 +0100 Subject: [PATCH 320/496] some practical hints and informations regarding usage of the Boost Libraries Add a page to the 'coding howto' section, listing those boost libraries we're using heavily, with a short characterisation for each. Plus some informations about potential problems --- doc/technical/howto/UsingBoost.txt | 165 +++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 doc/technical/howto/UsingBoost.txt diff --git a/doc/technical/howto/UsingBoost.txt b/doc/technical/howto/UsingBoost.txt new file mode 100644 index 000000000..2d937ca92 --- /dev/null +++ b/doc/technical/howto/UsingBoost.txt @@ -0,0 +1,165 @@ +Using Boost Libraries +===================== + +//Menu: label using boost + +_some arbitrary hints and notes regarding the use of link::http://www.boost.org[Boost Libraries] in Lumiera_ + +Notable Features +---------------- +Some of the Boost Libraries are especially worth mentioning. You should familiarise yourself with +those features, as we're using them heavily throughout our code base. As it stands, the C/C\++ language(s) +are quite lacking and outdated, compared with today's programming standards. To fill some of these gaps +and to compensate for compiler deficiencies, some members of the C\++ committee and generally very +knowledgeable programmers created a set of C\++ libraries generally known as *Boost*. Some of these +especially worthy additions are also proposed for inclusion into the C++ standard library. + +.memory +The `<boost/memory.hpp>` rsp. `<tr1/memory>` libraries define a family of smart-pointers to serve +several needs of basic memory management. In almost all cases, they're superior to using `std::auto_ptr`. + +When carefully combining these nifty templates with the RAII pattern, most concerns for memory +management, clean-up and error handling simply go away. (but please understand how to avoid +circular references and care for the implications of parallelism though) + +.functional +The `function` template adds generic functor objects to C++. In combination with the `bind` function +(which binds or ties an existing function invocation into a functor object), this allows to ``erase'' (hide) +the difference between functions, function pointers and member functions at your interfaces and thus enables +building all sorts of closures, signals (generic callbacks) and notification services. Picking up on these +concepts might be mind bending at start, but it's certainly worth the effort (in terms of programmer +productivity) + +.hashtables and hash functions +The `unordered_*` collection types amend a painful omission in the STL. The `functional_hash` library +supplements hash function for the primitive types and a lot of standard constructs using the STL; moreover +there is an extension point for using custom types in those hashtables +(-> read more link:HashFunctions.html[here...]) + +.noncopyable +Inheriting from `boost::noncoypable` inhibits any copy, assignment and copy construction. It's a highly +recommended practice _by default to use that for every new class you create_ -- unless you know for sure +your class is going to have _value semantics_. The C++ language has kind of a ``fixation'' on value +semantics, passing objects by value, and the language adds a lot of magic on that behalf. Which might lead +to surprising results if you aren't aware of the fine details. + +.type traits +Boost provides a very elaborate collection of type trait templates, allowing to ``detect'' several +properties of a given type at compile time. Since C++ has no reflection and only a very weak introspection +feature (RTTI, run time type information), using these type traits is often indispensable. + +.enable_if +a simple but ingenious metaprogramming trick, allowing to control precisely in which cases the compiler +shall pick a specific class or function template specialisation. Basically this allows to control the +code generation, based on some type traits or other (metaprogramming) predicates you provide. Again, +since C++ is lacking introspection features, we're frequently forced to resort to metaprogramming +techniques, i.e to influence the way the compiler translates our code from within that very code. + +.STATIC_ASSERT +a helper to check and enforce some conditions regarding types _at compile time_. +Because there is no support for this feature by the compiler, in case of assertion failure +a compilation error is provoked, trying to give at least a clue to the real problem by +creative use of variable names printed in the compiler's error message. + +.metaprogramming library +A very elaborate, and sometimes mind-bending library and framework. While heavily used within +Boost to build the more advanced features, it seems too complicated and esoteric for general purpose +and everyday use. Code written using the MPL tends to be very abstract and almost unreadable for +people without math background. In Lumiera, we _try to avoid using MPL directly._ Instead, we +supplement some metaprogramming helpers (type lists and tuples), written in a simple LISP style, +which -- hopefully -- should be decipherable without having to learn an abstract academic +terminology and framework. + +.lambda +In a similar vein, the `boost::lambda` library might be worth exploring a bit, yet doesn't add +much value in practical use. It is stunning to see how this library adds the capability to define +real _lambda expressions_ on-the-fly, but since C++ was never designed for language extensions of +that kind, using lambda in practice is surprisingly tricky, sometimes even obscure and rarely +not worth the hassle. (An notable exception might be when it comes to defining parser combinators) + +.operators +The `boost::operators` library allows to build families of types/objects with consistent +algebraic properties. Especially, it eases building _equality comparable_, _totally ordered_, +_additive_, _mulitplicative_ entities: You're just required to provide some basic operators +and the library will define all sorts of additional operations to care for the logical +consequences, removing the need for writing lots of boilerplate code. + +.lexical_cast +Converting numerics to string and back without much ado, as you'd expect it from any decent language. + +.boost::format +String formatting with `printf` style directives. Interpolating values into a template string for +formatted output -- but typesafe, using defined conversion operators and without the dangers of +the plain-C `printf` famility of functions. But beware: `boost::format` is implemented on top of +the C++ output stream operations (`<<` and manipulators), which in turn are implemented based +on `printf` -- you can expect it to be 5 to 10 times slower than the latter, and it has +quite some compilation overhead. + +.variant and any +These library provide a nice option for building data structures able to hold a mixture of +multiple types, especially types not directly related to each other. `boost::variant` is a +typeseafe union record, while `boost::any` is able to hold just any other type you provide +_at runtime_, still with some degree of type safety when retrieving the stored values. +Both libraries are compellingly simple to use, yet add some overhead in terms of size, +runtime, and compile time. + +.regular expressions +Boost provides a full blown regular expression library, supporting roughly the feature set of +perl regular expressions. The usage and handling is somewhat brittle though, when compared +with perl, python, java, etc. + +.program-options and filesystem +Same as the aforementioned, these two libraries just supply a familiar programming model for these tasks +(parsing the command line and navigating the filesystem) which can be considered quasi standard today, +and is available pretty much in the same style in Java, Python, Ruby, Perl and others. + + +Negative Impact +--------------- +Most Boost libraries are _header only_ and all of them make heavy use of template related features of C++. +Thus, _every inclusion of a Boost library_ might lead to _increased compilation times._ We pay that penalty +per compilation unit (not per header). Yet still, using a boost library within a header frequently included +throughout the code base might dangerously leverage that effect. + +debug mode +~~~~~~~~~~ +Usually, when developing, we translate our code without optimisation and with full debugging informations +switched on. Unfortunately, C++ templates were never designed to serve as a functional metaprogramming language +to start with -- but that's exactly what we're (ab)using them for. The Boost libraries drive that to quite +some extreme. This leads to lots and lots of debugging information to be added to the object files, +mentioning each and every intermediary type created in the course of expanding the metaprogramming +facilities. Even seemingly simple things may result in object files becoming several megabytes large. + +Fortunately, all of this overhead gets removed when _stripping_ your executable and libraries (or when +compiling without debug information). So this is solely an issue relevant for the developers, as it increases +compilation, linking and startup times. + +runtime overhead and template bloat +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The core Boost libraries (the not-so experimental ones) have a reputation for being of very high +quality. The're written by experts with a deep level of understanding of the language, the usual +implementation and the performance implications. Mostly, those quite elaborate metaprogramming +techniques where chosen exactly to minimise runtime overhead or code size. + +Since each instantiation of a template constitutes a completely new class, carelessly written +template code can lead to heavily bloated executables. Every instantiated _function_ and every +class with _virtual methods_ (i.e. with a VTable) adds to the weight. But this negative effect can +be balanced by the ability of reducing inline code. According to my own experience, I'd be much +more concerned _about my own code adding template bloat,_ then being concerned about the Boost +libraries (those people know very well what they're doing...) + +some practical guidelines +~~~~~~~~~~~~~~~~~~~~~~~~~ +- `boost::format`, `boost::variant`, `boost::any`, `boost::lambda` and the more elaborate metaprogramming + stuff adds considerable weight. A good advice is to confine those features to the implementation level: + use them within individual translation units (`*.cpp`) where this makes sense, but don't cast + general interfaces in terms of those library constructs. +- the _functional_ tools (`function`, `bind` and friends), the _hash functions_, _lexical_cast_ and the + _regular expressions_ create a moderate overhead. Probably fine in general purpose code, but you should + be aware that there is a price tag. About the same as with many STL features. +- the `shared_ptr` `weak_ptr`, `intrusive_ptr` and `scoped_ptr` are really indispensable and can be used + liberally everywhere. Same for `enable_if` and the `boost::type_traits`. The impact of those features + on compilation times and code size is negligible and the runtime overhead is zero, compared to _performing + the same stuff manually_ (obviously a ref-counting pointer like `smart_ptr` has the size of two raw pointers + and incurs an overhead on each creation, copy and disposal of a variable -- but that's besides the point I'm + trying to make here, and generally unavoidable) + From a3b7305b2bb3a3e73ecbf4fd999444651367a3e5 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 27 Dec 2011 01:27:46 +0100 Subject: [PATCH 321/496] reduce number of threads for some tests they tend to fail when running under builddrone --- tests/lib/diagnostic-context-test.cpp | 2 +- tests/lib/thread-local-test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/lib/diagnostic-context-test.cpp b/tests/lib/diagnostic-context-test.cpp index 07f3f43ed..001c92cef 100644 --- a/tests/lib/diagnostic-context-test.cpp +++ b/tests/lib/diagnostic-context-test.cpp @@ -39,7 +39,7 @@ namespace test{ namespace { // private test setup... - const uint NUM_THREADS = 100; + const uint NUM_THREADS = 50; const uint MAX_RAND = 1000*1000; inline bool diff --git a/tests/lib/thread-local-test.cpp b/tests/lib/thread-local-test.cpp index 45465045d..114ba4c05 100644 --- a/tests/lib/thread-local-test.cpp +++ b/tests/lib/thread-local-test.cpp @@ -38,7 +38,7 @@ namespace test{ namespace { // private test setup... - const uint NUM_THREADS = 100; + const uint NUM_THREADS = 50; const uint MAX_RAND = 5*1000*1000; From 50885a065bddea25cf0847f77fa021db5c0b97cb Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 27 Dec 2011 00:51:20 +0100 Subject: [PATCH 322/496] move asside lib/format.hpp ...to make room for a new more specialised header --- src/lib/{format.hpp => format-util.hpp} | 0 src/lib/meta/duck-detector.hpp | 2 +- src/lib/simple-allocator.hpp | 2 +- src/lib/sub-id.hpp | 2 +- src/lib/typed-allocation-manager.hpp | 2 +- src/proc/control/command-closure.hpp | 2 +- src/proc/control/command-registry.hpp | 1 - src/proc/control/memento-tie.hpp | 2 +- tests/components/proc/control/command-use1-test.cpp | 2 +- tests/lib/format-helper-test.cpp | 2 +- 10 files changed, 8 insertions(+), 9 deletions(-) rename src/lib/{format.hpp => format-util.hpp} (100%) diff --git a/src/lib/format.hpp b/src/lib/format-util.hpp similarity index 100% rename from src/lib/format.hpp rename to src/lib/format-util.hpp diff --git a/src/lib/meta/duck-detector.hpp b/src/lib/meta/duck-detector.hpp index bed83ce63..a559ed27a 100644 --- a/src/lib/meta/duck-detector.hpp +++ b/src/lib/meta/duck-detector.hpp @@ -30,7 +30,7 @@ ** Because, if we can get a \c bool answer to such a question <i>at compile time,</i> we can use ** \c boost::enable_if to pick a special implementation based on the test result. Together, these ** techniques allow to adopt a duck-typed programming style, where an arbitrary object is allowed - ** to enter a given API function, provided this object supports some special operations. + ** to enter a given API function, provided this object supports some specific operations. ** ** While C++ certainly isn't a dynamic language and doesn't provide any kind of run time introspection, ** doing such check-and branch at compile time allows even to combine such a flexible approach with diff --git a/src/lib/simple-allocator.hpp b/src/lib/simple-allocator.hpp index 8dd5cea25..c6af61723 100644 --- a/src/lib/simple-allocator.hpp +++ b/src/lib/simple-allocator.hpp @@ -49,7 +49,7 @@ #include "lib/error.hpp" #include "lib/meta/generator.hpp" #include "lib/meta/typelist-util.hpp" -#include "lib/format.hpp" +#include "lib/format-util.hpp" #include "lib/typed-counter.hpp" #include "include/logging.h" diff --git a/src/lib/sub-id.hpp b/src/lib/sub-id.hpp index fa47f42f5..7442fadf5 100644 --- a/src/lib/sub-id.hpp +++ b/src/lib/sub-id.hpp @@ -46,7 +46,7 @@ #ifndef LIB_SUB_ID_H #define LIB_SUB_ID_H -#include "lib/format.hpp" +#include "lib/format-util.hpp" //#include <functional> #include <boost/functional/hash.hpp> /////TODO better push the hash implementation into a cpp file (and btw, do it more seriously!) diff --git a/src/lib/typed-allocation-manager.hpp b/src/lib/typed-allocation-manager.hpp index a4d8810fc..e44eb01bb 100644 --- a/src/lib/typed-allocation-manager.hpp +++ b/src/lib/typed-allocation-manager.hpp @@ -67,7 +67,7 @@ #define LIB_TYPED_ALLOCATION_MANAGER_H #include "lib/error.hpp" -#include "lib/format.hpp" +#include "lib/format-util.hpp" #include "lib/typed-counter.hpp" #include "include/logging.h" diff --git a/src/proc/control/command-closure.hpp b/src/proc/control/command-closure.hpp index b03f88405..118beb68a 100644 --- a/src/proc/control/command-closure.hpp +++ b/src/proc/control/command-closure.hpp @@ -74,7 +74,7 @@ #include "lib/meta/function-erasure.hpp" #include "lib/meta/tuple.hpp" #include "lib/meta/maybe-compare.hpp" -#include "lib/format.hpp" +#include "lib/format-util.hpp" #include "lib/util.hpp" #include "proc/control/argument-erasure.hpp" #include "lib/typed-allocation-manager.hpp" diff --git a/src/proc/control/command-registry.hpp b/src/proc/control/command-registry.hpp index c2231bd6e..c749a2dbc 100644 --- a/src/proc/control/command-registry.hpp +++ b/src/proc/control/command-registry.hpp @@ -60,7 +60,6 @@ #include "lib/error.hpp" #include "lib/singleton.hpp" #include "lib/sync.hpp" -#include "lib/format.hpp" #include "include/logging.h" #include "lib/util.hpp" diff --git a/src/proc/control/memento-tie.hpp b/src/proc/control/memento-tie.hpp index 0e89f9b21..21881da6e 100644 --- a/src/proc/control/memento-tie.hpp +++ b/src/proc/control/memento-tie.hpp @@ -45,7 +45,7 @@ #include "lib/meta/function-closure.hpp" #include "proc/control/command-signature.hpp" #include "lib/functor-util.hpp" -#include "lib/format.hpp" +#include "lib/format-util.hpp" #include "lib/util.hpp" #include <boost/operators.hpp> diff --git a/tests/components/proc/control/command-use1-test.cpp b/tests/components/proc/control/command-use1-test.cpp index eaa00b2a5..d854d7f0d 100644 --- a/tests/components/proc/control/command-use1-test.cpp +++ b/tests/components/proc/control/command-use1-test.cpp @@ -25,7 +25,7 @@ #include "lib/test/test-helper.hpp" #include "proc/control/command-invocation.hpp" #include "proc/control/command-def.hpp" -#include "lib/format.hpp" +#include "lib/format-util.hpp" #include "lib/util.hpp" #include "proc/control/test-dummy-commands.hpp" diff --git a/tests/lib/format-helper-test.cpp b/tests/lib/format-helper-test.cpp index 4c246ff3d..9ca85d3ee 100644 --- a/tests/lib/format-helper-test.cpp +++ b/tests/lib/format-helper-test.cpp @@ -22,7 +22,7 @@ #include "lib/test/run.hpp" -#include "lib/format.hpp" +#include "lib/format-util.hpp" #include "lib/error.hpp" #include <iostream> From 89928bc447fef19c66ceb8f45fbf2e5ca09ab6a0 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 27 Dec 2011 01:25:09 +0100 Subject: [PATCH 323/496] refactor format-util: simplify picking the right specialisation --- src/lib/format-util.hpp | 105 ++++++++++++++++--------------- src/lib/meta/trait.hpp | 8 +-- tests/lib/format-helper-test.cpp | 14 ++--- 3 files changed, 64 insertions(+), 63 deletions(-) diff --git a/src/lib/format-util.hpp b/src/lib/format-util.hpp index 4aa0a1743..d1ef9d021 100644 --- a/src/lib/format-util.hpp +++ b/src/lib/format-util.hpp @@ -1,5 +1,5 @@ /* - FORMAT.hpp - helpers for formatting and diagnostics + FORMAT-UTIL.hpp - helpers for formatting and diagnostics Copyright (C) Lumiera.org 2009, Hermann Vosseler <Ichthyostega@web.de> @@ -23,18 +23,23 @@ /** @file format.hpp ** Collection of small helpers and convenience shortcuts for diagnostics & formatting. + ** - util::str() performs a failsafe to-String conversion, thereby preferring a + ** built-in conversion operator, falling back to just a mangled type string. + ** - util::tyStr() generates a string corresponding to the type of the given object. + ** Currently just implemented through the mangled RTTI type string ** - ** @todo we could add a facade to boost::format here, see Ticket #166 + ** @see FormatHelper_test + ** @see format-string.hpp frontend for boost::format, printf-style ** */ -#ifndef UTIL_FORMAT_H -#define UTIL_FORMAT_H +#ifndef LIB_FORMAT_UTIL_H +#define LIB_FORMAT_UTIL_H -//#include "lib/util.hpp" #include "lib/meta/trait.hpp" #include "lib/symbol.hpp" +#include "lib/util.hpp" #include <string> #include <cstring> @@ -46,61 +51,65 @@ namespace util { + using boost::enable_if; using lib::meta::can_ToString; using lib::meta::can_lexical2string; using lib::Symbol; - using boost::enable_if; - using boost::disable_if; + using util::isnil; using std::string; namespace { // we need to guard the string conversion // to avoid a compiler error in case the type isn't convertible.... - template<typename X> - inline string - invoke_2string ( typename enable_if< can_ToString<X>, - X >::type const& val) - { - return string(val); - } template<typename X> - inline string - invoke_2string ( typename disable_if< can_ToString<X>, - X >::type const&) - { - return ""; - } - - + struct use_StringConversion : can_ToString<X> { }; template<typename X> - inline string - invoke_indirect2string ( typename enable_if< can_lexical2string<X>, - X >::type const& val) - { - try { return boost::lexical_cast<string> (val); } - catch(...) { return ""; } - } + struct use_LexicalConversion + { + enum { value = can_lexical2string<X>::value + && !can_ToString<X>::value + }; + }; + + + /** helper: reliably get some string representation for type X */ + template<typename X, typename COND =void> + struct _InvokeFailsafe + { + static string toString (X const&) { return ""; } + }; template<typename X> - inline string - invoke_indirect2string ( typename disable_if< can_lexical2string<X>, - X >::type const&) - { - return ""; - } - } + struct _InvokeFailsafe<X, typename enable_if< use_StringConversion<X> >::type> + { + static string + toString (X const& val) + try { return string(val); } + catch(...) { return ""; } + }; + + template<typename X> + struct _InvokeFailsafe<X, typename enable_if< use_LexicalConversion<X> >::type> + { + static string + toString (X const& val) + try { return boost::lexical_cast<string> (val); } + catch(...) { return ""; } + }; + }//(End) guards/helpers + /** try to get an object converted to string. - * An custom/standard conversion to string is used, + * A custom/standard conversion to string is used, * if applicable; otherwise, some standard types can be * converted by a lexical_cast (based on operator<< ). * Otherwise, either the fallback string is used, or just - * a string denoting the (mangled) type. + * a string based on the (mangled) type. */ template<typename TY> inline string @@ -109,24 +118,16 @@ namespace util { , Symbol fallback =0 /// < replacement text to show if string conversion fails ) { - if (can_ToString<TY>::value) - return string(prefix) + invoke_2string<TY>(val); - + string res = _InvokeFailsafe<TY>::toString(val); + if (!isnil (res)) + return string(prefix) + res; else - { - if (can_lexical2string<TY>::value) - { - string res (invoke_indirect2string<TY> (val)); - if ("" != res) - return string(prefix) + res; - } - - return fallback? string(fallback) - : tyStr(val); - } + return fallback? string(fallback) + : tyStr(val); } + /** @return a string denoting the type. */ template<typename TY> inline string diff --git a/src/lib/meta/trait.hpp b/src/lib/meta/trait.hpp index 0f25822fb..3c5ecbe0d 100644 --- a/src/lib/meta/trait.hpp +++ b/src/lib/meta/trait.hpp @@ -33,7 +33,7 @@ #include <boost/type_traits/remove_reference.hpp> #include <boost/type_traits/remove_pointer.hpp> #include <boost/type_traits/remove_cv.hpp> -#include <boost/utility/enable_if.hpp> + #include <string> //Forward declarations for the Unwrap helper.... @@ -42,14 +42,14 @@ namespace boost{ } namespace std { namespace tr1 { - template<class X> class reference_wrapper; + template<class X> class reference_wrapper; template<class X> class shared_ptr; }} namespace lib{ - template<class X, class B> class P; + template<class X, class B> class P; } namespace mobject{ - template<class X, class B> class Placement; + template<class X, class B> class Placement; } diff --git a/tests/lib/format-helper-test.cpp b/tests/lib/format-helper-test.cpp index 9ca85d3ee..f223510af 100644 --- a/tests/lib/format-helper-test.cpp +++ b/tests/lib/format-helper-test.cpp @@ -35,8 +35,7 @@ namespace util { namespace test { class Reticent - { - }; + { }; class UnReticent : public Reticent @@ -47,10 +46,11 @@ namespace test { - /************************************************* - * verifies the proper working of helper functions - * frequently used within the Lumiera testsuite. - * @see test-helper.hpp + /******************************************************************************* + * @test verifies the proper working of some string-formatting helper functions. + * - util::str() provides a failsafe to-String conversion, preferring + * an built-in conversion, falling back to just a mangled type string. + * @see format-util.hpp */ class FormatHelper_test : public Test { @@ -61,7 +61,7 @@ namespace test { } - /** @test verify the maybe-to-string conversion. */ + /** @test verify a failasfe to-string conversion. */ void check2String () { From 545d9ea82b072741f21a8e18e0c903c48c8c6a9a Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 27 Dec 2011 05:22:02 +0100 Subject: [PATCH 324/496] WIP draft frontend for diagnostic output (#166) --- src/lib/format-string.hpp | 126 +++++++++++++ src/lib/format-util.hpp | 5 +- tests/40components.tests | 5 + tests/lib/format-string-test.cpp | 307 +++++++++++++++++++++++++++++++ 4 files changed, 440 insertions(+), 3 deletions(-) create mode 100644 src/lib/format-string.hpp create mode 100644 tests/lib/format-string-test.cpp diff --git a/src/lib/format-string.hpp b/src/lib/format-string.hpp new file mode 100644 index 000000000..bf98fa14f --- /dev/null +++ b/src/lib/format-string.hpp @@ -0,0 +1,126 @@ +/* + FORMAT-STRING.hpp - string template formatting based on boost::format + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file format-string.hpp + ** Front-end for printf-style string template interpolation. + ** While the actual implementation just delegates to boost::format, this front-end + ** hides the direct dependency, additionally invokes a custom toSting conversion + ** whenever possible, provides a direct conversion to string and catches any exceptions. + ** This front-end is used pervasively for diagnostics and logging, so keeping down the + ** compilation and object size cost and reliably handling any error is more important + ** than the (small) performance gain of directly invoking boost::format (which is + ** known to be 10 times slower than printf anyway). + ** + ** @remarks The implementation is invoked through a set of explicit specialisations. + ** For custom types, we prefer to invoke operator string(), which is determined + ** by a directly coded metaprogramming test. The compile time and object size + ** overhead incurred by using this header was verified to be negligible. + ** + ** @see FormatString_test + ** @see format-helper + ** + */ + + +#ifndef UTIL_FORMAT_STRING_H +#define UTIL_FORMAT_STRING_H + +//#include "lib/meta/trait.hpp" +//#include "lib/symbol.hpp" +//#include "lib/util.hpp" + +#include <string> +//#include <cstring> +#include <typeinfo> +//#include <boost/lexical_cast.hpp> +#include <boost/noncopyable.hpp> +#include <boost/utility/enable_if.hpp> + + + +namespace std { // forward declaration to avoid including <iostream> + + template<typename C> + class char_traits; + + template<typename C, class _TRAITS> + class basic_ostream; + + typedef basic_ostream<char, char_traits<char> > ostream; +} + + +namespace util { + + using boost::enable_if; +//using util::isnil; + using std::string; + + + namespace { // helpers to pick a suitable specialisation.... + + const size_t FORMATTER_SIZE = 100; + + }//(End) guards/helpers + + + + + /** + * @todo write type comment + */ + class _Fmt + : boost::noncopyable + { + char formatter_[FORMATTER_SIZE]; + + public: + _Fmt (string formatString); + + operator string() const; + + template<typename TY> + _Fmt& + operator% (TY const&); + + + friend std::ostream& + operator<< (std::ostream& os, _Fmt const&); + + friend bool operator== (_Fmt const&, _Fmt const&); + friend bool operator== (_Fmt const&, string const&); + friend bool operator== (_Fmt const&, const char * const); + friend bool operator== (string const& , _Fmt const&); + friend bool operator== (const char * const, _Fmt const&); + + template<typename X> + friend bool operator != (_Fmt const& fmt, X const& x) { return !(fmt == x); } + template<typename X> + friend bool operator != (X const& x, _Fmt const& fmt) { return !(x == fmt); } + }; + + + +} // namespace util + +#endif /*UTIL_FORMAT_STRING_H*/ diff --git a/src/lib/format-util.hpp b/src/lib/format-util.hpp index d1ef9d021..c5f1b76e8 100644 --- a/src/lib/format-util.hpp +++ b/src/lib/format-util.hpp @@ -21,7 +21,7 @@ */ -/** @file format.hpp +/** @file format-util.hpp ** Collection of small helpers and convenience shortcuts for diagnostics & formatting. ** - util::str() performs a failsafe to-String conversion, thereby preferring a ** built-in conversion operator, falling back to just a mangled type string. @@ -143,5 +143,4 @@ namespace util { } // namespace util - -#endif /*UTIL_FORMAT_H*/ +#endif diff --git a/tests/40components.tests b/tests/40components.tests index 54862528f..3ad2bc07d 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -329,6 +329,11 @@ return: 0 END +PLANNED "formatting by string template" FormatString_test <<END +return: 0 +END + + TEST "functional closure" FunctionClosure_test <<END out: List1 :-<1>-<2>-<3>- out: List2 :-<5>-<6>-<7>- diff --git a/tests/lib/format-string-test.cpp b/tests/lib/format-string-test.cpp new file mode 100644 index 000000000..79bce5f1b --- /dev/null +++ b/tests/lib/format-string-test.cpp @@ -0,0 +1,307 @@ +/* + FormatString(Test) - validate string template formatting (printf style) + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/format-string.hpp" +#include "lib/error.hpp" +#include "lib/util.hpp" + +#include <boost/lexical_cast.hpp> +#include <iostream> +#include <cstdlib> + +using boost::lexical_cast; +using std::cout; +using std::endl; + + +namespace util { +namespace test { + + namespace error = lumiera::error; + + + + /******************************************************************************* + * @test verifies our front-end for printf-style format string based formatting. + * - the implementation is based on boost::format + * - we create a wrapper-object on-the fly, which is able to hold + * the result of a partial invocation + * - explicit specialisations feed all primitive types directly + * down into the implementation level. If possible, a custom operator string() + * will be used for formatting. + * - any kind of exception will be logged, but handled gracefully + * + * @see format-string.hpp + * @see boost::format + */ + class FormatString_test : public Test + { + void + run (Arg) + { + check_simpleInvocation(); + check_delayedInvocation(); + verify_typeHandling(); + verify_customType(); + verify_errorHandling(); + verify_pointerHandling(); + } + + + + void + check_simpleInvocation () + { + string formatted = _Fmt("--format-template--int=%+04d--double=%5.2f--string=%-10s--") + % 12 + % 1.228 + % "Lumiera"; + cout << formatted << endl; + CHECK (formatted == "--format-template--int=0012--double= 1.23--string=Lumiera --"); + } + + + void + check_delayedInvocation () + { + string formatString = "%p %|20T_| %u"; + _Fmt formatter (formatString); + + uint val = rand() % 100; + + formatter % &val; + formatter % val; + + cout << formatter << endl; + } + + + void + verify_typeHandling () + { + int i(-12); + CHECK (_Fmt("%d") % i == "-12" ); + CHECK (_Fmt("%6d") % i == "- 12" ); + CHECK (_Fmt("%-6d") % i == "-12 " ); + CHECK (_Fmt("%+-6d") % -i == "+12 " ); + CHECK (_Fmt("%+06d") % -i == "+00012" ); + CHECK (_Fmt("%+06X") % -i == "+0000D" ); + CHECK (_Fmt("%+#X") % -i == "+Dh" ); + + uint u(12); + CHECK (_Fmt("%d") % u == "12" ); + CHECK (_Fmt("%6d") % u == " 12" ); + CHECK (_Fmt("%-6d") % u == "12 " ); + CHECK (_Fmt("%+-6d") % u == "+12 " ); + CHECK (_Fmt("%+06d") % u == "+00012" ); + + short sh(-123); + CHECK (_Fmt("%6d") % sh == "- 123" ); + + ushort ush(123); + CHECK (_Fmt("%6d") % ush == " 123" ); + + long l(-123); + CHECK (_Fmt("%6d") % l == "- 123" ); + + ulong ul(123); + CHECK (_Fmt("%6d") % ul == " 123" ); + + int64_t ll(5e+9); + CHECK (_Fmt("%d") % ll == "5000000000" ); + CHECK (_Fmt("%10.3d") %ll == " 500" ); + CHECK (_Fmt("%10.3E") %ll == " 5.000E9" ); + + uint64_t ull(ll); + CHECK (_Fmt("%d") % ull == "5000000000" ); + + float f(12.34); + CHECK (_Fmt("%g") % f == "12.34" ); + CHECK (_Fmt("%d") % f == "12" ); + CHECK (_Fmt("%e") % f == "12.34e0"); + + double d(-12.34); + CHECK (_Fmt("%g") % d == "-12.34" ); + + char c(0x40); + CHECK (_Fmt("%d") % c == "64" ); + CHECK (_Fmt("%x") % c == "40" ); + CHECK (_Fmt("%o") % c == "100" ); + CHECK (_Fmt("%c") % c == "@" ); + CHECK (_Fmt("%s") % c == "@" ); + + unsigned char uc(0xff); + CHECK (_Fmt("%d") % uc == "255" ); + CHECK (_Fmt("%x") % uc == "ff" ); + CHECK (_Fmt("%X") % uc == "FF" ); + CHECK (_Fmt("%c") % uc == "\xFF" ); + CHECK (_Fmt("%s") % uc == "\xFF" ); + + c = char(uc); + CHECK (_Fmt("%d") % uc == "-1" ); + CHECK (_Fmt("%c") % uc == "\xFF" ); + + string str("Lumiera"); + CHECK (_Fmt("%s") % str == "Lumiera" ); + CHECK (_Fmt("%10s") % str == " Lumiera" ); + CHECK (_Fmt("%8.4s") %str == " Lumi" ); + CHECK (_Fmt("%10c") % str == " L" ); + } + + + /* == some custom types to test with == */ + + struct Silent + { + int i_; + + Silent(int i) : i_(i) { } + }; + + struct Verbose + : Silent + { + Verbose(int i) : Silent(i) { } + + virtual + operator string() const + { + return _Fmt("Number-%03d") % i_; + } + }; + + struct Explosive + : Verbose + { + Explosive(int i) : Verbose(i) { } + + operator string() const + { + if (23 == i_) + throw error::State("encountered Fantomas"); + else + return Verbose::operator string(); + } + }; + + + void + verify_customType () + { + Silent s(12); + Verbose v(13); + Explosive x(23); + + Silent& rs1 = v; + Silent& rs2 = x; + + Verbose& rv = x; + + string mangledType = _Fmt("%s") % s; + cout << mangledType << endl; + CHECK (contains (mangledType, "util")); + CHECK (contains (mangledType, "test")); + CHECK (contains (mangledType, "Silent")); + + CHECK (_Fmt("!!%s!!") % v == "!!Number-013!!"); + CHECK (_Fmt("!!%s!!") % x == "!!unforeseen internal state (encountered Fantomas).!!"); + + CHECK (contains (_Fmt("%s") % rs1, "Silent")); + CHECK (contains (_Fmt("%s") % rs2, "Silent")); + + CHECK (_Fmt("!!%s!!") % rv == "!!unforeseen internal state (encountered Fantomas).!!"); + + x.i_ = 42; + CHECK (_Fmt("!!%s!!") % rv == "!!Number-042!!"); + CHECK (_Fmt("!!%s!!") % x == "!!Number-042!!"); + } + + + void + verify_errorHandling () + { + cout << _Fmt("__nix_") % 1 % 2 << endl; + cout << _Fmt("__%d__") << endl; + cout << _Fmt("__%d__") % 1 << endl; + cout << _Fmt("__%d__") % 1 % 2 << endl; + + cout << _Fmt("__%d__") % "dirt" << endl; + cout << _Fmt("__%d__") % "1234" << endl; + cout << _Fmt("__%d__") % "0xff" << endl; + } + + + void + verify_pointerHandling () + { + int i(-12); int * pi = & i; + uint u(12); uint * pu = & u; + short sh(-123); short * psh = & sh; + ushort ush(123); ushort *push = &ush; + long l(-123); long * pl = & l; + ulong ul(123); ulong * pul = & ul; + int64_t ll(5e+9); int64_t * pll = & ll; + uint64_t ull(ll); uint64_t *pull = &ull; + float f(12.34); float * pf = & f; + double d(-12.34); double * pd = & d; + char c(0x40); char * pc = & c; + unsigned char uc(0xff); unsigned char * puc = & uc; + string str("Lumiera"); string *pstr = &str; + + CHECK (_Fmt("%d") % i == _Fmt("%d") % pi); + CHECK (_Fmt("%d") % u == _Fmt("%d") % pu); + CHECK (_Fmt("%d") % sh == _Fmt("%d") % psh); + CHECK (_Fmt("%d") % ush == _Fmt("%d") % push); + CHECK (_Fmt("%d") % l == _Fmt("%d") % pl); + CHECK (_Fmt("%d") % ul == _Fmt("%d") % pul); + CHECK (_Fmt("%d") % ll == _Fmt("%d") % pll); + CHECK (_Fmt("%d") % ull == _Fmt("%d") % pull); + CHECK (_Fmt("%e") % f == _Fmt("%e") % pf); + CHECK (_Fmt("%e") % d == _Fmt("%e") % pd); + CHECK (_Fmt("%g") % c == _Fmt("%g") % pc); + CHECK (_Fmt("%g") % uc == _Fmt("%g") % puc); + CHECK (_Fmt("%s") % str == _Fmt("%s") % pstr); + + Verbose v(42); + Verbose *pv = &v; + void *vv = pv; + CHECK (_Fmt("__%s__") % v == "__Number-042__"); + CHECK (_Fmt("__%s__") % pv == "__Number-042__"); + CHECK (_Fmt("__%s__") % vv != "__Number-042__"); + + string address = lexical_cast<string>(pv); + CHECK (_Fmt("__%s__") % vv == "__"+address+"__"); + + pv = NULL; + vv = NULL; + CHECK (_Fmt("__%s__") % pv == "__(null)__"); + CHECK (_Fmt("__%s__") % pv == "__0__"); + } + }; + + LAUNCHER (FormatString_test, "unit common"); + + +}} // namespace util::test + From 6e4883bcf1bea9bf29a02b1e84ca0cc760c8a565 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 27 Dec 2011 06:14:46 +0100 Subject: [PATCH 325/496] add stub definitions to pass compilation --- src/lib/format-string.cpp | 88 +++++++++++++++++++++++++++++++++++++++ src/lib/format-string.hpp | 74 +++++++++++++++++++++++++++++--- 2 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 src/lib/format-string.cpp diff --git a/src/lib/format-string.cpp b/src/lib/format-string.cpp new file mode 100644 index 000000000..30d3a7774 --- /dev/null +++ b/src/lib/format-string.cpp @@ -0,0 +1,88 @@ +/* + FormatString - string template formatting based on boost::format + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + +/** @file format-string.cpp + ** Implementation for printf-style formatting, based on boost::format. + ** This file holds the generic implementation of our format frontend, + ** which actually just invokes boost::format. The corresponding header + ** format-string.hpp contains some template functions and classes, + ** which select an appropriate wrapper to pass the calls down. + ** Here, we define explicit specialisations for the frontend to invoke, + ** which in turn just pass on the given argument value to the embedded + ** boost::format object, which in turn integrates the formatted result + ** into an embedded string stream. + ** + ** To avoid exposing boost::format in the frontend header, we use an + ** opaque buffer of appropriate size to piggyback the formatter object. + ** @warning unfortunately this requires a hard coded buffer size constant + ** in the front-end, which we define there manually, based on + ** the current implementation layout found in the boost libraries. + ** If Boost eventually changes the implementation, the assertion + ** in our constructor will trigger. + ** + ** @see FormatString_test + ** + */ + + + +#include "lib/error.hpp" +#include "lib/format-string.hpp" + +#include <boost/format.hpp> +#include <iostream> + + + + + +namespace util { + + namespace { // implementation details... + + + }//(End) implementation details + + + /** */ + _Fmt::_Fmt (string formatString) + { + UNIMPLEMENTED ("create the embedded boost::format object"); + } + + + /** */ + _Fmt::operator string() const + { + UNIMPLEMENTED ("forward to the embedded boost::format object"); + } + + + std::ostream& + operator<< (std::ostream& os, _Fmt const& fmt) + { + return os << string(fmt); + } + + + +} // namespace util diff --git a/src/lib/format-string.hpp b/src/lib/format-string.hpp index bf98fa14f..5efbe2f6f 100644 --- a/src/lib/format-string.hpp +++ b/src/lib/format-string.hpp @@ -79,7 +79,7 @@ namespace util { namespace { // helpers to pick a suitable specialisation.... - const size_t FORMATTER_SIZE = 100; + enum{ FORMATTER_SIZE = 100 }; }//(End) guards/helpers @@ -94,21 +94,28 @@ namespace util { { char formatter_[FORMATTER_SIZE]; + template<typename VAL, class SEL =void> + struct Param; + + template<typename VAL, class SEL> + friend struct Param; + + public: _Fmt (string formatString); operator string() const; - template<typename TY> + template<typename VAL> _Fmt& - operator% (TY const&); + operator% (VAL const&); friend std::ostream& operator<< (std::ostream& os, _Fmt const&); - friend bool operator== (_Fmt const&, _Fmt const&); - friend bool operator== (_Fmt const&, string const&); + friend bool operator== (_Fmt const&, _Fmt const&); + friend bool operator== (_Fmt const&, string const&); friend bool operator== (_Fmt const&, const char * const); friend bool operator== (string const& , _Fmt const&); friend bool operator== (const char * const, _Fmt const&); @@ -121,6 +128,63 @@ namespace util { + + + /* == forwarding into the implementation == */ + + template<typename VAL> + inline _Fmt& + _Fmt::operator% (VAL const& val) + { + Param<VAL>::push (val); + return *this; + } + + + template<typename VAL, class SEL> + struct _Fmt::Param + { + static void + push (VAL const&) + { + UNIMPLEMENTED ("get type string"); + } + }; + + + + inline bool + operator== (_Fmt const& left, _Fmt const& right) + { + return string(left) == string(right); + } + + inline bool + operator== (_Fmt const& fmt, string const& str) + { + return string(fmt) == str; + } + + inline bool + operator== (_Fmt const& fmt, const char * const cString) + { + return string(fmt) == string(cString); + } + + inline bool + operator== (string const& str, _Fmt const& fmt) + { + return fmt == str; + } + + inline bool + operator== (const char * const cString, _Fmt const& fmt) + { + return fmt == cString; + } + + + } // namespace util #endif /*UTIL_FORMAT_STRING_H*/ From ce6fbe5f0f7320e5fcbbc28114a99b99d1059f89 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 27 Dec 2011 07:43:39 +0100 Subject: [PATCH 326/496] define how to pick the correct specialisation some basic types are passed on directly; for those, we use explicit specialisations in the implementation file, and a traits template to mark those cases in the header. custom types with custom string conversion will be converted to string; everything else just becomes a type id --- src/lib/format-string.cpp | 32 +++++++++ src/lib/format-string.hpp | 141 ++++++++++++++++++++++++++++++++++---- 2 files changed, 159 insertions(+), 14 deletions(-) diff --git a/src/lib/format-string.cpp b/src/lib/format-string.cpp index 30d3a7774..c0e84fad1 100644 --- a/src/lib/format-string.cpp +++ b/src/lib/format-string.cpp @@ -70,6 +70,38 @@ namespace util { } + /** */ + template<typename VAL> + void + _Fmt::pushParameter (VAL const& val) + { + UNIMPLEMENTED ("feed the parameter to the embedded formatter"); + } + + template<typename VAL> + void + _Fmt::pushParameter (const VAL * const pVal) + { + if (pVal) + pushParameter(*pVal); + else + pushParameter(string("(null)")); + } + + + /* ===== explicitly supported =================== */ + + template void _Fmt::pushParameter(string const&); + template void _Fmt::pushParameter(int const&); + template void _Fmt::pushParameter(uint const&); + + template void _Fmt::pushParameter(const string * const); + template void _Fmt::pushParameter(const int * const); + template void _Fmt::pushParameter(const uint * const); + + + + /** */ _Fmt::operator string() const { diff --git a/src/lib/format-string.hpp b/src/lib/format-string.hpp index 5efbe2f6f..53d3d278e 100644 --- a/src/lib/format-string.hpp +++ b/src/lib/format-string.hpp @@ -48,6 +48,7 @@ //#include "lib/meta/trait.hpp" //#include "lib/symbol.hpp" //#include "lib/util.hpp" +#include "lib/error.hpp" #include <string> //#include <cstring> @@ -70,6 +71,7 @@ namespace std { // forward declaration to avoid including <iostream> } + namespace util { using boost::enable_if; @@ -77,11 +79,6 @@ namespace util { using std::string; - namespace { // helpers to pick a suitable specialisation.... - - enum{ FORMATTER_SIZE = 100 }; - - }//(End) guards/helpers @@ -92,13 +89,19 @@ namespace util { class _Fmt : boost::noncopyable { + enum{ FORMATTER_SIZE = 100 }; + + + char formatter_[FORMATTER_SIZE]; template<typename VAL, class SEL =void> - struct Param; + struct Converter; - template<typename VAL, class SEL> - friend struct Param; + template<typename VAL> + void pushParameter (VAL const&); + template<typename VAL> + void pushParameter (const VAL * const); public: @@ -130,28 +133,138 @@ namespace util { - /* == forwarding into the implementation == */ + /* ===== forwarding into the implementation ====== */ + /** The percent operator (\c '%' ) is used do feed + * additional parameter values to be included into + * the formatted result, at the positions marked + * within the format string. + * + * \par type specific treatment + * Basic types (numbers, chars, strings) are passed to the implementation + * (= boost::format) literally. For custom types, we try to use an custom + * string conversion, if applicable. Any other type gets just translated + * into a type-ID (using the mangled RTTI info). In case of errors during + * the conversion, a string representation of the error is returned + * @param val arbitrary value or pointer to be included into the result + * @warning you need to provide exactly the right number of parameters, + * i.e. matching the number of fields in the format string. + */ template<typename VAL> inline _Fmt& _Fmt::operator% (VAL const& val) { - Param<VAL>::push (val); + pushParameter (Converter<VAL>::prepare (val)); return *this; } - template<typename VAL, class SEL> - struct _Fmt::Param + + namespace { // helpers to pick a suitable specialisation.... + + template<typename X> + struct _can_convertToString + { + enum{ value = false }; + }; + + + /** + * by default we don't allow to + * treat any types directly by boost::format. + * As fallback we rather just produce a type-ID + */ + template<typename X> + struct _can_forward { enum{ value = false };}; + + /* the following definitions enable some basic types + * to be forwarded to boost::format literally */ + template<> struct _can_forward<string> { enum{ value = true }; }; + template<> struct _can_forward<int> { enum{ value = true }; }; + template<> struct _can_forward<uint> { enum{ value = true }; }; + + + + inline string + _log_and_stringify (std::exception const& ex) { - static void - push (VAL const&) + WARN (progress, "Error while invoking custom string conversion: %s", ex.what()); + return ex.what(); + } + + inline string + _log_unknown_exception() + { + const char* errID = lumiera_error(); + if (errID) + { + ERROR (progress, "Unknown error while invoking custom string conversion. Lumiera error flag = %s", errID); + return string("Unknown error in string conversion. FLAG=")+errID; + } + else + { + ERROR (progress, "Unknown error while invoking custom string conversion. No Lumiera error flag set."); + return "Unknown error in string conversion"; + } + } + + }//(End) guards/helpers + + + + /* === explicit specialisations to control the kind of conversion === */ + + template<typename VAL, class SEL> + struct _Fmt::Converter + { + static string + prepare (VAL const&) { UNIMPLEMENTED ("get type string"); } }; + template<typename VAL> + struct _Fmt::Converter<VAL, typename enable_if< _can_convertToString<VAL> >::type> + { + static string + prepare (VAL const& val) + try { + return string(val); + } + catch(std::exception const& ex) + { + return _log_and_stringify(ex); + } + catch(...) + { + return _log_unknown_exception(); + } + }; + template<typename VAL> + struct _Fmt::Converter<VAL, typename enable_if< _can_forward<VAL> >::type> + { + static VAL const& + prepare (VAL const& val) + { + return val; + } + }; + + template<typename VAL> + struct _Fmt::Converter<VAL*, typename enable_if< _can_forward<VAL> >::type> + { + static const VAL * + prepare (const VAL * const pVal) + { + return pVal; + } + }; + + + + /* === comparison of formatter objects === */ inline bool operator== (_Fmt const& left, _Fmt const& right) From 1d7d63e27c15e6f2b5cb0cd4239fb44801ffbf96 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 28 Dec 2011 03:40:39 +0100 Subject: [PATCH 327/496] document a hack for guessing implementation sizes --- doc/technical/code/darkCorners.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/technical/code/darkCorners.txt b/doc/technical/code/darkCorners.txt index 732c2c9ee..03bd9311c 100644 --- a/doc/technical/code/darkCorners.txt +++ b/doc/technical/code/darkCorners.txt @@ -33,3 +33,22 @@ unable to pinpoint _equivalence_, e.g. functors bound to the same function with the same arguments through separate but otherwise identical invocations of +bind+. Besides, should boost or the standard library implementors eventually change the implementation, this workaround will break. + + +Size of standard library facilities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Sometimes we need to know the size of an STL or Boost class, but can't afford +to include the header and just write a `sizeof()`. Because including some of those +headers incurs quite some price in terms of compilation time and even size of the +debug executable. + +Obviously, a simple solution would be to measure those sizes and hardcode them. +But what about portability? To get out of that dilemma, I created a traits class +which mimics the implementation memory layout of those facilities in question, +simplified as much as possible. As long as the GNU libstdc++ or Boost don't +change their implementation layout, this give us precise and fast size bounds. + +When relying on that hack, we should make sure always to place some kind of +STATIC_ASSERT into the corresponding implementation files to ensure the real +facilites actually _do fit_ into the guessed storage dimensions. + From 2d1e098d2cd0976d64cec272a95411129798821e Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 28 Dec 2011 03:55:15 +0100 Subject: [PATCH 328/496] crosslink to the TypedID concept in the TiddlyWiki --- doc/technical/overview.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/technical/overview.txt b/doc/technical/overview.txt index 6d600d492..587ff2dac 100644 --- a/doc/technical/overview.txt +++ b/doc/technical/overview.txt @@ -734,11 +734,12 @@ at runtime (type erasure) Typed Lookup ^^^^^^^^^^^^ -_planned_ a system of per-type lookup tables, based on `EntryID`, together +_[,yellow]#planned#_ a system of per-type lookup tables, based on `EntryID`, together with an type specific access functor. Together, this allows to translate transparently and typesafe from symbolic ID to object instance, which is an prerequisite for integrating a rules based system. Besides, these -tables allow unique IDs per type +tables allow unique IDs per type + +-> more details about this concept link:{l}/wiki/renderengine.html#EntryID%20TypedID%20TypedLookup[in the TiddlyWiki] Allocators From 962c789a056b77d9c7c756fe321bfca98e7d3f00 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 28 Dec 2011 03:19:46 +0100 Subject: [PATCH 329/496] a horrible hack to guess the size of boost::format ...without actually including the boost/format.hpp This saves us a lot of compilation time and debug code size --- src/lib/format-string.hpp | 8 +- src/lib/meta/size-trait.hpp | 144 ++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 src/lib/meta/size-trait.hpp diff --git a/src/lib/format-string.hpp b/src/lib/format-string.hpp index 53d3d278e..c164aa400 100644 --- a/src/lib/format-string.hpp +++ b/src/lib/format-string.hpp @@ -45,10 +45,10 @@ #ifndef UTIL_FORMAT_STRING_H #define UTIL_FORMAT_STRING_H -//#include "lib/meta/trait.hpp" //#include "lib/symbol.hpp" //#include "lib/util.hpp" #include "lib/error.hpp" +#include "lib/meta/size-trait.hpp" #include <string> //#include <cstring> @@ -89,12 +89,14 @@ namespace util { class _Fmt : boost::noncopyable { - enum{ FORMATTER_SIZE = 100 }; - + /** size of an internal implementation Buffer */ + enum{ FORMATTER_SIZE = lib::meta::SizeTrait::BOOST_FORMAT }; + /** @internal buffer to hold a boost::format */ char formatter_[FORMATTER_SIZE]; + template<typename VAL, class SEL =void> struct Converter; diff --git a/src/lib/meta/size-trait.hpp b/src/lib/meta/size-trait.hpp new file mode 100644 index 000000000..7d615d55a --- /dev/null +++ b/src/lib/meta/size-trait.hpp @@ -0,0 +1,144 @@ +/* + SIZE-TRAIT.hpp - helpers and definitions to deal with the size of some known types + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef LIB_META_SIZE_TRAIT_H +#define LIB_META_SIZE_TRAIT_H + + + +#include <vector> +#include <string> + + +namespace lib { +namespace meta { + + + /** + * A collection of constants to describe the expected size + * of some known classes, without needing to include the + * respective headers. This is an optimisation to improve + * compilation times and/or reduce size of the generated + * object files in debug mode. To get those sizes computed + * in a fairly portable way, but without much overhead, + * we mimic the memory layout of "the real thing" + * with some reasonable simplifications: + * - the size of vectors doesn't really depend on the elements + * - our strings, streams and buffers use just simple chars + * + * \par Interface + * The purpose of this whole construction is to pull off + * some constants based on sizeof expressions: + * - the size of a string + * - the size of a vector + * - the size of a boost::format + * + * @warning this setup is quite fragile and directly relies + * on the implementation layout of the GNU STL and Boost. + * Whenever using this stuff, make sure to place an assertion + * somewhere down in the implementation level to check against + * the size of the real thing. + */ + class SizeTrait + { + //-------------------------------------mimicked-definitions-- + + typedef std::vector<size_t> Vector; + + struct CompatAllocator + : std::allocator<char> + { }; + + struct Locale + { + void* _M_impl; + }; + + template<class T> + struct Optional + { + bool m_initialized_; + T m_storage_; + }; + + enum IOS_Openmode + { + _S_app = 1L << 0, + _S_ate = 1L << 1, + _S_bin = 1L << 2, + _S_in = 1L << 3, + _S_out = 1L << 4, + _S_trunc = 1L << 5, + _S_ios_openmode_end = 1L << 16 + }; + + struct BasicStringbuf + { + char * _M_in_beg; + char * _M_in_cur; + char * _M_in_end; + char * _M_out_beg; + char * _M_out_cur; + char * _M_out_end; + + Locale _M_buf_locale; + }; + + struct BasicAltstringbuf + : BasicStringbuf + { + char * putend_; + bool is_allocated_; + IOS_Openmode mode_; + CompatAllocator alloc_; + }; + + struct BoostFormat + { + Vector items_; + Vector bound_; + int style_; + int cur_arg_; + int num_args_; + bool dumped_; + std::string prefix_; + unsigned char exceptions; + BasicAltstringbuf buf_; + Optional<Locale> loc_; + }; + //-------------------------------------mimicked-definitions-- + + + public:/* ===== Interface: size constants ===== */ + + enum { + STRING = sizeof(std::string) + , VECTOR = sizeof(std::vector<size_t>) + + , BOOST_FORMAT = sizeof(BoostFormat) + }; + }; + + +}} // namespace lib::meta +#endif From cd3c633fc064afd2736b4e65214a94307007abab Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 28 Dec 2011 05:44:57 +0100 Subject: [PATCH 330/496] STATIC_ASSERT to verify guessed size ... and we indeed had an error: vector<bool> is way larger than normal vectors! --- src/lib/format-string.cpp | 3 +++ src/lib/meta/size-trait.hpp | 23 ++++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/lib/format-string.cpp b/src/lib/format-string.cpp index c0e84fad1..56fab4a96 100644 --- a/src/lib/format-string.cpp +++ b/src/lib/format-string.cpp @@ -48,6 +48,7 @@ #include "lib/error.hpp" #include "lib/format-string.hpp" +#include <boost/static_assert.hpp> #include <boost/format.hpp> #include <iostream> @@ -66,6 +67,8 @@ namespace util { /** */ _Fmt::_Fmt (string formatString) { + BOOST_STATIC_ASSERT (sizeof(boost::format) <= FORMATTER_SIZE); + UNIMPLEMENTED ("create the embedded boost::format object"); } diff --git a/src/lib/meta/size-trait.hpp b/src/lib/meta/size-trait.hpp index 7d615d55a..1abbb046b 100644 --- a/src/lib/meta/size-trait.hpp +++ b/src/lib/meta/size-trait.hpp @@ -64,6 +64,7 @@ namespace meta { //-------------------------------------mimicked-definitions-- typedef std::vector<size_t> Vector; + typedef std::vector<bool> BVector; struct CompatAllocator : std::allocator<char> @@ -102,6 +103,8 @@ namespace meta { char * _M_out_end; Locale _M_buf_locale; + + virtual ~BasicStringbuf() { } }; struct BasicAltstringbuf @@ -115,12 +118,12 @@ namespace meta { struct BoostFormat { - Vector items_; - Vector bound_; - int style_; - int cur_arg_; - int num_args_; - bool dumped_; + Vector items_; + BVector bound_; // note: differs in size + int style_; + int cur_arg_; + int num_args_; + mutable bool dumped_; std::string prefix_; unsigned char exceptions; BasicAltstringbuf buf_; @@ -131,9 +134,11 @@ namespace meta { public:/* ===== Interface: size constants ===== */ - enum { - STRING = sizeof(std::string) - , VECTOR = sizeof(std::vector<size_t>) + enum { ALIGNMENT = sizeof(size_t) + + , STRING = sizeof(std::string) + , VECTOR = sizeof(Vector) + , BVECTOR = sizeof(BVector) , BOOST_FORMAT = sizeof(BoostFormat) }; From dbb63ffc081c20ac42a48238434d2c824da51ffb Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 28 Dec 2011 06:17:26 +0100 Subject: [PATCH 331/496] perfer STATIC_ASSERT to check for suitable placement-New buffer size Still incomplete, but already this small change detected an error in the output-designation. Cough. --- src/lib/opaque-holder.hpp | 21 +++++++++++++-------- src/lib/polymorphic-value.hpp | 4 +++- src/proc/mobject/output-designation.hpp | 8 +++++--- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/lib/opaque-holder.hpp b/src/lib/opaque-holder.hpp index 646c24b45..d19ecfcc8 100644 --- a/src/lib/opaque-holder.hpp +++ b/src/lib/opaque-holder.hpp @@ -73,6 +73,7 @@ #include "lib/util.hpp" #include <boost/noncopyable.hpp> +#include <boost/static_assert.hpp> namespace lib { @@ -266,7 +267,8 @@ namespace lib { explicit Buff (SUB const& obj) { - REQUIRE (siz >= sizeof(SUB)); + BOOST_STATIC_ASSERT (siz >= sizeof(SUB)); + new(Buffer::ptr()) SUB (obj); } @@ -613,6 +615,8 @@ namespace lib { void placeDefault() { + BOOST_STATIC_ASSERT (siz >= sizeof(DEFAULT)); + new(&buf_) DEFAULT(); } @@ -640,13 +644,14 @@ namespace lib { destroy(); \ try \ { \ - REQUIRE (siz >= sizeof(TY)); \ - return *new(&buf_) _CTOR_CALL_; \ - } \ - catch (...) \ - { \ - placeDefault(); \ - throw; \ + BOOST_STATIC_ASSERT (siz >= sizeof(TY));\ + \ + return *new(&buf_) _CTOR_CALL_; \ + } \ + catch (...) \ + { \ + placeDefault(); \ + throw; \ } diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp index a9b92bc5e..2213a67a3 100644 --- a/src/lib/polymorphic-value.hpp +++ b/src/lib/polymorphic-value.hpp @@ -146,6 +146,7 @@ #include "lib/meta/duck-detector.hpp" #include <boost/utility/enable_if.hpp> +#include <boost/static_assert.hpp> namespace lib { @@ -384,7 +385,8 @@ namespace lib { template<class IMP> PolymorphicValue (IMP*) { - REQUIRE (siz >= sizeof(IMP)); + BOOST_STATIC_ASSERT (siz >= sizeof(IMP)); + new(&buf_) IMP(); } diff --git a/src/proc/mobject/output-designation.hpp b/src/proc/mobject/output-designation.hpp index d9b24dc6e..ab5c3ca55 100644 --- a/src/proc/mobject/output-designation.hpp +++ b/src/proc/mobject/output-designation.hpp @@ -103,10 +103,12 @@ namespace mobject { }; private: - enum { - SPEC_SIZ = mp::maxSize< + enum + { VTABLE = sizeof(size_t) + , SPEC_SIZ = VTABLE + + mp::maxSize< mp::Types< PID, lumiera_uid, uint>::List>::value - }; + }; typedef lib::OpaqueHolder<TargetSpec, SPEC_SIZ> SpecBuff; From 6c95c1b0e48e87df10f38b7ca811b3d41bde6988 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 28 Dec 2011 06:40:51 +0100 Subject: [PATCH 332/496] format-frontend: fill in missing bits of the implementation --- src/lib/format-string.cpp | 24 ++++++++++++++++++++---- src/lib/format-string.hpp | 9 +++++---- tests/lib/format-string-test.cpp | 4 ++-- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/lib/format-string.cpp b/src/lib/format-string.cpp index 56fab4a96..4db936d6e 100644 --- a/src/lib/format-string.cpp +++ b/src/lib/format-string.cpp @@ -58,8 +58,15 @@ namespace util { + using boost::format; + namespace { // implementation details... + inline boost::format& + accessImpl (char* buffer) + { + return reinterpret_cast<boost::format&> (*buffer); + } }//(End) implementation details @@ -68,8 +75,13 @@ namespace util { _Fmt::_Fmt (string formatString) { BOOST_STATIC_ASSERT (sizeof(boost::format) <= FORMATTER_SIZE); - - UNIMPLEMENTED ("create the embedded boost::format object"); + + new(formatter_) boost::format(formatString); + } + + _Fmt::~_Fmt () + { + accessImpl(formatter_).~format(); } @@ -78,7 +90,7 @@ namespace util { void _Fmt::pushParameter (VAL const& val) { - UNIMPLEMENTED ("feed the parameter to the embedded formatter"); + accessImpl(formatter_) % val; } template<typename VAL> @@ -97,10 +109,14 @@ namespace util { template void _Fmt::pushParameter(string const&); template void _Fmt::pushParameter(int const&); template void _Fmt::pushParameter(uint const&); + template void _Fmt::pushParameter(float const&); + template void _Fmt::pushParameter(double const&); template void _Fmt::pushParameter(const string * const); template void _Fmt::pushParameter(const int * const); template void _Fmt::pushParameter(const uint * const); + template void _Fmt::pushParameter(const float * const); + template void _Fmt::pushParameter(const double * const); @@ -108,7 +124,7 @@ namespace util { /** */ _Fmt::operator string() const { - UNIMPLEMENTED ("forward to the embedded boost::format object"); + return accessImpl(formatter_).str(); } diff --git a/src/lib/format-string.hpp b/src/lib/format-string.hpp index c164aa400..78c51aa9b 100644 --- a/src/lib/format-string.hpp +++ b/src/lib/format-string.hpp @@ -51,9 +51,7 @@ #include "lib/meta/size-trait.hpp" #include <string> -//#include <cstring> #include <typeinfo> -//#include <boost/lexical_cast.hpp> #include <boost/noncopyable.hpp> #include <boost/utility/enable_if.hpp> @@ -94,7 +92,7 @@ namespace util { /** @internal buffer to hold a boost::format */ - char formatter_[FORMATTER_SIZE]; + mutable char formatter_[FORMATTER_SIZE]; template<typename VAL, class SEL =void> @@ -107,6 +105,7 @@ namespace util { public: + ~_Fmt (); _Fmt (string formatString); operator string() const; @@ -184,6 +183,8 @@ namespace util { template<> struct _can_forward<string> { enum{ value = true }; }; template<> struct _can_forward<int> { enum{ value = true }; }; template<> struct _can_forward<uint> { enum{ value = true }; }; + template<> struct _can_forward<float> { enum{ value = true }; }; + template<> struct _can_forward<double> { enum{ value = true }; }; @@ -222,7 +223,7 @@ namespace util { static string prepare (VAL const&) { - UNIMPLEMENTED ("get type string"); + return string("«")+typeid(VAL).name()+"»"; } }; diff --git a/tests/lib/format-string-test.cpp b/tests/lib/format-string-test.cpp index 79bce5f1b..b304c81ed 100644 --- a/tests/lib/format-string-test.cpp +++ b/tests/lib/format-string-test.cpp @@ -73,12 +73,12 @@ namespace test { void check_simpleInvocation () { - string formatted = _Fmt("--format-template--int=%+04d--double=%5.2f--string=%-10s--") + string formatted = _Fmt("--format-template--int=%04d--double=%+5.2f--string=%-10s--") % 12 % 1.228 % "Lumiera"; cout << formatted << endl; - CHECK (formatted == "--format-template--int=0012--double= 1.23--string=Lumiera --"); + CHECK (formatted == "--format-template--int=0012--double=+1.23--string=Lumiera --"); } From 7efff377d00f3e23b2c611f0f6d216a275f05ff5 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 30 Dec 2011 03:45:51 +0100 Subject: [PATCH 333/496] use separate test group for metaprogramming tests --- tests/lib/format-string-test.cpp | 2 +- tests/lib/meta/duck-detector-test.cpp | 2 +- tests/lib/meta/type-tuple-test.cpp | 2 +- tests/lib/meta/typelist-util-test.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/lib/format-string-test.cpp b/tests/lib/format-string-test.cpp index b304c81ed..e87fe8739 100644 --- a/tests/lib/format-string-test.cpp +++ b/tests/lib/format-string-test.cpp @@ -300,7 +300,7 @@ namespace test { } }; - LAUNCHER (FormatString_test, "unit common"); + LAUNCHER (FormatString_test, "unit meta"); }} // namespace util::test diff --git a/tests/lib/meta/duck-detector-test.cpp b/tests/lib/meta/duck-detector-test.cpp index 064ed9041..7eade48ed 100644 --- a/tests/lib/meta/duck-detector-test.cpp +++ b/tests/lib/meta/duck-detector-test.cpp @@ -90,7 +90,7 @@ namespace test{ /** Register this test class... */ - LAUNCHER (DuckDetector_test, "unit common"); + LAUNCHER (DuckDetector_test, "unit meta"); diff --git a/tests/lib/meta/type-tuple-test.cpp b/tests/lib/meta/type-tuple-test.cpp index 8589b322a..0bfa8e3a9 100644 --- a/tests/lib/meta/type-tuple-test.cpp +++ b/tests/lib/meta/type-tuple-test.cpp @@ -451,7 +451,7 @@ namespace test { /** Register this test class... */ - LAUNCHER (TypeTuple_test, "unit common"); + LAUNCHER (TypeTuple_test, "unit meta"); diff --git a/tests/lib/meta/typelist-util-test.cpp b/tests/lib/meta/typelist-util-test.cpp index 66a2112c0..576167a40 100644 --- a/tests/lib/meta/typelist-util-test.cpp +++ b/tests/lib/meta/typelist-util-test.cpp @@ -76,7 +76,7 @@ namespace test { /** Register this test class... */ - LAUNCHER (TypeListUtil_test, "unit common"); + LAUNCHER (TypeListUtil_test, "unit meta"); From e054c272b6c1d484146b6affb309615da8b8a928 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 30 Dec 2011 03:45:10 +0100 Subject: [PATCH 334/496] research: detecting the possibility of a string conversion find out about the corner cases of this simplistic implementation --- research/try.cpp | 91 +++++++++++++-- src/lib/format-string.hpp | 58 +++++----- src/lib/meta/util.hpp | 35 +++++- tests/40components.tests | 5 + tests/lib/meta/meta-utils-test.cpp | 171 +++++++++++++++++++++++++++++ 5 files changed, 321 insertions(+), 39 deletions(-) create mode 100644 tests/lib/meta/meta-utils-test.cpp diff --git a/research/try.cpp b/research/try.cpp index 7b73f4c1f..763f90caa 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -21,30 +21,101 @@ // 1/11 - how to fetch the path of the own executable -- at least under Linux? // 10/11 - simple demo using a pointer and a struct // 11/11 - using the boost random number generator(s) +// 12/11 - how to detect if string conversion is possible? +#include "lib/util.hpp" + #include <iostream> -#include <boost/lexical_cast.hpp> -#include <boost/random/linear_congruential.hpp> - -using boost::lexical_cast; +#include <string> +using std::string; using std::cout; using std::endl; +typedef char Yes_t; +struct No_t { char more_than_one[4]; }; + + +template<typename TY> +struct _can_convertToString + { + static TY & probe(); + + static Yes_t check(string); + static No_t check(...); + + public: + static const bool value = (sizeof(Yes_t)==sizeof(check(probe()))); + }; + + +class SubString + : public string + { + public: + SubString() : string("sublunar") { } + }; + +class Something { }; + +struct SomehowStringy + { + operator string() { return "No such thing"; } + }; + +struct SomehowSub + { + operator SubString() { return SubString(); } + }; + +class SomehowSubSub + : public SomehowSub + { + }; + + + + +template<typename TY> +bool +investigate (TY const&) + { + return _can_convertToString<TY>::value; + } + +#define SHOW_CHECK(_EXPR_) cout << STRINGIFY(_EXPR_) << "\t : " << (investigate(_EXPR_)? "Yes":"No") << endl; int -main (int cnt, char* argv[]) +main (int, char**) { - int32_t seed = (2 == cnt)? lexical_cast<int32_t> (argv[1]) : 42; + SHOW_CHECK (string("nebbich")); + SHOW_CHECK ("gurks"); + SHOW_CHECK (23.34); + SHOW_CHECK (23); - boost::rand48 ranGen(seed); + string urgs("urgs"); + string & urgs_ref (urgs); + string const& urgs_const_ref (urgs); + string * urgs_ptr = &urgs; - cout << "seed = "<< seed << endl; - for (uint i=0; i< 100; ++i) - cout << ranGen() % CHAR_MAX <<"__"; + SHOW_CHECK (urgs_ref); + SHOW_CHECK (urgs_const_ref); + SHOW_CHECK (*urgs_ptr); + + SubString sub; + Something thing; + const SomehowStringy stringy = SomehowStringy(); + SomehowSubSub subsub; + SubString const& subRef(subsub); + + SHOW_CHECK (sub); + SHOW_CHECK (thing); + SHOW_CHECK (stringy); + SHOW_CHECK (subsub); + SHOW_CHECK (subRef); cout << "\n.gulp.\n"; diff --git a/src/lib/format-string.hpp b/src/lib/format-string.hpp index 78c51aa9b..b1a8ce44b 100644 --- a/src/lib/format-string.hpp +++ b/src/lib/format-string.hpp @@ -45,9 +45,8 @@ #ifndef UTIL_FORMAT_STRING_H #define UTIL_FORMAT_STRING_H -//#include "lib/symbol.hpp" -//#include "lib/util.hpp" #include "lib/error.hpp" +#include "lib/meta/util.hpp" #include "lib/meta/size-trait.hpp" #include <string> @@ -73,7 +72,6 @@ namespace std { // forward declaration to avoid including <iostream> namespace util { using boost::enable_if; -//using util::isnil; using std::string; @@ -95,11 +93,13 @@ namespace util { mutable char formatter_[FORMATTER_SIZE]; + /** helper to prepare parameters for inclusion */ template<typename VAL, class SEL =void> struct Converter; template<typename VAL> void pushParameter (VAL const&); + template<typename VAL> void pushParameter (const VAL * const); @@ -108,7 +108,7 @@ namespace util { ~_Fmt (); _Fmt (string formatString); - operator string() const; + operator string() const; ///< get the formatted result template<typename VAL> _Fmt& @@ -136,14 +136,13 @@ namespace util { /* ===== forwarding into the implementation ====== */ - /** The percent operator (\c '%' ) is used do feed - * additional parameter values to be included into - * the formatted result, at the positions marked - * within the format string. + /** The percent operator (\c '%' ) is used do feed parameter values + * to be included into the formatted result, at the positions marked + * by printf-style placeholders within the format string. * * \par type specific treatment * Basic types (numbers, chars, strings) are passed to the implementation - * (= boost::format) literally. For custom types, we try to use an custom + * (= boost::format) literally. For custom types, we try to use a custom * string conversion, if applicable. Any other type gets just translated * into a type-ID (using the mangled RTTI info). In case of errors during * the conversion, a string representation of the error is returned @@ -163,30 +162,31 @@ namespace util { namespace { // helpers to pick a suitable specialisation.... - template<typename X> - struct _can_convertToString - { - enum{ value = false }; - }; - - /** * by default we don't allow to * treat any types directly by boost::format. * As fallback we rather just produce a type-ID */ template<typename X> - struct _can_forward { enum{ value = false };}; + struct _shall_forward { enum{ value = false };}; /* the following definitions enable some basic types * to be forwarded to boost::format literally */ - template<> struct _can_forward<string> { enum{ value = true }; }; - template<> struct _can_forward<int> { enum{ value = true }; }; - template<> struct _can_forward<uint> { enum{ value = true }; }; - template<> struct _can_forward<float> { enum{ value = true }; }; - template<> struct _can_forward<double> { enum{ value = true }; }; + template<> struct _shall_forward<string> { enum{ value = true }; }; + template<> struct _shall_forward<int> { enum{ value = true }; }; + template<> struct _shall_forward<uint> { enum{ value = true }; }; + template<> struct _shall_forward<float> { enum{ value = true }; }; + template<> struct _shall_forward<double> { enum{ value = true }; }; + template<typename X> + struct _shall_convert_toString + { + enum{ value = ! _shall_forward<X>::value + && lib::meta::_can_convertToString<X>::value + }; + }; + inline string _log_and_stringify (std::exception const& ex) @@ -215,8 +215,10 @@ namespace util { + /* === explicit specialisations to control the kind of conversion === */ + /** default/fallback: just indicate the (static) type */ template<typename VAL, class SEL> struct _Fmt::Converter { @@ -227,8 +229,9 @@ namespace util { } }; + /** some custom types explicitly provide string representation */ template<typename VAL> - struct _Fmt::Converter<VAL, typename enable_if< _can_convertToString<VAL> >::type> + struct _Fmt::Converter<VAL, typename enable_if< _shall_convert_toString<VAL> >::type> { static string prepare (VAL const& val) @@ -245,8 +248,10 @@ namespace util { } }; + /** some basic types are directly forwarded down to the implementation; + * @note this requires explicit specialisations in format-string.cpp */ template<typename VAL> - struct _Fmt::Converter<VAL, typename enable_if< _can_forward<VAL> >::type> + struct _Fmt::Converter<VAL, typename enable_if< _shall_forward<VAL> >::type> { static VAL const& prepare (VAL const& val) @@ -256,7 +261,7 @@ namespace util { }; template<typename VAL> - struct _Fmt::Converter<VAL*, typename enable_if< _can_forward<VAL> >::type> + struct _Fmt::Converter<VAL*, typename enable_if< _shall_forward<VAL> >::type> { static const VAL * prepare (const VAL * const pVal) @@ -302,5 +307,4 @@ namespace util { } // namespace util - -#endif /*UTIL_FORMAT_STRING_H*/ +#endif diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index f32cbb1ed..f305c46bb 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -21,10 +21,22 @@ */ +/** @file util.hpp + ** Simple and lightweight helpers for metaprogramming and type detection. + ** This header is a collection of very basic type detection and metaprogramming utilities. + ** @warning indirectly, this header gets included into the majority of compilation units. + ** Avoid anything here which increases compilation times or adds much debugging info. + ** + ** @see MetaUtils_test + ** @see trait.hpp + ** @see typelist.hpp + ** + */ + + #ifndef LIB_META_UTIL_H #define LIB_META_UTIL_H - namespace lib { namespace meta { @@ -33,7 +45,7 @@ namespace meta { /* types for figuring out the overload resolution chosen by the compiler */ typedef char Yes_t; - struct No_t { char padding[8]; }; + struct No_t { char more_than_one[4]; }; @@ -56,6 +68,25 @@ namespace meta { }; + /** detect possibility of a conversion to string. + * Naive implementation just trying a the direct conversion. + * The embedded constant #value will be true in case this succeeds. + * Might fail in more tricky situations (references, const, volatile) + * @see string-util.hpp more elaborate solution including lexical_cast + */ + template<typename T> + struct _can_convertToString + { + static T & probe(); + + static Yes_t check(std::string); + static No_t check(...); + + public: + static const bool value = (sizeof(Yes_t)==sizeof(check(probe()))); + }; + + /** 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 3ad2bc07d..62827efac 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -394,6 +394,11 @@ return: 0 END +TEST "metaprogramming helpers" Metautils_test <<END +return: 0 +END + + TEST "NIL object singleton" NullValue_test <<END return: 0 END diff --git a/tests/lib/meta/meta-utils-test.cpp b/tests/lib/meta/meta-utils-test.cpp new file mode 100644 index 000000000..3b2d84bb3 --- /dev/null +++ b/tests/lib/meta/meta-utils-test.cpp @@ -0,0 +1,171 @@ +/* + MetaUtils(Test) - check some simple type trait helpers + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/meta/util.hpp" +#include "lib/meta/typelist.hpp" + +#include <string> + + +namespace lib { +namespace meta { +namespace test { + + using std::string; + + + + + + + /************************************************************************* + * @test verify basic type trait and metaprogramming helpers. + * - marker types to tell which overload the compiler picks + * - simple trait to detect the possibility of a string conversion + * - trait to detect a typelist type + */ + class MetaUtils_test : public Test + { + void + run (Arg) + { + verify_basicAssumptions(); + + detect_stringConversion(); + detect_typeList(); + } + + + /** @test demonstrate / verify the + * basic type trait detection technique: + * By investigating the return type, we can + * figure out which overload the compiler picks.. + */ + void + verify_basicAssumptions() + { + CHECK (sizeof(Yes_t) != sizeof (No_t)); + + CHECK (sizeof(Yes_t) == sizeof (probe (1))); + CHECK (sizeof(Yes_t) == sizeof (probe (1L))); // conversion long -> int + CHECK (sizeof(Yes_t) == sizeof (probe ('a'))); // conversion char -> int + CHECK (sizeof(No_t) == sizeof (probe ("a"))); // char * can't be converted + } + + static Yes_t probe (int); + static No_t probe (...); + + + + //-------------------------------------------------TEST-types-- + class SubString : public string + { + public: + SubString() : string("sublunar") { } + }; + + class Something { }; + + struct SomehowStringy + { + operator string() { return "No such thing"; } + }; + + struct SomehowSubtle + { + operator SubString() { return SubString(); } + }; + + class SomehowSubSub : public SomehowSubtle { }; + //-------------------------------------------------TEST-types-- + + template<typename TY> + static bool + can_convert (TY const&) + { + return _can_convertToString<TY>::value; + } + + void + detect_stringConversion() + { + CHECK ( can_convert (string("inline string"))); + CHECK ( can_convert ("char literal")); + CHECK (!can_convert (23.34)); + CHECK (!can_convert (23)); + CHECK (!can_convert (1L)); + + string str("mhm"); + string & str_ref (str); + string const& str_const_ref (str); + string * str_ptr = &str; + + CHECK ( can_convert (str)); + CHECK ( can_convert (str_ref)); + CHECK ( can_convert (str_const_ref)); + CHECK ( can_convert (*str_ptr)); + CHECK (!can_convert (str_ptr)); + + SubString sub; + Something thing; + const SomehowStringy stringy = SomehowStringy(); + SomehowSubSub subsub; + SubString const& subRef(subsub); + + CHECK ( can_convert (sub)); + CHECK (!can_convert (thing)); + CHECK ( can_convert (stringy)); + CHECK ( can_convert (subsub)); + CHECK ( can_convert (subRef)); + } + + + + //-------------------------------------------------TEST-types-- + typedef Types< int + , uint + , int64_t + , uint64_t + >::List TheList; + + typedef Types< >::List EmptyList; + //-------------------------------------------------TEST-types-- + + + void + detect_typeList() + { + CHECK ( is_Typelist<TheList>::value); + CHECK ( is_Typelist<EmptyList>::value); + CHECK (!is_Typelist<Something>::value); + } + }; + + + /** Register this test class... */ + LAUNCHER (MetaUtils_test, "unit meta"); + + + +}}} // namespace lib::meta::test From 0d136e2703f69bc21822c98bb9a3055de3c50ef1 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 30 Dec 2011 05:13:27 +0100 Subject: [PATCH 335/496] define explicit specialisations for primitive types --- src/lib/format-string.cpp | 20 ++++++++++++++++++ src/lib/format-string.hpp | 15 +++++++++++--- src/lib/meta/util.hpp | 2 +- tests/lib/format-string-test.cpp | 35 ++++++++++++++++---------------- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/lib/format-string.cpp b/src/lib/format-string.cpp index 4db936d6e..d0804c77f 100644 --- a/src/lib/format-string.cpp +++ b/src/lib/format-string.cpp @@ -103,18 +103,38 @@ namespace util { pushParameter(string("(null)")); } + template<> + void + _Fmt::pushParameter (const char * const cString) + { + pushParameter (cString); + } + + /* ===== explicitly supported =================== */ template void _Fmt::pushParameter(string const&); + template void _Fmt::pushParameter(char const&); + template void _Fmt::pushParameter(uchar const&); template void _Fmt::pushParameter(int const&); template void _Fmt::pushParameter(uint const&); + template void _Fmt::pushParameter(short const&); + template void _Fmt::pushParameter(ushort const&); + template void _Fmt::pushParameter(int64_t const&); + template void _Fmt::pushParameter(uint64_t const&); template void _Fmt::pushParameter(float const&); template void _Fmt::pushParameter(double const&); + template void _Fmt::pushParameter(void * const&); template void _Fmt::pushParameter(const string * const); + template void _Fmt::pushParameter(const uchar * const); template void _Fmt::pushParameter(const int * const); template void _Fmt::pushParameter(const uint * const); + template void _Fmt::pushParameter(const short * const); + template void _Fmt::pushParameter(const ushort * const); + template void _Fmt::pushParameter(const int64_t * const); + template void _Fmt::pushParameter(const uint64_t * const); template void _Fmt::pushParameter(const float * const); template void _Fmt::pushParameter(const double * const); diff --git a/src/lib/format-string.hpp b/src/lib/format-string.hpp index b1a8ce44b..d7eb1fde2 100644 --- a/src/lib/format-string.hpp +++ b/src/lib/format-string.hpp @@ -71,10 +71,11 @@ namespace std { // forward declaration to avoid including <iostream> namespace util { + typedef unsigned char uchar; + using boost::enable_if; using std::string; - - + @@ -173,17 +174,25 @@ namespace util { /* the following definitions enable some basic types * to be forwarded to boost::format literally */ template<> struct _shall_forward<string> { enum{ value = true }; }; + template<> struct _shall_forward<char> { enum{ value = true }; }; + template<> struct _shall_forward<uchar> { enum{ value = true }; }; template<> struct _shall_forward<int> { enum{ value = true }; }; template<> struct _shall_forward<uint> { enum{ value = true }; }; + template<> struct _shall_forward<short> { enum{ value = true }; }; + template<> struct _shall_forward<ushort> { enum{ value = true }; }; + template<> struct _shall_forward<int64_t> { enum{ value = true }; }; + template<> struct _shall_forward<uint64_t>{ enum{ value = true }; }; template<> struct _shall_forward<float> { enum{ value = true }; }; template<> struct _shall_forward<double> { enum{ value = true }; }; + template<> struct _shall_forward<void*> { enum{ value = true }; }; template<typename X> struct _shall_convert_toString { enum{ value = ! _shall_forward<X>::value - && lib::meta::_can_convertToString<X>::value + && lib::meta::can_convertToString<X>::value + && !lib::meta::is_sameType<X,char*>::value }; }; diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index f305c46bb..91724943f 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -75,7 +75,7 @@ namespace meta { * @see string-util.hpp more elaborate solution including lexical_cast */ template<typename T> - struct _can_convertToString + struct can_convertToString { static T & probe(); diff --git a/tests/lib/format-string-test.cpp b/tests/lib/format-string-test.cpp index e87fe8739..acc39eb7e 100644 --- a/tests/lib/format-string-test.cpp +++ b/tests/lib/format-string-test.cpp @@ -89,8 +89,9 @@ namespace test { _Fmt formatter (formatString); uint val = rand() % 100; + void *pt = &val; - formatter % &val; + formatter % &pt; formatter % val; cout << formatter << endl; @@ -102,7 +103,7 @@ namespace test { { int i(-12); CHECK (_Fmt("%d") % i == "-12" ); - CHECK (_Fmt("%6d") % i == "- 12" ); + CHECK (_Fmt("%6d") % i == " -12" ); CHECK (_Fmt("%-6d") % i == "-12 " ); CHECK (_Fmt("%+-6d") % -i == "+12 " ); CHECK (_Fmt("%+06d") % -i == "+00012" ); @@ -242,8 +243,8 @@ namespace test { verify_errorHandling () { cout << _Fmt("__nix_") % 1 % 2 << endl; - cout << _Fmt("__%d__") << endl; - cout << _Fmt("__%d__") % 1 << endl; + cout << _Fmt("__%d__") << endl; + cout << _Fmt("__%d__") % 1 << endl; cout << _Fmt("__%d__") % 1 % 2 << endl; cout << _Fmt("__%d__") % "dirt" << endl; @@ -255,19 +256,19 @@ namespace test { void verify_pointerHandling () { - int i(-12); int * pi = & i; - uint u(12); uint * pu = & u; - short sh(-123); short * psh = & sh; - ushort ush(123); ushort *push = &ush; - long l(-123); long * pl = & l; - ulong ul(123); ulong * pul = & ul; - int64_t ll(5e+9); int64_t * pll = & ll; - uint64_t ull(ll); uint64_t *pull = &ull; - float f(12.34); float * pf = & f; - double d(-12.34); double * pd = & d; - char c(0x40); char * pc = & c; - unsigned char uc(0xff); unsigned char * puc = & uc; - string str("Lumiera"); string *pstr = &str; + int i(-12); int * pi = & i; + uint u(12); uint * pu = & u; + short sh(-123); short * psh = & sh; + ushort ush(123); ushort * push = &ush; + long l(-123); long * pl = & l; + ulong ul(123); ulong * pul = & ul; + int64_t ll(5e+9); int64_t * pll = & ll; + uint64_t ull(ll); uint64_t * pull = &ull; + float f(12.34); float * pf = & f; + double d(-12.34); double * pd = & d; + char c(0x40); char * pc = & c; + uchar uc(0xff); uchar * puc = & uc; + string str("Lumiera"); string * pstr = &str; CHECK (_Fmt("%d") % i == _Fmt("%d") % pi); CHECK (_Fmt("%d") % u == _Fmt("%d") % pu); From e838fb97990ae4b66af073699d745ccbecadb624 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 30 Dec 2011 06:10:47 +0100 Subject: [PATCH 336/496] document and cover various formatting special cases --- src/lib/format-string.cpp | 25 +++++++++-- tests/lib/format-string-test.cpp | 71 +++++++++++++++++------------- tests/lib/meta/meta-utils-test.cpp | 2 +- 3 files changed, 64 insertions(+), 34 deletions(-) diff --git a/src/lib/format-string.cpp b/src/lib/format-string.cpp index d0804c77f..d11f63bc2 100644 --- a/src/lib/format-string.cpp +++ b/src/lib/format-string.cpp @@ -85,7 +85,16 @@ namespace util { } - /** */ + /** @internal access points for the frontend, + * allowing to push a parameter value down into + * the implementation for the actual formatting. + * @note we need to generate instantiations of this template function + * explicitly for all basic types to be supported for direct handling, + * otherwise we'll get linker errors. Lumiera uses the ``inclusion model'' + * for template instantiation, which means there is no compiler magic + * involved and a template function either needs to be \em defined in + * a header or explicitly instantiated in some translation unit. + */ template<typename VAL> void _Fmt::pushParameter (VAL const& val) @@ -141,17 +150,27 @@ namespace util { - /** */ + /** @remarks usually the _Fmt helper is used inline + * at places where a string is expected. The '%' operator + * used to fed parameters has a higher precedence than the + * assignment or comparison operators, ensuring that all parameters + * are evaluated and formatted prior to receiving the formatted result + */ _Fmt::operator string() const { return accessImpl(formatter_).str(); } + /** send the formatted buffer directly to the output stream. + * @note this is more efficient than creating a string and outputting that, + * because boost::format internally uses a stringstream to generate + * the formatted representation, relying on the C++ output framework + */ std::ostream& operator<< (std::ostream& os, _Fmt const& fmt) { - return os << string(fmt); + return os << accessImpl(fmt.formatter_); } diff --git a/tests/lib/format-string-test.cpp b/tests/lib/format-string-test.cpp index acc39eb7e..530339ec5 100644 --- a/tests/lib/format-string-test.cpp +++ b/tests/lib/format-string-test.cpp @@ -73,12 +73,12 @@ namespace test { void check_simpleInvocation () { - string formatted = _Fmt("--format-template--int=%04d--double=%+5.2f--string=%-10s--") + string formatted = _Fmt("--format-template--int=%04d--double=%+5.2f--string=%-9s--") % 12 % 1.228 % "Lumiera"; cout << formatted << endl; - CHECK (formatted == "--format-template--int=0012--double=+1.23--string=Lumiera --"); + CHECK (formatted == "--format-template--int=0012--double=+1.23--string=Lumiera --"); } @@ -91,7 +91,7 @@ namespace test { uint val = rand() % 100; void *pt = &val; - formatter % &pt; + formatter % pt; formatter % val; cout << formatter << endl; @@ -107,66 +107,77 @@ namespace test { CHECK (_Fmt("%-6d") % i == "-12 " ); CHECK (_Fmt("%+-6d") % -i == "+12 " ); CHECK (_Fmt("%+06d") % -i == "+00012" ); - CHECK (_Fmt("%+06X") % -i == "+0000D" ); - CHECK (_Fmt("%+#X") % -i == "+Dh" ); + CHECK (_Fmt("%06X") % -i == "00000C" ); + CHECK (_Fmt("%#x") % -i == "0xc" ); uint u(12); CHECK (_Fmt("%d") % u == "12" ); CHECK (_Fmt("%6d") % u == " 12" ); CHECK (_Fmt("%-6d") % u == "12 " ); - CHECK (_Fmt("%+-6d") % u == "+12 " ); - CHECK (_Fmt("%+06d") % u == "+00012" ); + CHECK (_Fmt("%-+6d") % u == "12 " ); + CHECK (_Fmt("%+06d") % u == "000012" ); short sh(-123); - CHECK (_Fmt("%6d") % sh == "- 123" ); + CHECK (_Fmt("%6d") % sh == " -123" ); ushort ush(123); CHECK (_Fmt("%6d") % ush == " 123" ); long l(-123); - CHECK (_Fmt("%6d") % l == "- 123" ); + CHECK (_Fmt("%6d") % l == " -123" ); ulong ul(123); CHECK (_Fmt("%6d") % ul == " 123" ); int64_t ll(5e+9); CHECK (_Fmt("%d") % ll == "5000000000" ); - CHECK (_Fmt("%10.3d") %ll == " 500" ); - CHECK (_Fmt("%10.3E") %ll == " 5.000E9" ); + CHECK (_Fmt("%5.3d") %ll == "5000000000" ); + CHECK (_Fmt("%10.3e") %ll == "5000000000" ); uint64_t ull(ll); CHECK (_Fmt("%d") % ull == "5000000000" ); float f(12.34); - CHECK (_Fmt("%g") % f == "12.34" ); - CHECK (_Fmt("%d") % f == "12" ); - CHECK (_Fmt("%e") % f == "12.34e0"); + CHECK (_Fmt("%g") % f == "12.34" ); + CHECK (_Fmt("%d") % f == "12.34" ); + CHECK (_Fmt("%.3f") % f == "12.340" ); + CHECK (_Fmt("%.1e") % f == "1.2e+01"); + CHECK (_Fmt("%.0f") % f == "12" ); double d(-12.34); CHECK (_Fmt("%g") % d == "-12.34" ); char c(0x40); - CHECK (_Fmt("%d") % c == "64" ); - CHECK (_Fmt("%x") % c == "40" ); - CHECK (_Fmt("%o") % c == "100" ); - CHECK (_Fmt("%c") % c == "@" ); - CHECK (_Fmt("%s") % c == "@" ); + CHECK (_Fmt("%d") % c == "@" ); + CHECK (_Fmt("%x") % c == "@" ); + CHECK (_Fmt("%o") % c == "@" ); + CHECK (_Fmt("%c") % c == "@" ); + CHECK (_Fmt("%s") % c == "@" ); + CHECK (_Fmt("%d") % short(c) == "64" ); + CHECK (_Fmt("%x") % short(c) == "40" ); + CHECK (_Fmt("%o") % short(c) == "100" ); + CHECK (_Fmt("%c") % short(c) == "6" ); + CHECK (_Fmt("%s") % short(c) == "64" ); - unsigned char uc(0xff); - CHECK (_Fmt("%d") % uc == "255" ); - CHECK (_Fmt("%x") % uc == "ff" ); - CHECK (_Fmt("%X") % uc == "FF" ); - CHECK (_Fmt("%c") % uc == "\xFF" ); - CHECK (_Fmt("%s") % uc == "\xFF" ); + uchar uc(0xff); + CHECK (_Fmt("%d") % uint(uc) == "255" ); + CHECK (_Fmt("%x") % uint(uc) == "ff" ); + CHECK (_Fmt("%X") % uint(uc) == "FF" ); + CHECK (_Fmt("%c") % uint(uc) == "2" ); + CHECK (_Fmt("%s") % uint(uc) == "255" ); + CHECK (_Fmt("%d") % uc == "\xFF"); + CHECK (_Fmt("%x") % uc == "\xFF"); + CHECK (_Fmt("%X") % uc == "\xFF"); + CHECK (_Fmt("%c") % uc == "\xFF"); + CHECK (_Fmt("%s") % uc == "\xFF"); c = char(uc); - CHECK (_Fmt("%d") % uc == "-1" ); - CHECK (_Fmt("%c") % uc == "\xFF" ); + CHECK (_Fmt("%c") % c == "\xFF"); string str("Lumiera"); CHECK (_Fmt("%s") % str == "Lumiera" ); CHECK (_Fmt("%10s") % str == " Lumiera" ); - CHECK (_Fmt("%8.4s") %str == " Lumi" ); + CHECK (_Fmt("%7.4s") %str == " Lumi" ); CHECK (_Fmt("%10c") % str == " L" ); } @@ -226,12 +237,12 @@ namespace test { CHECK (contains (mangledType, "Silent")); CHECK (_Fmt("!!%s!!") % v == "!!Number-013!!"); - CHECK (_Fmt("!!%s!!") % x == "!!unforeseen internal state (encountered Fantomas).!!"); + CHECK (_Fmt("!!%s!!") % x == "!!LUMIERA_ERROR_STATE:unforeseen state (encountered Fantomas).!!"); CHECK (contains (_Fmt("%s") % rs1, "Silent")); CHECK (contains (_Fmt("%s") % rs2, "Silent")); - CHECK (_Fmt("!!%s!!") % rv == "!!unforeseen internal state (encountered Fantomas).!!"); + CHECK (_Fmt("!!%s!!") % rv == "!!LUMIERA_ERROR_STATE:unforeseen state (encountered Fantomas).!!"); x.i_ = 42; CHECK (_Fmt("!!%s!!") % rv == "!!Number-042!!"); diff --git a/tests/lib/meta/meta-utils-test.cpp b/tests/lib/meta/meta-utils-test.cpp index 3b2d84bb3..50aa87a4c 100644 --- a/tests/lib/meta/meta-utils-test.cpp +++ b/tests/lib/meta/meta-utils-test.cpp @@ -104,7 +104,7 @@ namespace test { static bool can_convert (TY const&) { - return _can_convertToString<TY>::value; + return can_convertToString<TY>::value; } void From 35ed2dcf5cb26f24dd7dba1ebdd811895d3322c6 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 30 Dec 2011 20:20:11 +0100 Subject: [PATCH 337/496] add more robust error handling ...incl handling of secondary errors --- src/lib/format-string.cpp | 102 ++++++++++++++++++++++++++----- src/lib/format-string.hpp | 17 +++--- tests/lib/format-string-test.cpp | 8 ++- 3 files changed, 101 insertions(+), 26 deletions(-) diff --git a/src/lib/format-string.cpp b/src/lib/format-string.cpp index d11f63bc2..927200f30 100644 --- a/src/lib/format-string.cpp +++ b/src/lib/format-string.cpp @@ -46,6 +46,7 @@ #include "lib/error.hpp" +#include "lib/format-util.hpp" #include "lib/format-string.hpp" #include <boost/static_assert.hpp> @@ -68,15 +69,45 @@ namespace util { return reinterpret_cast<boost::format&> (*buffer); } + /** in case the formatting of a (primitive) value fails, + * we try to use a error indicator instead + */ + inline void + pushFailsafeReplacement (char* formatter, const char* errorMsg =NULL) + try { + string placeholder("<Error"); + if (errorMsg){ + placeholder += ": "; + placeholder += errorMsg; + } + placeholder += ">"; + + accessImpl(formatter) % placeholder; + } + ERROR_LOG_AND_IGNORE (progress, "Supplying placeholder for problematic format parameter") + + + inline void + suppressInsufficientArgumentErrors (char* formatter) + { + using namespace boost::io; + accessImpl(formatter).exceptions (all_error_bits ^ too_few_args_bit); + } + + }//(End) implementation details + + + /** */ _Fmt::_Fmt (string formatString) { BOOST_STATIC_ASSERT (sizeof(boost::format) <= FORMATTER_SIZE); new(formatter_) boost::format(formatString); + suppressInsufficientArgumentErrors (formatter_); } _Fmt::~_Fmt () @@ -86,8 +117,8 @@ namespace util { /** @internal access points for the frontend, - * allowing to push a parameter value down into - * the implementation for the actual formatting. + * allowing to push a parameter value down into the implementation + * for the actual formatting. * @note we need to generate instantiations of this template function * explicitly for all basic types to be supported for direct handling, * otherwise we'll get linker errors. Lumiera uses the ``inclusion model'' @@ -98,18 +129,38 @@ namespace util { template<typename VAL> void _Fmt::pushParameter (VAL const& val) - { - accessImpl(formatter_) % val; - } + try { + accessImpl(formatter_) % val; + } + + catch (boost::io::too_many_args& argErr) + { + WARN (progress, "Format: excess argument '%s' of type %s ignored." + , cStr(str(val)) + , cStr(tyStr(val))); + } + catch (std::exception& failure) + { + WARN (progress, "Format: Parameter '%s' causes problems: %s" + , cStr(str(val)) + , failure.what()); + pushFailsafeReplacement (formatter_, failure.what()); + } + catch (...) + { + WARN (progress, "Format: Unexpected problems accepting format parameter '%s'", cStr(str(val))); + pushFailsafeReplacement (formatter_); + } + template<typename VAL> void _Fmt::pushParameter (const VAL * const pVal) { if (pVal) - pushParameter(*pVal); + pushParameter (*pVal); else - pushParameter(string("(null)")); + pushParameter (string("<null>")); } template<> @@ -157,21 +208,44 @@ namespace util { * are evaluated and formatted prior to receiving the formatted result */ _Fmt::operator string() const - { - return accessImpl(formatter_).str(); - } + try { + return accessImpl(formatter_).str(); + } + + catch (std::exception& failure) + { + WARN (progress, "Format: Failure to receive formatted result: %s", failure.what()); + return "<formatting failure>"; + } + catch (...) + { + WARN (progress, "Format: Unexpected problems while formatting output."); + return "<unexpected problems>"; + } + /** send the formatted buffer directly to the output stream. * @note this is more efficient than creating a string and outputting that, - * because boost::format internally uses a stringstream to generate + * because boost::format internally uses a string-stream to generate * the formatted representation, relying on the C++ output framework */ std::ostream& operator<< (std::ostream& os, _Fmt const& fmt) - { - return os << accessImpl(fmt.formatter_); - } + try { + return os << accessImpl(fmt.formatter_); + } + + catch(std::exception& failure) + { + WARN (progress, "Format: Failure when outputting formatted result: %s", failure.what()); + return os << "<formatting failure>"; + } + catch(...) + { + WARN (progress, "Format: Unexpected problems while producing formatted output."); + return os << "<unexpected problems>"; + } diff --git a/src/lib/format-string.hpp b/src/lib/format-string.hpp index d7eb1fde2..af58a9bda 100644 --- a/src/lib/format-string.hpp +++ b/src/lib/format-string.hpp @@ -201,7 +201,11 @@ namespace util { _log_and_stringify (std::exception const& ex) { WARN (progress, "Error while invoking custom string conversion: %s", ex.what()); - return ex.what(); + try { + return string("<string conversion failed: ")+ex.what()+">"; + } + catch(...) { /* secondary errors ignored */ } + return "(formatting failure)"; } inline string @@ -209,15 +213,10 @@ namespace util { { const char* errID = lumiera_error(); if (errID) - { - ERROR (progress, "Unknown error while invoking custom string conversion. Lumiera error flag = %s", errID); - return string("Unknown error in string conversion. FLAG=")+errID; - } + ERROR (progress, "Unknown error while invoking custom string conversion. Lumiera error flag = %s", errID); else - { - ERROR (progress, "Unknown error while invoking custom string conversion. No Lumiera error flag set."); - return "Unknown error in string conversion"; - } + ERROR (progress, "Unknown error while invoking custom string conversion. No Lumiera error flag set."); + return "<Unknown error in string conversion>"; } }//(End) guards/helpers diff --git a/tests/lib/format-string-test.cpp b/tests/lib/format-string-test.cpp index 530339ec5..46cae95c1 100644 --- a/tests/lib/format-string-test.cpp +++ b/tests/lib/format-string-test.cpp @@ -237,12 +237,12 @@ namespace test { CHECK (contains (mangledType, "Silent")); CHECK (_Fmt("!!%s!!") % v == "!!Number-013!!"); - CHECK (_Fmt("!!%s!!") % x == "!!LUMIERA_ERROR_STATE:unforeseen state (encountered Fantomas).!!"); + CHECK (_Fmt("!!%s!!") % x == "!!<string conversion failed: LUMIERA_ERROR_STATE:unforeseen state (encountered Fantomas).>!!"); CHECK (contains (_Fmt("%s") % rs1, "Silent")); CHECK (contains (_Fmt("%s") % rs2, "Silent")); - CHECK (_Fmt("!!%s!!") % rv == "!!LUMIERA_ERROR_STATE:unforeseen state (encountered Fantomas).!!"); + CHECK (_Fmt("!!%s!!") % rv == "!!<string conversion failed: LUMIERA_ERROR_STATE:unforeseen state (encountered Fantomas).>!!"); x.i_ = 42; CHECK (_Fmt("!!%s!!") % rv == "!!Number-042!!"); @@ -258,6 +258,8 @@ namespace test { cout << _Fmt("__%d__") % 1 << endl; cout << _Fmt("__%d__") % 1 % 2 << endl; + const char* evil = NULL; + cout << _Fmt("__%d__") % evil << endl; cout << _Fmt("__%d__") % "dirt" << endl; cout << _Fmt("__%d__") % "1234" << endl; cout << _Fmt("__%d__") % "0xff" << endl; @@ -307,7 +309,7 @@ namespace test { pv = NULL; vv = NULL; - CHECK (_Fmt("__%s__") % pv == "__(null)__"); + CHECK (_Fmt("__%s__") % pv == "__<null>__"); CHECK (_Fmt("__%s__") % pv == "__0__"); } }; From c8f46f47c9ac544cc05ae062e5e47fd42081e12d Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 31 Dec 2011 01:30:07 +0100 Subject: [PATCH 338/496] handling of ptrs, first attempt --- src/lib/format-string.cpp | 118 +++++++++++++++++-------------- src/lib/format-string.hpp | 75 ++++++++++++-------- src/lib/meta/util.hpp | 30 ++++++++ tests/lib/format-string-test.cpp | 7 +- 4 files changed, 145 insertions(+), 85 deletions(-) diff --git a/src/lib/format-string.cpp b/src/lib/format-string.cpp index 927200f30..562fe10d1 100644 --- a/src/lib/format-string.cpp +++ b/src/lib/format-string.cpp @@ -69,10 +69,11 @@ namespace util { return reinterpret_cast<boost::format&> (*buffer); } + /** in case the formatting of a (primitive) value fails, * we try to use a error indicator instead */ - inline void + void pushFailsafeReplacement (char* formatter, const char* errorMsg =NULL) try { string placeholder("<Error"); @@ -87,6 +88,34 @@ namespace util { ERROR_LOG_AND_IGNORE (progress, "Supplying placeholder for problematic format parameter") + /** Core function: let boost::format handle a value */ + template<typename VAL> + void + doFormatParameter (char* formatter, VAL const& val) + try { + accessImpl(formatter) % val; + } + + catch (boost::io::too_many_args& argErr) + { + WARN (progress, "Format: excess argument '%s' of type %s ignored." + , cStr(str(val)) + , cStr(tyStr(val))); + } + catch (std::exception& failure) + { + WARN (progress, "Format: Parameter '%s' causes problems: %s" + , cStr(str(val)) + , failure.what()); + pushFailsafeReplacement (formatter, failure.what()); + } + catch (...) + { + WARN (progress, "Format: Unexpected problems accepting format parameter '%s'", cStr(str(val))); + pushFailsafeReplacement (formatter); + } + + inline void suppressInsufficientArgumentErrors (char* formatter) { @@ -128,75 +157,60 @@ namespace util { */ template<typename VAL> void - _Fmt::pushParameter (VAL const& val) - try { - accessImpl(formatter_) % val; - } - - catch (boost::io::too_many_args& argErr) - { - WARN (progress, "Format: excess argument '%s' of type %s ignored." - , cStr(str(val)) - , cStr(tyStr(val))); - } - catch (std::exception& failure) - { - WARN (progress, "Format: Parameter '%s' causes problems: %s" - , cStr(str(val)) - , failure.what()); - pushFailsafeReplacement (formatter_, failure.what()); - } - catch (...) - { - WARN (progress, "Format: Unexpected problems accepting format parameter '%s'", cStr(str(val))); - pushFailsafeReplacement (formatter_); - } - + _Fmt::pushParameter (const VAL val) + { + doFormatParameter (formatter_, val); + } template<typename VAL> void - _Fmt::pushParameter (const VAL * const pVal) + _Fmt::pushParameter (const VAL * pVal) { if (pVal) - pushParameter (*pVal); + doFormatParameter (formatter_, *pVal); else - pushParameter (string("<null>")); + doFormatParameter (formatter_, "<null>"); } template<> void - _Fmt::pushParameter (const char * const cString) + _Fmt::pushParameter (const char * cString) { - pushParameter (cString); + doFormatParameter (formatter_, cString? cString : "↯" ); + } + template<> + void + _Fmt::pushParameter (const void * address) + { + doFormatParameter (formatter_, address); } /* ===== explicitly supported =================== */ - template void _Fmt::pushParameter(string const&); - template void _Fmt::pushParameter(char const&); - template void _Fmt::pushParameter(uchar const&); - template void _Fmt::pushParameter(int const&); - template void _Fmt::pushParameter(uint const&); - template void _Fmt::pushParameter(short const&); - template void _Fmt::pushParameter(ushort const&); - template void _Fmt::pushParameter(int64_t const&); - template void _Fmt::pushParameter(uint64_t const&); - template void _Fmt::pushParameter(float const&); - template void _Fmt::pushParameter(double const&); - template void _Fmt::pushParameter(void * const&); + template void _Fmt::pushParameter(const string); + template void _Fmt::pushParameter(const char); + template void _Fmt::pushParameter(const uchar); + template void _Fmt::pushParameter(const int); + template void _Fmt::pushParameter(const uint); + template void _Fmt::pushParameter(const short); + template void _Fmt::pushParameter(const ushort); + template void _Fmt::pushParameter(const int64_t); + template void _Fmt::pushParameter(const uint64_t); + template void _Fmt::pushParameter(const float); + template void _Fmt::pushParameter(const double); - template void _Fmt::pushParameter(const string * const); - template void _Fmt::pushParameter(const uchar * const); - template void _Fmt::pushParameter(const int * const); - template void _Fmt::pushParameter(const uint * const); - template void _Fmt::pushParameter(const short * const); - template void _Fmt::pushParameter(const ushort * const); - template void _Fmt::pushParameter(const int64_t * const); - template void _Fmt::pushParameter(const uint64_t * const); - template void _Fmt::pushParameter(const float * const); - template void _Fmt::pushParameter(const double * const); + template void _Fmt::pushParameter(const string * ); + template void _Fmt::pushParameter(const uchar * ); + template void _Fmt::pushParameter(const int * ); + template void _Fmt::pushParameter(const uint * ); + template void _Fmt::pushParameter(const short * ); + template void _Fmt::pushParameter(const ushort * ); + template void _Fmt::pushParameter(const int64_t *); + template void _Fmt::pushParameter(const uint64_t*); + template void _Fmt::pushParameter(const float * ); + template void _Fmt::pushParameter(const double * ); diff --git a/src/lib/format-string.hpp b/src/lib/format-string.hpp index af58a9bda..2e7f63060 100644 --- a/src/lib/format-string.hpp +++ b/src/lib/format-string.hpp @@ -99,10 +99,13 @@ namespace util { struct Converter; template<typename VAL> - void pushParameter (VAL const&); + void pushParameter (const VAL); template<typename VAL> - void pushParameter (const VAL * const); + void pushParameter (const VAL *); + + template<typename VAL> + void pushParameter (VAL *); public: @@ -169,31 +172,44 @@ namespace util { * As fallback we rather just produce a type-ID */ template<typename X> - struct _shall_forward { enum{ value = false };}; + struct _allow_call { enum{ value = false };}; + + /* the following definitions enable some primitive types + * to be handed over to the boost::format implementation */ + template<> struct _allow_call<string> { enum{ value = true }; }; + template<> struct _allow_call<char> { enum{ value = true }; }; + template<> struct _allow_call<uchar> { enum{ value = true }; }; + template<> struct _allow_call<int> { enum{ value = true }; }; + template<> struct _allow_call<uint> { enum{ value = true }; }; + template<> struct _allow_call<short> { enum{ value = true }; }; + template<> struct _allow_call<ushort> { enum{ value = true }; }; + template<> struct _allow_call<int64_t> { enum{ value = true }; }; + template<> struct _allow_call<uint64_t>{ enum{ value = true }; }; + template<> struct _allow_call<float> { enum{ value = true }; }; + template<> struct _allow_call<double> { enum{ value = true }; }; + template<> struct _allow_call<void*> { enum{ value = true }; }; + + template<typename X> + struct _shall_format_directly + { + typedef typename lib::meta::UnConst<X>::Type BaseType; + + enum{ value = _allow_call<BaseType>::value }; + }; + + template<typename X> + struct _shall_format_directly<X*> + : _shall_format_directly<X> + { }; - /* the following definitions enable some basic types - * to be forwarded to boost::format literally */ - template<> struct _shall_forward<string> { enum{ value = true }; }; - template<> struct _shall_forward<char> { enum{ value = true }; }; - template<> struct _shall_forward<uchar> { enum{ value = true }; }; - template<> struct _shall_forward<int> { enum{ value = true }; }; - template<> struct _shall_forward<uint> { enum{ value = true }; }; - template<> struct _shall_forward<short> { enum{ value = true }; }; - template<> struct _shall_forward<ushort> { enum{ value = true }; }; - template<> struct _shall_forward<int64_t> { enum{ value = true }; }; - template<> struct _shall_forward<uint64_t>{ enum{ value = true }; }; - template<> struct _shall_forward<float> { enum{ value = true }; }; - template<> struct _shall_forward<double> { enum{ value = true }; }; - template<> struct _shall_forward<void*> { enum{ value = true }; }; template<typename X> struct _shall_convert_toString { - enum{ value = ! _shall_forward<X>::value - && lib::meta::can_convertToString<X>::value - && !lib::meta::is_sameType<X,char*>::value - }; + enum{ value = ! _shall_format_directly<X>::value + && lib::meta::can_convertToString<X>::value + }; }; @@ -230,7 +246,7 @@ namespace util { template<typename VAL, class SEL> struct _Fmt::Converter { - static string + static const string prepare (VAL const&) { return string("«")+typeid(VAL).name()+"»"; @@ -241,7 +257,7 @@ namespace util { template<typename VAL> struct _Fmt::Converter<VAL, typename enable_if< _shall_convert_toString<VAL> >::type> { - static string + static const string prepare (VAL const& val) try { return string(val); @@ -259,7 +275,7 @@ namespace util { /** some basic types are directly forwarded down to the implementation; * @note this requires explicit specialisations in format-string.cpp */ template<typename VAL> - struct _Fmt::Converter<VAL, typename enable_if< _shall_forward<VAL> >::type> + struct _Fmt::Converter<VAL, typename enable_if< _shall_format_directly<VAL> >::type> { static VAL const& prepare (VAL const& val) @@ -269,14 +285,11 @@ namespace util { }; template<typename VAL> - struct _Fmt::Converter<VAL*, typename enable_if< _shall_forward<VAL> >::type> + void + _Fmt::pushParameter (VAL * pV) ///< treat as const { - static const VAL * - prepare (const VAL * const pVal) - { - return pVal; - } - }; + pushParameter ((const VAL*) pV); + } diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index 91724943f..67dacc400 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -87,6 +87,36 @@ namespace meta { }; + /** strip const from type: naive implementation */ + template<typename T> + struct UnConst + { + typedef T Type; + }; + + template<typename T> + struct UnConst<const T> + { + typedef T Type; + }; + template<typename T> + struct UnConst<const T *> + { + typedef T* Type; + }; + template<typename T> + struct UnConst<T * const> + { + typedef T* Type; + }; + template<typename T> + struct UnConst<const T * const> + { + typedef T* Type; + }; + + + /** 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/lib/format-string-test.cpp b/tests/lib/format-string-test.cpp index 46cae95c1..6ba3b4c32 100644 --- a/tests/lib/format-string-test.cpp +++ b/tests/lib/format-string-test.cpp @@ -279,7 +279,6 @@ namespace test { uint64_t ull(ll); uint64_t * pull = &ull; float f(12.34); float * pf = & f; double d(-12.34); double * pd = & d; - char c(0x40); char * pc = & c; uchar uc(0xff); uchar * puc = & uc; string str("Lumiera"); string * pstr = &str; @@ -293,14 +292,18 @@ namespace test { CHECK (_Fmt("%d") % ull == _Fmt("%d") % pull); CHECK (_Fmt("%e") % f == _Fmt("%e") % pf); CHECK (_Fmt("%e") % d == _Fmt("%e") % pd); - CHECK (_Fmt("%g") % c == _Fmt("%g") % pc); CHECK (_Fmt("%g") % uc == _Fmt("%g") % puc); CHECK (_Fmt("%s") % str == _Fmt("%s") % pstr); + char c('a'); + char pc[2] = {'a', '\0'}; + CHECK (_Fmt("%g") % c == _Fmt("%g") % pc); + Verbose v(42); Verbose *pv = &v; void *vv = pv; CHECK (_Fmt("__%s__") % v == "__Number-042__"); + cout << "yyy=" << string(_Fmt("__%s__") % pv) << endl; CHECK (_Fmt("__%s__") % pv == "__Number-042__"); CHECK (_Fmt("__%s__") % vv != "__Number-042__"); From e1b9b5b1354cd013768f1087b5c74b4ac0e9820e Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 31 Dec 2011 03:52:10 +0100 Subject: [PATCH 339/496] rewrite type selection logic to handle ptrs in the front-end whew, quite a heavy rewrite, but greatly simplifies the code and removes the necessity to declare explicit specialisations of pointers --- src/lib/format-string.cpp | 128 ++++++++++++------------------- src/lib/format-string.hpp | 84 ++++++++++++-------- tests/lib/format-string-test.cpp | 11 ++- 3 files changed, 104 insertions(+), 119 deletions(-) diff --git a/src/lib/format-string.cpp b/src/lib/format-string.cpp index 562fe10d1..c0c6926f2 100644 --- a/src/lib/format-string.cpp +++ b/src/lib/format-string.cpp @@ -70,6 +70,13 @@ namespace util { } + inline void + destroyImpl (char* buffer) + { + accessImpl(buffer).~format(); + } + + /** in case the formatting of a (primitive) value fails, * we try to use a error indicator instead */ @@ -88,34 +95,6 @@ namespace util { ERROR_LOG_AND_IGNORE (progress, "Supplying placeholder for problematic format parameter") - /** Core function: let boost::format handle a value */ - template<typename VAL> - void - doFormatParameter (char* formatter, VAL const& val) - try { - accessImpl(formatter) % val; - } - - catch (boost::io::too_many_args& argErr) - { - WARN (progress, "Format: excess argument '%s' of type %s ignored." - , cStr(str(val)) - , cStr(tyStr(val))); - } - catch (std::exception& failure) - { - WARN (progress, "Format: Parameter '%s' causes problems: %s" - , cStr(str(val)) - , failure.what()); - pushFailsafeReplacement (formatter, failure.what()); - } - catch (...) - { - WARN (progress, "Format: Unexpected problems accepting format parameter '%s'", cStr(str(val))); - pushFailsafeReplacement (formatter); - } - - inline void suppressInsufficientArgumentErrors (char* formatter) { @@ -141,13 +120,16 @@ namespace util { _Fmt::~_Fmt () { - accessImpl(formatter_).~format(); + destroyImpl (formatter_); } - /** @internal access points for the frontend, - * allowing to push a parameter value down into the implementation - * for the actual formatting. + /** @internal access point for the frontend, + * allowing to push a single parameter value down + * into the implementation for the actual formatting. + * Only selected primitive types are handled directly this way, + * while all custom conversions, the handling of pointers and the + * fallback (showing a type string) is done in the frontend. * @note we need to generate instantiations of this template function * explicitly for all basic types to be supported for direct handling, * otherwise we'll get linker errors. Lumiera uses the ``inclusion model'' @@ -157,60 +139,48 @@ namespace util { */ template<typename VAL> void - _Fmt::pushParameter (const VAL val) - { - doFormatParameter (formatter_, val); - } + _Fmt::format (const VAL val, Implementation& formatter) + try { + accessImpl(formatter) % val; + } - template<typename VAL> - void - _Fmt::pushParameter (const VAL * pVal) - { - if (pVal) - doFormatParameter (formatter_, *pVal); - else - doFormatParameter (formatter_, "<null>"); - } + catch (boost::io::too_many_args& argErr) + { + WARN (progress, "Format: excess argument '%s' of type %s ignored." + , cStr(str(val)) + , cStr(tyStr(val))); + } + catch (std::exception& failure) + { + WARN (progress, "Format: Parameter '%s' causes problems: %s" + , cStr(str(val)) + , failure.what()); + pushFailsafeReplacement (formatter, failure.what()); + } + catch (...) + { + WARN (progress, "Format: Unexpected problems accepting format parameter '%s'", cStr(str(val))); + pushFailsafeReplacement (formatter); + } - template<> - void - _Fmt::pushParameter (const char * cString) - { - doFormatParameter (formatter_, cString? cString : "↯" ); - } - template<> - void - _Fmt::pushParameter (const void * address) - { - doFormatParameter (formatter_, address); - } /* ===== explicitly supported =================== */ - template void _Fmt::pushParameter(const string); - template void _Fmt::pushParameter(const char); - template void _Fmt::pushParameter(const uchar); - template void _Fmt::pushParameter(const int); - template void _Fmt::pushParameter(const uint); - template void _Fmt::pushParameter(const short); - template void _Fmt::pushParameter(const ushort); - template void _Fmt::pushParameter(const int64_t); - template void _Fmt::pushParameter(const uint64_t); - template void _Fmt::pushParameter(const float); - template void _Fmt::pushParameter(const double); - - template void _Fmt::pushParameter(const string * ); - template void _Fmt::pushParameter(const uchar * ); - template void _Fmt::pushParameter(const int * ); - template void _Fmt::pushParameter(const uint * ); - template void _Fmt::pushParameter(const short * ); - template void _Fmt::pushParameter(const ushort * ); - template void _Fmt::pushParameter(const int64_t *); - template void _Fmt::pushParameter(const uint64_t*); - template void _Fmt::pushParameter(const float * ); - template void _Fmt::pushParameter(const double * ); + template void _Fmt::format (const char, Implementation&); + template void _Fmt::format (const uchar, Implementation&); + template void _Fmt::format (const int, Implementation&); + template void _Fmt::format (const uint, Implementation&); + template void _Fmt::format (const short, Implementation&); + template void _Fmt::format (const ushort, Implementation&); + template void _Fmt::format (const int64_t, Implementation&); + template void _Fmt::format (const uint64_t,Implementation&); + template void _Fmt::format (const float, Implementation&); + template void _Fmt::format (const double, Implementation&); + template void _Fmt::format (const string, Implementation&); + template void _Fmt::format (const void *, Implementation&); + template void _Fmt::format (const char *, Implementation&); diff --git a/src/lib/format-string.hpp b/src/lib/format-string.hpp index 2e7f63060..0c49bbe0a 100644 --- a/src/lib/format-string.hpp +++ b/src/lib/format-string.hpp @@ -89,23 +89,19 @@ namespace util { /** size of an internal implementation Buffer */ enum{ FORMATTER_SIZE = lib::meta::SizeTrait::BOOST_FORMAT }; + typedef char Implementation[FORMATTER_SIZE]; /** @internal buffer to hold a boost::format */ - mutable char formatter_[FORMATTER_SIZE]; + mutable Implementation formatter_; + template<typename VAL> + static void format (const VAL, Implementation&); + /** helper to prepare parameters for inclusion */ template<typename VAL, class SEL =void> struct Converter; - template<typename VAL> - void pushParameter (const VAL); - - template<typename VAL> - void pushParameter (const VAL *); - - template<typename VAL> - void pushParameter (VAL *); public: @@ -158,7 +154,7 @@ namespace util { inline _Fmt& _Fmt::operator% (VAL const& val) { - pushParameter (Converter<VAL>::prepare (val)); + Converter<VAL>::dump (val, formatter_); return *this; } @@ -187,7 +183,6 @@ namespace util { template<> struct _allow_call<uint64_t>{ enum{ value = true }; }; template<> struct _allow_call<float> { enum{ value = true }; }; template<> struct _allow_call<double> { enum{ value = true }; }; - template<> struct _allow_call<void*> { enum{ value = true }; }; template<typename X> struct _shall_format_directly @@ -197,11 +192,6 @@ namespace util { enum{ value = _allow_call<BaseType>::value }; }; - template<typename X> - struct _shall_format_directly<X*> - : _shall_format_directly<X> - { }; - template<typename X> @@ -246,10 +236,43 @@ namespace util { template<typename VAL, class SEL> struct _Fmt::Converter { - static const string - prepare (VAL const&) + static void + dump (VAL const&, Implementation& impl) { - return string("«")+typeid(VAL).name()+"»"; + format (string("«")+typeid(VAL).name()+"»", impl); + } + }; + + template<typename VAL> + struct _Fmt::Converter<VAL*> + { + static void + dump (const VAL *pVal, Implementation& impl) + { + if (pVal) + Converter<VAL>::dump(*pVal, impl); + else + format ("<null>", impl); + } + }; + + template<> + struct _Fmt::Converter<void *> + { + static void + dump (const void* address, Implementation& impl) + { + format (address, impl); + } + }; + + template<> + struct _Fmt::Converter<const char *> + { + static void + dump (const char* cString, Implementation& impl) + { + format (cString? cString : "↯", impl); } }; @@ -257,18 +280,18 @@ namespace util { template<typename VAL> struct _Fmt::Converter<VAL, typename enable_if< _shall_convert_toString<VAL> >::type> { - static const string - prepare (VAL const& val) + static void + dump (VAL const& val, Implementation& impl) try { - return string(val); + format (string(val), impl); } catch(std::exception const& ex) { - return _log_and_stringify(ex); + format (_log_and_stringify(ex), impl); } catch(...) { - return _log_unknown_exception(); + format (_log_unknown_exception(), impl); } }; @@ -277,20 +300,13 @@ namespace util { template<typename VAL> struct _Fmt::Converter<VAL, typename enable_if< _shall_format_directly<VAL> >::type> { - static VAL const& - prepare (VAL const& val) + static void + dump (const VAL val, Implementation& impl) { - return val; + format (val, impl); } }; - template<typename VAL> - void - _Fmt::pushParameter (VAL * pV) ///< treat as const - { - pushParameter ((const VAL*) pV); - } - /* === comparison of formatter objects === */ diff --git a/tests/lib/format-string-test.cpp b/tests/lib/format-string-test.cpp index 6ba3b4c32..48c5b5df3 100644 --- a/tests/lib/format-string-test.cpp +++ b/tests/lib/format-string-test.cpp @@ -46,11 +46,11 @@ namespace test { * @test verifies our front-end for printf-style format string based formatting. * - the implementation is based on boost::format * - we create a wrapper-object on-the fly, which is able to hold - * the result of a partial invocation - * - explicit specialisations feed all primitive types directly - * down into the implementation level. If possible, a custom operator string() + * the result of a partial invocation, until all parameters are given. + * - explicit specialisations feed all primitive types directly down + * into the implementation level. If possible, a custom operator string() * will be used for formatting. - * - any kind of exception will be logged, but handled gracefully + * - exceptions will be logged, but handled gracefully * * @see format-string.hpp * @see boost::format @@ -303,7 +303,6 @@ namespace test { Verbose *pv = &v; void *vv = pv; CHECK (_Fmt("__%s__") % v == "__Number-042__"); - cout << "yyy=" << string(_Fmt("__%s__") % pv) << endl; CHECK (_Fmt("__%s__") % pv == "__Number-042__"); CHECK (_Fmt("__%s__") % vv != "__Number-042__"); @@ -313,7 +312,7 @@ namespace test { pv = NULL; vv = NULL; CHECK (_Fmt("__%s__") % pv == "__<null>__"); - CHECK (_Fmt("__%s__") % pv == "__0__"); + CHECK (_Fmt("__%s__") % vv == "__0__"); } }; From 37384f1b681f5bbfa7dc4d50b8588ed801fbddb3 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 31 Dec 2011 06:38:31 +0100 Subject: [PATCH 340/496] formatting wrapper/frontend: unit test pass. Closes #166 --- src/lib/format-string.cpp | 47 +++++++--- src/lib/format-string.hpp | 106 +++++++++++++++++++---- tests/40components.tests | 15 +++- tests/lib/format-string-test.cpp | 3 + tests/lib/time/quantiser-basics-test.cpp | 2 +- 5 files changed, 142 insertions(+), 31 deletions(-) diff --git a/src/lib/format-string.cpp b/src/lib/format-string.cpp index c0c6926f2..85c3abc19 100644 --- a/src/lib/format-string.cpp +++ b/src/lib/format-string.cpp @@ -24,11 +24,13 @@ ** Implementation for printf-style formatting, based on boost::format. ** This file holds the generic implementation of our format frontend, ** which actually just invokes boost::format. The corresponding header - ** format-string.hpp contains some template functions and classes, - ** which select an appropriate wrapper to pass the calls down. + ** format-string.hpp provides some template functions and classes, + ** either invoking a custom string conversion, or passing primitive + ** values down unaltered. + ** ** Here, we define explicit specialisations for the frontend to invoke, ** which in turn just pass on the given argument value to the embedded - ** boost::format object, which in turn integrates the formatted result + ** boost::format object, which in turn dumps the formatted result ** into an embedded string stream. ** ** To avoid exposing boost::format in the frontend header, we use an @@ -61,6 +63,7 @@ namespace util { using boost::format; + namespace { // implementation details... inline boost::format& @@ -78,8 +81,7 @@ namespace util { /** in case the formatting of a (primitive) value fails, - * we try to use a error indicator instead - */ + * we try to supply an error indicator instead */ void pushFailsafeReplacement (char* formatter, const char* errorMsg =NULL) try { @@ -109,14 +111,26 @@ namespace util { - /** */ + /** Build a formatter object based on the given format string. + * The actual implementation is delegated to an boost::format object, + * which is placement-constructed into an opaque buffer embedded into + * this object. Defining the necessary size for this buffer relies + * on a implementation details of boost::format (and might break) + */ _Fmt::_Fmt (string formatString) - { - BOOST_STATIC_ASSERT (sizeof(boost::format) <= FORMATTER_SIZE); - - new(formatter_) boost::format(formatString); - suppressInsufficientArgumentErrors (formatter_); - } + try { + BOOST_STATIC_ASSERT (sizeof(boost::format) <= FORMATTER_SIZE); + + new(formatter_) boost::format(formatString); + suppressInsufficientArgumentErrors (formatter_); + } + catch (boost::io::bad_format_string& syntaxError) + { + throw lumiera::error::Fatal (syntaxError + , _Fmt("Format string '%s' is broken") % formatString + , LUMIERA_ERROR_FORMAT_SYNTAX); + } + _Fmt::~_Fmt () { @@ -152,6 +166,7 @@ namespace util { } catch (std::exception& failure) { + _clear_errorflag(); WARN (progress, "Format: Parameter '%s' causes problems: %s" , cStr(str(val)) , failure.what()); @@ -159,6 +174,7 @@ namespace util { } catch (...) { + _clear_errorflag(); WARN (progress, "Format: Unexpected problems accepting format parameter '%s'", cStr(str(val))); pushFailsafeReplacement (formatter); } @@ -198,11 +214,13 @@ namespace util { catch (std::exception& failure) { + _clear_errorflag(); WARN (progress, "Format: Failure to receive formatted result: %s", failure.what()); return "<formatting failure>"; } catch (...) { + _clear_errorflag(); WARN (progress, "Format: Unexpected problems while formatting output."); return "<unexpected problems>"; } @@ -222,15 +240,20 @@ namespace util { catch(std::exception& failure) { + _clear_errorflag(); WARN (progress, "Format: Failure when outputting formatted result: %s", failure.what()); return os << "<formatting failure>"; } catch(...) { + _clear_errorflag(); WARN (progress, "Format: Unexpected problems while producing formatted output."); return os << "<unexpected problems>"; } + LUMIERA_ERROR_DEFINE (FORMAT_SYNTAX, "Syntax error in format string for boost::format"); + + } // namespace util diff --git a/src/lib/format-string.hpp b/src/lib/format-string.hpp index 0c49bbe0a..716aa0e3d 100644 --- a/src/lib/format-string.hpp +++ b/src/lib/format-string.hpp @@ -25,19 +25,77 @@ ** Front-end for printf-style string template interpolation. ** While the actual implementation just delegates to boost::format, this front-end ** hides the direct dependency, additionally invokes a custom toSting conversion - ** whenever possible, provides a direct conversion to string and catches any exceptions. + ** whenever possible, provides a direct automatic conversion to the formatted result + ** to string and catches any exceptions. + ** ** This front-end is used pervasively for diagnostics and logging, so keeping down the ** compilation and object size cost and reliably handling any error is more important ** than the (small) performance gain of directly invoking boost::format (which is ** known to be 10 times slower than printf anyway). ** - ** @remarks The implementation is invoked through a set of explicit specialisations. - ** For custom types, we prefer to invoke operator string(), which is determined - ** by a directly coded metaprogramming test. The compile time and object size - ** overhead incurred by using this header was verified to be negligible. + ** \par Implementation notes + ** To perform the formatting, usually a \c _Fmt object is created as an anonymous + ** temporary, but it may as well be stored into a variable. Copying is not permitted. + ** Individual parameters are then fed for formatting through the \c '%' operator. + ** Each instance of _Fmt uses its own embedded boost::format object for implementation, + ** but this formatter resides within an opaque buffer embedded into the frontend object. + ** The rationale for this admittedly tricky approach is to confine any usage of boost::format + ** to the implementation translation unit (format-string.cpp). ** + ** The implementation is invoked by the frontend through a set of explicit specialisations + ** for all the relevant \em primitive data types. For custom types, we prefer to invoke + ** operator string() if possible, which is determined by a simple metaprogramming test, + ** which is defined in lib/meta/util.pp, without relying on boost. As a fallback, for + ** all other types without built-in or custom string conversion, we use the mangled + ** type string produced by RTTI. + ** + ** The compile time and object size overhead incurred by using this header was verified + ** to be negligible, in comparison to using boost::format. When compiling a demo example + ** on x86_64, the following executable sizes could be observed: + ** + ** debug stripped + ** just string concatenation ............... 42k 8.8k + ** including and using format-string.hpp ... 50k 9.4k + ** including and using boost::format ....... 420k 140k + ** + ** In addition, we need to take the implementation translation unit (format-string.cpp) + ** into account, which is required once per application and contains the specialisations + ** for all primitive types. In the test showed above, the corresponding object file + ** had a size of 1300k (with debug information) resp. 290k (stripped). + ** + ** \par Usage + ** The syntax of the format string is defined by boost::format and closely mimics + ** the printf formatting directives. The notable difference is that boost::format + ** uses the C++ stream output framework, and thus avoiding the perils of printf. + ** The individual formatting placeholders just set the corresponding flags on + ** an embedded string stream, thus the actual parameter types cause the + ** selection of a suitable format, not the definitions within the + ** format string. + ** + ** An illegal format string will raise an error::Fatal. Any other error during usage of + ** the formatter is caught, logged and suppressed, inserting an error indicator into + ** the formatted result instead + ** + ** A formatter is usually created as an anonymous object, at places where a string + ** is expected. An arbitrary number of parameters is then supplied using the \c '%' operator. + ** The result can be obtained + ** - by string conversion + ** - by feeding into an output stream. + ** + ** Code example: + ** \code + ** double total = 22.9499; + ** const char * currency = "€"; + ** cout << _Fmt("price %+5.2f %s") % total % currency << endl; + ** \endcode + ** + ** @remarks See the unit-test for extensive usage examples and corner cases. + ** The header format-util.hpp provides an alternative string conversion, + ** using a bit of boost type traits and lexical_cast, but no boost::format. + ** @warning not suited for performance critical code. About 10 times slower than printf. + ** ** @see FormatString_test - ** @see format-helper + ** @see format-util.hpp ** */ @@ -71,26 +129,33 @@ namespace std { // forward declaration to avoid including <iostream> namespace util { - typedef unsigned char uchar; - - using boost::enable_if; using std::string; - + using boost::enable_if; + typedef unsigned char uchar; + + LUMIERA_ERROR_DECLARE (FORMAT_SYNTAX); ///< "Syntax error in format string for boost::format" /** - * @todo write type comment + * A front-end for using printf-style formatting. + * Values to be formatted can be supplied through the + * operator%. Custom defined string conversions on objects + * will be used, any errors while invoking the format operation + * will be suppressed. The implementation is based on boost::format, + * but kept opaque to keep code size and compilation times down. + * @see FormatString_test */ class _Fmt : boost::noncopyable { - /** size of an internal implementation Buffer */ + /** size of an opaque implementation Buffer */ enum{ FORMATTER_SIZE = lib::meta::SizeTrait::BOOST_FORMAT }; typedef char Implementation[FORMATTER_SIZE]; + /** @internal buffer to hold a boost::format */ mutable Implementation formatter_; @@ -143,9 +208,10 @@ namespace util { * \par type specific treatment * Basic types (numbers, chars, strings) are passed to the implementation * (= boost::format) literally. For custom types, we try to use a custom - * string conversion, if applicable. Any other type gets just translated - * into a type-ID (using the mangled RTTI info). In case of errors during - * the conversion, a string representation of the error is returned + * string conversion, if applicable. Non-NULL pointers will be dereferenced, + * with the exception of C-Strings and \c void*. Any other type gets just + * translated into a type-ID (using the mangled RTTI info). In case of errors + * during the conversion, a string representation of the error is returned * @param val arbitrary value or pointer to be included into the result * @warning you need to provide exactly the right number of parameters, * i.e. matching the number of fields in the format string. @@ -203,9 +269,17 @@ namespace util { }; + inline void + _clear_errorflag() + { + const char* errID = lumiera_error(); + TRACE_IF (errID, progress, "Lumiera errorstate '%s' cleared.", errID); + } + inline string _log_and_stringify (std::exception const& ex) { + _clear_errorflag(); WARN (progress, "Error while invoking custom string conversion: %s", ex.what()); try { return string("<string conversion failed: ")+ex.what()+">"; @@ -276,7 +350,7 @@ namespace util { } }; - /** some custom types explicitly provide string representation */ + /** some custom types explicitly provide a string representation */ template<typename VAL> struct _Fmt::Converter<VAL, typename enable_if< _shall_convert_toString<VAL> >::type> { diff --git a/tests/40components.tests b/tests/40components.tests index 62827efac..92ead61e2 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -329,7 +329,18 @@ return: 0 END -PLANNED "formatting by string template" FormatString_test <<END +TEST "formatting by string template" FormatString_test <<END +out-lit: --format-template--int=0012--double=+1.23--string=Lumiera -- +out: 0x....+ _____ . +out: «.+util.+test.+Silent.» +out-lit: __nix_ +out-lit: ____ +out-lit: __1__ +out-lit: __1__ +out-lit: __↯__ +out-lit: __dirt__ +out-lit: __1234__ +out-lit: __0xff__ return: 0 END @@ -394,7 +405,7 @@ return: 0 END -TEST "metaprogramming helpers" Metautils_test <<END +TEST "metaprogramming helpers" MetaUtils_test <<END return: 0 END diff --git a/tests/lib/format-string-test.cpp b/tests/lib/format-string-test.cpp index 48c5b5df3..34bf3a2c2 100644 --- a/tests/lib/format-string-test.cpp +++ b/tests/lib/format-string-test.cpp @@ -22,6 +22,7 @@ #include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" #include "lib/format-string.hpp" #include "lib/error.hpp" #include "lib/util.hpp" @@ -263,6 +264,8 @@ namespace test { cout << _Fmt("__%d__") % "dirt" << endl; cout << _Fmt("__%d__") % "1234" << endl; cout << _Fmt("__%d__") % "0xff" << endl; + + VERIFY_ERROR(FORMAT_SYNTAX, _Fmt("%broken")); } diff --git a/tests/lib/time/quantiser-basics-test.cpp b/tests/lib/time/quantiser-basics-test.cpp index cca585491..a2656fa79 100644 --- a/tests/lib/time/quantiser-basics-test.cpp +++ b/tests/lib/time/quantiser-basics-test.cpp @@ -88,7 +88,7 @@ namespace test{ FixedFrameQuantiser fixQ(25); uint frames = (rand() % MAX_FRAMES); - FSecs dirt = (F25 / (1 + rand() % DIRT_GRAIN)); + FSecs dirt = (F25 / (2 + rand() % DIRT_GRAIN)); Time rawTime (dirt + frames*F25); From adc120fecbc8abe75d28b7b9c618c3fabc7eae8a Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 31 Dec 2011 07:54:16 +0100 Subject: [PATCH 341/496] document the new formatting helper, based on boost::format --- doc/technical/howto/UsingBoost.txt | 4 +- doc/technical/overview.txt | 62 +++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/doc/technical/howto/UsingBoost.txt b/doc/technical/howto/UsingBoost.txt index 2d937ca92..5b3bab017 100644 --- a/doc/technical/howto/UsingBoost.txt +++ b/doc/technical/howto/UsingBoost.txt @@ -92,7 +92,9 @@ formatted output -- but typesafe, using defined conversion operators and without the plain-C `printf` famility of functions. But beware: `boost::format` is implemented on top of the C++ output stream operations (`<<` and manipulators), which in turn are implemented based on `printf` -- you can expect it to be 5 to 10 times slower than the latter, and it has -quite some compilation overhead. +quite some compilation overhead and size impact (-> see our own +link:http://git.lumiera.org/gitweb?p=lumiera/ichthyo;a=blob;f=src/lib/format-string.hpp;h=716aa0e3d23f09269973b7659910d74b3ee334ea;hb=37384f1b681f5bbfa7dc4d50b8588ed801fbddb3[formatting front-end] +to reduce this overhead) .variant and any These library provide a nice option for building data structures able to hold a mixture of diff --git a/doc/technical/overview.txt b/doc/technical/overview.txt index 587ff2dac..3e389544c 100644 --- a/doc/technical/overview.txt +++ b/doc/technical/overview.txt @@ -434,11 +434,33 @@ Library The Lumiera support library contains lots of helper functionality factored out from the internals and re-used. It is extended as we go. +practical shortcuts +~~~~~~~~~~~~~~~~~~~ +The header 'lib/util.hpp' defines some shortcuts heavily used throughout +the code base. The idea is to highlight a common semantic meaning, while +hide differentiation on the technical details. + +isnil:: indicate a _missing value_, irrespective if this is a NULL pointer, + an empty string or an empty container. Several of our custom wrappers also + support this notion. + +contains:: indicate that a value is _somehow contained_ within a collection, + irrespective if this is a set, a map, a vector or string (substring test) + +sanitise:: make any string usable as identifier. + +In a similar vein, the header 'lib/util-foreach.hpp' provides a generic +``for-each-element'' mechanism, which works for all STL containers, but +also for all _Lumiera Forward Iterators_. The loop body is provided +as a functor. In case this functor is a predicate (boolean result), +the +and_all+ and +has_any+ functions allow to test for conjunction +and disjunction. + Locking ~~~~~~~ -Based on object monitors. Performance critical code -uses mutexes, condition vars and rwlocks direcly. +General purpose Locking is based on object monitors. Performance critical code +in the backend uses mutexes, condition vars and rwlocks direcly. Intentionally no semaphores. - C++ locks are managed by scoped automatic variables @@ -661,6 +683,42 @@ Itertools _tbw_ +Front-end for boost::format +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Formatting values with `printf` is a notorious source for intricate errors. +Additionally, using (s|n)`printf` can be clunky in practice, and it doesn't +support custom defined string conversions, which are an important diagnostic +aid when working with objects. We might +link:{ldoc}/technical/howto/UsingBoost.html[use Boost], which provides the +`boost::format` library to address those problems. Unfortunately including this +header-only library solution incurs a significant overhead, both in terms of +compilation time and code size. While maybe still acceptable at the implementation +level, using boost::format is thus certainly a ``no go'' for any code residing +in headers frequently included. + +To work around these problems, we provide a front-end wrapper, defined in +'lib/format-string.hpp'. This allows to keep the actual boost::format based +implementation confined to a single translation unit, while still being able +to use all primitive types as usual with boost::format or printf. Additionally, +our frontend automatically invokes a custom or built-in string conversion, if +applicable, it dereferences pointers and catches all errors encountered while +the formatting. So it's well suited for usage in error handling code. + +[source,C] +------------------------------------------------------------ +#include "lib/format-string.hpp" +using util::_Fmt; + +double total = 22.9499; +const char * currency = "€"; + +cout << _Fmt("price %+5.2f %s") % total % currency << endl; +------------------------------------------------------------ + +WARNING: `boost::format` is known to be about 10 times slower than `printf` -- + best to avoid it for performance critical code. + + Wrappers and Opaque Holders ~~~~~~~~~~~~~~~~~~~~~~~~~~~ .smart handle From f047069284f1e9b425a2601b2b686b3a51293e8b Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 31 Dec 2011 20:33:50 +0100 Subject: [PATCH 342/496] rearrange OutputSlot ConnectionStateManager ... shape the various APIs more clearly, make most functions protected --- src/proc/play/output-slot-connection.hpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/proc/play/output-slot-connection.hpp b/src/proc/play/output-slot-connection.hpp index 2cbd34c4e..29e6bf8e4 100644 --- a/src/proc/play/output-slot-connection.hpp +++ b/src/proc/play/output-slot-connection.hpp @@ -171,14 +171,11 @@ namespace play { } - public: - ConnectionStateManager() - { } - - virtual - ~ConnectionStateManager() - { } + protected: /* == API for OutputSlot-Impl == */ + /** factory function to build the actual + * connection handling objects per channel */ + virtual CON buildConnection() =0; void init (uint numChannels) @@ -188,9 +185,13 @@ namespace play { } - /** factory function to build the actual - * connection handling objects per channel */ - virtual CON buildConnection() =0; + ConnectionStateManager() { } + + public: + virtual + ~ConnectionStateManager() + { } + private: // Implementation details From e63fa6d64643688b8cfa75fa3519b6e2cfe2951c Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 1 Jan 2012 06:20:42 +0100 Subject: [PATCH 343/496] Test-driven brainstorming: draft a ScopedCollection #877 --- src/lib/opaque-holder.hpp | 4 +- src/lib/scoped-collection.hpp | 161 +++++++++ src/lib/test/testdummy.hpp | 14 +- tests/40components.tests | 7 +- .../proc/play/diagnostic-output-slot.hpp | 2 + tests/lib/diagnostic-context-test.cpp | 2 +- tests/lib/scoped-collection-test.cpp | 326 ++++++++++++++++++ tests/lib/scoped-holder-test.cpp | 12 +- tests/lib/scoped-holder-transfer-test.cpp | 2 +- 9 files changed, 516 insertions(+), 14 deletions(-) create mode 100644 src/lib/scoped-collection.hpp create mode 100644 tests/lib/scoped-collection-test.cpp diff --git a/src/lib/opaque-holder.hpp b/src/lib/opaque-holder.hpp index d19ecfcc8..7ffb2463b 100644 --- a/src/lib/opaque-holder.hpp +++ b/src/lib/opaque-holder.hpp @@ -253,6 +253,8 @@ namespace lib { template<typename SUB> struct Buff : Buffer { + BOOST_STATIC_ASSERT (siz >= sizeof(SUB)); + SUB& get() const ///< core operation: target is contained within the inline buffer { @@ -267,8 +269,6 @@ namespace lib { explicit Buff (SUB const& obj) { - BOOST_STATIC_ASSERT (siz >= sizeof(SUB)); - new(Buffer::ptr()) SUB (obj); } diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp new file mode 100644 index 000000000..8a2fb81e0 --- /dev/null +++ b/src/lib/scoped-collection.hpp @@ -0,0 +1,161 @@ +/* + SCOPED-COLLECTION.hpp - managing a fixed collection of noncopyable polymorphic objects + + Copyright (C) Lumiera.org + 2012, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file scoped-collection.hpp + ** Managing a collection of noncopyable polymorphic objects in compact storage. + ** This helper supports the frequently encountered situation where a service + ** implementation internally manages a collection of implementation related + ** sub-components with reference semantics. Typically, those objects are + ** being used polymorphically, and often they are also added step by step. + ** The storage holding all those child objects is allocated in one chunk + ** and never adjusted. + ** + ** - TODO: retro-fit with RefArray interface + ** + ** @see ScopedCollection_test + ** @see scoped-ptrvect.hpp quite similar, but using individual heap pointers + */ + + +#ifndef LIB_SCOPED_COLLECTION_H +#define LIB_SCOPED_COLLECTION_H + + +//#include "include/logging.h" +//#include "lib/iter-adapter.hpp" +#include "lib/error.hpp" +//#include "lib/util.hpp" + +//#include <vector> +//#include <algorithm> +#include <boost/noncopyable.hpp> + + +namespace lib { + + + + /** + * A fixed collection of noncopyable polymorphic objects. + * All child objects reside in a common chunk of storage + * and are owned and managed by this collection holder. + * Array style access and iteration. + */ + template<class T> + class ScopedCollection + : boost::noncopyable + { +// typedef std::vector<T*> _Vec; +// typedef typename _Vec::iterator VIter; +// +// typedef RangeIter<VIter> RIter; +// typedef PtrDerefIter<RIter> IterType; +// +// typedef typename IterType::ConstIterType ConstIterType; +// typedef typename IterType::WrappedConstIterType RcIter; + + + public: + typedef size_t size_type; +// typedef T & reference; +// typedef T const& const_reference; + + + + ScopedCollection () + { } + + explicit + ScopedCollection (size_type capacity) + { + UNIMPLEMENTED ("prepare scoped collection storage"); + } + + ~ScopedCollection () + { + clear(); + } + + + + + void + clear() + { + UNIMPLEMENTED ("drop all content objects"); + } + + + /* === Element access and iteration === */ + + T& + operator[] (size_type i) + { + return *get(i); + } + +// typedef IterType iterator; +// typedef ConstIterType const_iterator; +// +// iterator begin() { return iterator (allPtrs()); } +// iterator end() { return iterator ( RIter() ); } +// const_iterator begin() const { return const_iterator::build_by_cast (allPtrs()); } +// const_iterator end() const { return const_iterator::nil(); } + + + + + /* ====== proxied vector functions ==================== */ + +// size_type size () const { return _Vec::size(); } +// size_type max_size () const { return _Vec::max_size(); } +// size_type capacity () const { return _Vec::capacity(); } +// bool empty () const { return _Vec::empty(); } + + + private: + /** @internal element access, including range and null check */ + T* + get (size_type i) + { + UNIMPLEMENTED("raw element access"); + } + +// /** @internal access sequence of all managed pointers */ +// RIter +// allPtrs () +// { +// return RIter (_Vec::begin(), _Vec::end()); +// } +// RIter +// allPtrs () const +// { +// _Vec& elements = util::unConst(*this); +// return RIter (elements.begin(), elements.end()); +// } + }; + + + + +} // namespace lib +#endif diff --git a/src/lib/test/testdummy.hpp b/src/lib/test/testdummy.hpp index 24d467c1f..27d4e79c7 100644 --- a/src/lib/test/testdummy.hpp +++ b/src/lib/test/testdummy.hpp @@ -48,14 +48,22 @@ namespace test{ : val_(v) { init(); } - ~Dummy() + virtual ~Dummy() { checksum() -= val_; } - long add (int i) { return val_+i; } + virtual long + acc (int i) ///< dummy API operation + { + return val_+i; + } - int getVal() const { return val_; } + int + getVal() const + { + return val_; + } void setVal (int newVal) diff --git a/tests/40components.tests b/tests/40components.tests index 92ead61e2..d22d69d07 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -617,7 +617,12 @@ out: ^\.throw some exceptions... END -TEST "ScopedPtrVect_test" ScopedPtrVect_test <<END +PLANNED "Managed Collection (I)" ScopedCollection_test <<END +return: 0 +END + + +TEST "Managed Collection (II)" ScopedPtrVect_test <<END return: 0 END diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index ff948d814..9ab0f98a7 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -102,6 +102,8 @@ namespace play { TimeValue deadlineFor (FrameID frameNr) { + // a real world implementation + // would offset by a latency here return frameGrid_->timeOf (frameNr); } diff --git a/tests/lib/diagnostic-context-test.cpp b/tests/lib/diagnostic-context-test.cpp index 001c92cef..008c37af5 100644 --- a/tests/lib/diagnostic-context-test.cpp +++ b/tests/lib/diagnostic-context-test.cpp @@ -39,7 +39,7 @@ namespace test{ namespace { // private test setup... - const uint NUM_THREADS = 50; + const uint NUM_THREADS = 40; const uint MAX_RAND = 1000*1000; inline bool diff --git a/tests/lib/scoped-collection-test.cpp b/tests/lib/scoped-collection-test.cpp new file mode 100644 index 000000000..f712c239e --- /dev/null +++ b/tests/lib/scoped-collection-test.cpp @@ -0,0 +1,326 @@ +/* + ScopedCollection(Test) - holding and owning a fixed collection of noncopyable objects + + Copyright (C) Lumiera.org + 2012, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/util.hpp" + +#include "lib/scoped-collection.hpp" +#include "lib/test/testdummy.hpp" + +#include <cstdlib> + + +namespace lib { +namespace test{ + + namespace error = lumiera::error; + + + namespace { // our explosive special Dummy + + LUMIERA_ERROR_DEFINE(SUBVERSIVE, "undercover action"); + + class SubDummy + : public Dummy + { + int trigger_; + + /** special variant of the dummy API operation: + * @param i when zero, the trigger value will be revealed + */ + virtual long + acc (int i) + { + if (!i) + return getVal() + trigger_; + else + return Dummy::acc(i); + } + + public: + SubDummy (int id, int trigger) + : Dummy(id) + , trigger_(trigger) + { + if (trigger == getVal()) + throw new error::Fatal ("Subversive Bomb", LUMIERA_ERROR_SUBVERSIVE); + } + }; + + }//(End) subversive test data + + + + + using util::isnil; +//using std::tr1::placeholders::_1; /////////////////////////////TODO + using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST; + + typedef ScopedCollection<Dummy> CollD; + + + /******************************************************************** + * @test ScopedCollection manages a fixed set of objects, but these + * child objects are noncopyable, may be polymorphic, an can + * be created either all at once or chunk wise. The API is + * similar to a vector and allows for element access + * and iteration. + */ + class ScopedCollection_test : public Test + { + + virtual void + run (Arg) + { + simpleUsage(); + building_RAII_Style(); + building_StackStyle(); + iterating(); + } + + + void + simpleUsage() + { + CHECK (0 == Dummy::checksum()); + { +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 + CollD container(5); + CHECK (isnil (container)); + CHECK (0 == container.size()); + CHECK (0 == Dummy::checksum()); + + container.populate(); + CHECK (!isnil (container)); + CHECK (5 == container.size()); + CHECK (0 != Dummy::checksum()); + + holder.clear(); + CHECK (isnil (container)); + CHECK (0 == container.size()); + CHECK (0 == Dummy::checksum()); + + container.populate(); + CHECK (Dummy::checksum() == container[0].getVal() + + container[1].getVal() + + container[2].getVal() + + container[3].getVal() + + container[4].getVal()); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 + } + CHECK (0 == Dummy::checksum()); + } + + + void + iterating() + { + CHECK (0 == Dummy::checksum()); + { +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 + CollD coll(50); + for (int i=0; i<coll.capacity(); ++i) + coll.appendNew<Dummy>(i); + + int check=0; + CollD::iterator ii = coll.begin(); + while (ii) + { + CHECK (check == ii->getVal()); + CHECK (check == ii->acc(+5) - 5); + ++check; + ++ii; + } + + + // Test the const iterator + check = 0; + CollD::const_iterator cii = coll.begin(); + while (cii) + { + CHECK (check == cii->getVal()); + ++check; + ++cii; + } + + + // Verify correct behaviour of iteration end + CHECK (! (coll.end())); + CHECK (isnil (coll.end())); + + VERIFY_ERROR (ITER_EXHAUST, *coll.end() ); + VERIFY_ERROR (ITER_EXHAUST, ++coll.end() ); + + CHECK (ii == coll.end()); + CHECK (cii == coll.end()); + VERIFY_ERROR (ITER_EXHAUST, ++ii ); + VERIFY_ERROR (ITER_EXHAUST, ++cii ); + +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 + } + CHECK (0 == Dummy::checksum()); + } + + + void + building_RAII_Style() + { + UNIMPLEMENTED ("building all at once"); + CHECK (0 == Dummy::checksum()); + { +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 + /** Functor to populate the Collection */ + class Populator + { + uint i_; + int off_; + + public: + Populator (int baseOffset) + : i_(0) + , off_(baseOffset) + { } + + void + operator() (int specialOffset, void* storage) + { + switch (i_ % 2) + { + case 0: + new(storage) Dummy(i_+off_); + break; + + case 1: + new(storage) SubDummy(i_+off_, specialOffset); + break; + } } + }; + + + int rr = rand() % 100; + int trigger = 101; + + CollD coll (6, Populator(rr), trigger, _1 ); + + CHECK (!isnil (coll)); + CHECK (6 == coll.size()); + CHECK (0 != Dummy::checksum()); + + CHECK (coll[0].acc(0) == 0 + rr); + CHECK (coll[1].acc(0) == 1 + rr + trigger); + CHECK (coll[2].acc(0) == 2 + rr); + CHECK (coll[3].acc(0) == 3 + rr + trigger); + CHECK (coll[4].acc(0) == 4 + rr); + CHECK (coll[5].acc(0) == 5 + rr + trigger); + + coll.clear(); + CHECK (0 == Dummy::checksum()); + + // Verify Error handling while in creation: + // SubDummy explodes on equal ctor parameters + // which here happens for i==7 + VERIFY_ERROR (SUBVERSIVE, CollD(10, Populator(0), 7, _1 ) ); + + // any already created object was properly destroyed + CHECK (0 == Dummy::checksum()); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 + } + CHECK (0 == Dummy::checksum()); + } + + + void + building_StackStyle() + { + UNIMPLEMENTED ("pushing new objects successively"); + CHECK (0 == Dummy::checksum()); + { +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 + + int rr = rand() % 100; + + CollD coll(3); + CHECK (0 == coll.size()); + CHECK (0 == Dummy::checksum()); + + Dummy& d0 = coll.appendNew(); + CHECK (1 == coll.size()); + + Dummy& d1 = coll.appendNew<Dummy> (r1); + CHECK (2 == coll.size()); + + int sum = Dummy::checksum(); + + // trigger the bomb + VERIFY_ERROR (SUBVERSIVE, coll.appendNew<SubSummy>(r1,r1) ); + + CHECK ( 2 == coll.size()); // the other objects survived + CHECK (sum == Dummy::checksum()); + + Dummy& d2 = coll.appendNew<SubDummy> (r1, r1+1); + CHECK (3 == coll.size()); + + CHECK (sum + r1 == Dummy::checksum()); + + VERIFY_ERROR (CAPACITY, coll.appendNew()); + VERIFY_ERROR (CAPACITY, coll.appendNew()); + VERIFY_ERROR (CAPACITY, coll.appendNew()); + + CHECK (3 == coll.size()); + CHECK (sum + r1 == Dummy::checksum()); + + + CHECK (d0.acc(11) == coll[0].getVal() + 11 ); + CHECK (d1.acc(22) == r1 + 22); + CHECK (d2.acc(33) == r1 + 33); + CHECK (d2.acc(0) == r1 + (r1+1) ); // SubDummy's special implementation of the acc()-function + // returns the trigger value, when the argument is zero + + coll.clear(); + coll.appendNew<SubDummy> (11,22); + + CHECK (3 == coll.size()); + CHECK (11 == Dummy::checksum()); + + // NOTE DANGEROUS: + // The previously obtained references just point into the object storage. + // Thus we're now accessing a different object, even a different type! + CHECK (d0.acc(0) == 11 + 22); + + // The others even point into obsoleted storage holding zombie objects + CHECK (d1.acc(44) == r1 + 44); + +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 + } + CHECK (0 == Dummy::checksum()); + } + }; + + + LAUNCHER (ScopedCollection_test, "unit common"); + + +}} // namespace lib::test + diff --git a/tests/lib/scoped-holder-test.cpp b/tests/lib/scoped-holder-test.cpp index 40b2637e7..3815d86a3 100644 --- a/tests/lib/scoped-holder-test.cpp +++ b/tests/lib/scoped-holder-test.cpp @@ -96,15 +96,15 @@ namespace test{ CHECK (false!=holder); CHECK (holder!=false); - CHECK (0 != Dummy::checksum()); + CHECK (0 < Dummy::checksum()); CHECK ( &(*holder)); - CHECK (holder->add(2) == 2 + Dummy::checksum()); + CHECK (holder->acc(2) == 2 + Dummy::checksum()); Dummy *rawP = holder.get(); CHECK (rawP); CHECK (holder); CHECK (rawP == &(*holder)); - CHECK (rawP->add(-5) == holder->add(-5)); + CHECK (rawP->acc(-5) == holder->acc(-5)); TRACE (test, "holder at %p", &holder); TRACE (test, "object at %p", holder.get() ); @@ -127,7 +127,7 @@ namespace test{ try { create_contained_object (holder); - NOTREACHED (); + NOTREACHED ("expect failure in ctor"); } catch (int val) { @@ -221,13 +221,13 @@ namespace test{ { create_contained_object (maph[i]); CHECK (maph[i]); - CHECK (0 < maph[i]->add(12)); + CHECK (0 < maph[i]->acc(12)); } CHECK (100==maph.size()); CHECK (0 != Dummy::checksum()); - long value55 = maph[55]->add(0); + long value55 = maph[55]->acc(0); long currSum = Dummy::checksum(); CHECK (1 == maph.erase(55)); diff --git a/tests/lib/scoped-holder-transfer-test.cpp b/tests/lib/scoped-holder-transfer-test.cpp index 36b1bdd2c..add399caa 100644 --- a/tests/lib/scoped-holder-transfer-test.cpp +++ b/tests/lib/scoped-holder-transfer-test.cpp @@ -143,7 +143,7 @@ namespace test { CHECK (rawP); CHECK (table[5]); CHECK (rawP == &(*table[5])); - CHECK (rawP->add(-555) == table[5]->add(-555)); + CHECK (rawP->acc(-555) == table[5]->acc(-555)); } CHECK (0 == Dummy::checksum()); } From 6c01579a26e395f1d2b4b727e051570169977edd Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Mon, 2 Jan 2012 06:11:27 +0100 Subject: [PATCH 344/496] WIP draft implementation of ScopedCollection --- src/lib/error.hpp | 1 + src/lib/exception.cpp | 1 + src/lib/scoped-collection.hpp | 263 ++++++++++++++++++++++----- tests/lib/scoped-collection-test.cpp | 2 +- 4 files changed, 218 insertions(+), 49 deletions(-) diff --git a/src/lib/error.hpp b/src/lib/error.hpp index 110ffe61b..1677557be 100644 --- a/src/lib/error.hpp +++ b/src/lib/error.hpp @@ -125,6 +125,7 @@ namespace lumiera { 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 (INDEX_BOUNDS); ///< index out of bounds LUMIERA_ERROR_DECLARE (BOTTOM_VALUE); ///< invalid or NIL value LUMIERA_ERROR_DECLARE (UNCONNECTED); ///< missing connection LUMIERA_ERROR_DECLARE (UNIMPLEMENTED);///< unimplemented feature diff --git a/src/lib/exception.cpp b/src/lib/exception.cpp index 8c524e5d4..26ddaadb2 100644 --- a/src/lib/exception.cpp +++ b/src/lib/exception.cpp @@ -82,6 +82,7 @@ namespace lumiera { 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 (INDEX_BOUNDS, "index out of bounds"); LUMIERA_ERROR_DEFINE (BOTTOM_VALUE, "invalid or NIL value"); LUMIERA_ERROR_DEFINE (UNCONNECTED, "missing connection"); LUMIERA_ERROR_DEFINE (UNIMPLEMENTED, "using a feature not yet implemented...."); diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp index 8a2fb81e0..f0f8aac34 100644 --- a/src/lib/scoped-collection.hpp +++ b/src/lib/scoped-collection.hpp @@ -41,117 +41,284 @@ //#include "include/logging.h" -//#include "lib/iter-adapter.hpp" +#include "lib/iter-adapter.hpp" #include "lib/error.hpp" //#include "lib/util.hpp" //#include <vector> //#include <algorithm> #include <boost/noncopyable.hpp> +#include <boost/scoped_array.hpp> +#include <boost/static_assert.hpp> namespace lib { + namespace error = lumiera::error; + using error::LUMIERA_ERROR_INDEX_BOUNDS; + /** * A fixed collection of noncopyable polymorphic objects. + * All child objects reside in a common chunk of storage * and are owned and managed by this collection holder. * Array style access and iteration. */ - template<class T> + template + < class I ///< the nominal Base/Interface class for a family of types + , size_t siz = sizeof(I) ///< maximum storage required for the targets to be held inline + > class ScopedCollection : boost::noncopyable { + + /** + * Wrapper to hold one Child object. + * The storage will be an heap allocated + * array of such Wrapper objects. + */ + class ElementHolder + : boost::noncopyable + { + + mutable char buf_[siz]; + + public: + ElementHolder () { } + ~ElementHolder () + { + destroy(); + } + + I& + accessObj() const + { + return reinterpret_cast<I&> (buf_); + } + + void + destroy() + { + accessObj().~I(); + } + + I& + operator* () const + { + return accessObj(); + } + + + + /** Abbreviation for placement new */ +#define EMBEDDED_ELEMENT_CTOR(_CTOR_CALL_) \ + BOOST_STATIC_ASSERT (siz >= sizeof(TY));\ + return *new(&buf_) _CTOR_CALL_; \ + + + template<class TY> + TY& + create () + { + EMBEDDED_ELEMENT_CTOR ( TY() ) + } + + + template<class TY, typename A1> + TY& //___________________________________________ + create (A1& a1) ///< place object of type TY, using 1-arg ctor + { + EMBEDDED_ELEMENT_CTOR ( TY(a1) ) + } + + + template< class TY + , typename A1 + , typename A2 + > + TY& //___________________________________________ + create (A1& a1, A2& a2) ///< place object of type TY, using 2-arg ctor + { + EMBEDDED_ELEMENT_CTOR ( TY(a1,a2) ) + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + > + TY& //___________________________________________ + create (A1& a1, A2& a2, A3& a3) ///< place object of type TY, using 3-arg ctor + { + EMBEDDED_ELEMENT_CTOR ( TY(a1,a2,a3) ) + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + , typename A4 + > + TY& //___________________________________________ + create (A1& a1, A2& a2, A3& a3, A4& a4) ///< place object of type TY, using 4-arg ctor + { + EMBEDDED_ELEMENT_CTOR ( TY(a1,a2,a3,a4) ) + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + , typename A4 + , typename A5 + > + TY& //___________________________________________ + create (A1& a1, A2& a2, A3& a3, A4& a4, A5& a5) ///< place object of type TY, using 5-arg ctor + { + EMBEDDED_ELEMENT_CTOR ( TY(a1,a2,a3,a4,a5) ) + } +#undef EMBEDDED_ELEMENT_CTOR + }; + + typedef boost::scoped_array<ElementHolder> ElementStorage; + + size_t level_; + size_t capacity_; + ElementStorage elements_; + // typedef std::vector<T*> _Vec; // typedef typename _Vec::iterator VIter; + typedef ElementHolder* StorageIter; // -// typedef RangeIter<VIter> RIter; -// typedef PtrDerefIter<RIter> IterType; -// -// typedef typename IterType::ConstIterType ConstIterType; -// typedef typename IterType::WrappedConstIterType RcIter; + typedef RangeIter<StorageIter> RIter; + typedef PtrDerefIter<RIter> IterType; + + typedef typename IterType::ConstIterType ConstIterType; + typedef typename IterType::WrappedConstIterType RcIter; public: - typedef size_t size_type; +// typedef size_t size_type; // typedef T & reference; // typedef T const& const_reference; - ScopedCollection () - { } - - explicit - ScopedCollection (size_type capacity) - { - UNIMPLEMENTED ("prepare scoped collection storage"); - } - ~ScopedCollection () { clear(); } + explicit + ScopedCollection (size_t maxElements) + : level_(0) + , capacity_(maxElements) + , elements_(new ElementHolder[maxElements]) + { } + + template<class CTOR> + ScopedCollection (size_t maxElements, CTOR builder) + : level_(0) + , capacity_(maxElements) + , elements_(new ElementHolder[maxElements]) + { + UNIMPLEMENTED ("use the builder to populate the elements right away"); + } void clear() { - UNIMPLEMENTED ("drop all content objects"); + REQUIRE (level_ <= capacity_, "Storage corrupted"); + + while (level_) + { + --level_; + try { + elements_[level_].destroy(); + } + ERROR_LOG_AND_IGNORE (progress, "Clean-up of element in ScopedCollection") + } } + void + populate() + try { + while (level_ < capacity_) + { + elements_[level_].template create<I>(); + ++level_; + } + } + catch(...) + { + WARN (progress, "Failure while populating ScopedCollection. " + "All elements will be discarded"); + clear(); + throw; + } + /* === Element access and iteration === */ - T& - operator[] (size_type i) + I& + operator[] (size_t index) { - return *get(i); + if (index < level_) + return elements_[index]; + + throw error::Logic ("Attempt to access not (yet) existing object in ScopedCollection" + , LUMIERA_ERROR_INDEX_BOUNDS); } -// typedef IterType iterator; -// typedef ConstIterType const_iterator; -// -// iterator begin() { return iterator (allPtrs()); } -// iterator end() { return iterator ( RIter() ); } -// const_iterator begin() const { return const_iterator::build_by_cast (allPtrs()); } -// const_iterator end() const { return const_iterator::nil(); } + typedef IterType iterator; + typedef ConstIterType const_iterator; + + iterator begin() { return iterator (allPtrs()); } + iterator end() { return iterator ( RIter() ); } + const_iterator begin() const { return const_iterator::build_by_cast (allPtrs()); } + const_iterator end() const { return const_iterator::nil(); } /* ====== proxied vector functions ==================== */ -// size_type size () const { return _Vec::size(); } + size_t size () const { return level_; } // size_type max_size () const { return _Vec::max_size(); } // size_type capacity () const { return _Vec::capacity(); } -// bool empty () const { return _Vec::empty(); } + bool empty () const { return 0 == level_; } private: - /** @internal element access, including range and null check */ - T* - get (size_type i) - { - UNIMPLEMENTED("raw element access"); - } +// /** @internal element access, including range and null check */ +// T* +// get (size_type i) +// { +// UNIMPLEMENTED("raw element access"); +// } -// /** @internal access sequence of all managed pointers */ -// RIter -// allPtrs () -// { -// return RIter (_Vec::begin(), _Vec::end()); -// } -// RIter -// allPtrs () const -// { -// _Vec& elements = util::unConst(*this); -// return RIter (elements.begin(), elements.end()); -// } + /** @internal access sequence of all managed pointers */ + RIter + allPtrs () + { + ElementHolder * storage_begin = elements_.get(); + ElementHolder * storage_end = storage_begin + capacity_; + + return RIter (storage_begin, storage_end); + } + RIter + allPtrs () const + { + ScopedCollection& elements = util::unConst(*this); + return RIter (elements.begin(), elements.end()); + } }; diff --git a/tests/lib/scoped-collection-test.cpp b/tests/lib/scoped-collection-test.cpp index f712c239e..29eddda2f 100644 --- a/tests/lib/scoped-collection-test.cpp +++ b/tests/lib/scoped-collection-test.cpp @@ -117,7 +117,7 @@ namespace test{ CHECK (5 == container.size()); CHECK (0 != Dummy::checksum()); - holder.clear(); + container.clear(); CHECK (isnil (container)); CHECK (0 == container.size()); CHECK (0 == Dummy::checksum()); From fa5e7db2d397f2a111d2a1ca4e50f1dd908c933a Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Tue, 3 Jan 2012 02:50:22 +0100 Subject: [PATCH 345/496] WIP rewrite iteration-control logic... --- src/lib/scoped-collection.hpp | 68 ++++++++++++++++------------ tests/lib/scoped-collection-test.cpp | 2 - 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp index f0f8aac34..d9b80b469 100644 --- a/src/lib/scoped-collection.hpp +++ b/src/lib/scoped-collection.hpp @@ -191,15 +191,42 @@ namespace lib { size_t capacity_; ElementStorage elements_; -// typedef std::vector<T*> _Vec; -// typedef typename _Vec::iterator VIter; - typedef ElementHolder* StorageIter; -// - typedef RangeIter<StorageIter> RIter; - typedef PtrDerefIter<RIter> IterType; - typedef typename IterType::ConstIterType ConstIterType; - typedef typename IterType::WrappedConstIterType RcIter; + typedef IterAdapter< I *, const ScopedCollection *> IterType; + typedef IterAdapter<const I *, const ScopedCollection *> ConstIterType; + + /* ==== internal callback API for the iterator ==== */ + + friend void + iterNext (const ScopedCollection*, const I* & pos) + { + ElementHolder* & storageLocation = reinterpret_cast<ElementHolder* &> (pos); + ++storageLocation; + } + + /** Implementation of Iteration-logic: detect iteration end. + * @note the problem here is that this implementation chooses + * to use two representations of "bottom" (end, invalid). + * The reason is, we want the default-constructed IterAdapter + * also be the "bottom" value. Thus, when we detect the + * iteration end by internal logic (\c numberz_.end() ), we + * immediately transform this into the official "bottom" + */ + friend bool + hasNext (const ScopedCollection* src, const I* & pos) + { + REQUIRE (src); + if ((pos) && (pos < src->_access_end())) + return true; + else + { + pos = 0; + return false; + } } + + I* _access_begin() { return &elements_[0]; } + I* _access_end() { return &elements_[level_]; } + public: @@ -271,7 +298,7 @@ namespace lib { operator[] (size_t index) { if (index < level_) - return elements_[index]; + return elements_[index].accessObj(); throw error::Logic ("Attempt to access not (yet) existing object in ScopedCollection" , LUMIERA_ERROR_INDEX_BOUNDS); @@ -280,10 +307,10 @@ namespace lib { typedef IterType iterator; typedef ConstIterType const_iterator; - iterator begin() { return iterator (allPtrs()); } - iterator end() { return iterator ( RIter() ); } - const_iterator begin() const { return const_iterator::build_by_cast (allPtrs()); } - const_iterator end() const { return const_iterator::nil(); } + iterator begin () { return iterator (this, _access_begin()); } + const_iterator begin () const { return const_iterator (this, _access_begin()); } + iterator end () { return iterator(); } + const_iterator end () const { return const_iterator(); } @@ -304,21 +331,6 @@ namespace lib { // UNIMPLEMENTED("raw element access"); // } - /** @internal access sequence of all managed pointers */ - RIter - allPtrs () - { - ElementHolder * storage_begin = elements_.get(); - ElementHolder * storage_end = storage_begin + capacity_; - - return RIter (storage_begin, storage_end); - } - RIter - allPtrs () const - { - ScopedCollection& elements = util::unConst(*this); - return RIter (elements.begin(), elements.end()); - } }; diff --git a/tests/lib/scoped-collection-test.cpp b/tests/lib/scoped-collection-test.cpp index 29eddda2f..8847ee15f 100644 --- a/tests/lib/scoped-collection-test.cpp +++ b/tests/lib/scoped-collection-test.cpp @@ -106,7 +106,6 @@ namespace test{ { CHECK (0 == Dummy::checksum()); { -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 CollD container(5); CHECK (isnil (container)); CHECK (0 == container.size()); @@ -128,7 +127,6 @@ namespace test{ + container[2].getVal() + container[3].getVal() + container[4].getVal()); -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 } CHECK (0 == Dummy::checksum()); } From 57741a94d970b311dd23bb228dbc3297cf425d93 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 4 Jan 2012 03:30:34 +0100 Subject: [PATCH 346/496] fix random init error (division by zero possible) --- tests/components/proc/asset/meta/time-grid-basics-test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/proc/asset/meta/time-grid-basics-test.cpp b/tests/components/proc/asset/meta/time-grid-basics-test.cpp index ea98b04bc..e90eb794a 100644 --- a/tests/components/proc/asset/meta/time-grid-basics-test.cpp +++ b/tests/components/proc/asset/meta/time-grid-basics-test.cpp @@ -103,7 +103,7 @@ namespace test { Time point (myGrid->timeOf (randomFrame)); CHECK (point == testOrigin + randomFrame * testFps.duration()); - uint fract = rand() % DIRT_GRAIN; + uint fract = 1 + rand() % DIRT_GRAIN; FSecs dirt = rational_cast<FSecs> (1 / testFps / fract); Time dirty(point + Time(dirt)); From 690304f9bcf307c018dc3b214ac329227d7df205 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 4 Jan 2012 04:05:03 +0100 Subject: [PATCH 347/496] get the simple and iteration unit tests to pass --- src/lib/error.hpp | 1 + src/lib/exception.cpp | 1 + src/lib/scoped-collection.hpp | 130 ++++++++++++++++++++++++--- src/lib/scoped-ptrvect.hpp | 2 + tests/lib/scoped-collection-test.cpp | 9 +- 5 files changed, 125 insertions(+), 18 deletions(-) diff --git a/src/lib/error.hpp b/src/lib/error.hpp index 1677557be..364fd1fe6 100644 --- a/src/lib/error.hpp +++ b/src/lib/error.hpp @@ -125,6 +125,7 @@ namespace lumiera { 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 (CAPACITY); ///< predefined fixed storage capacity LUMIERA_ERROR_DECLARE (INDEX_BOUNDS); ///< index out of bounds LUMIERA_ERROR_DECLARE (BOTTOM_VALUE); ///< invalid or NIL value LUMIERA_ERROR_DECLARE (UNCONNECTED); ///< missing connection diff --git a/src/lib/exception.cpp b/src/lib/exception.cpp index 26ddaadb2..53d068899 100644 --- a/src/lib/exception.cpp +++ b/src/lib/exception.cpp @@ -82,6 +82,7 @@ namespace lumiera { 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 (CAPACITY, "predefined fixed storage capacity"); LUMIERA_ERROR_DEFINE (INDEX_BOUNDS, "index out of bounds"); LUMIERA_ERROR_DEFINE (BOTTOM_VALUE, "invalid or NIL value"); LUMIERA_ERROR_DEFINE (UNCONNECTED, "missing connection"); diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp index d9b80b469..adc2339f5 100644 --- a/src/lib/scoped-collection.hpp +++ b/src/lib/scoped-collection.hpp @@ -31,6 +31,8 @@ ** ** - TODO: retro-fit with RefArray interface ** + ** @warning deliberately \em not threadsafe + ** ** @see ScopedCollection_test ** @see scoped-ptrvect.hpp quite similar, but using individual heap pointers */ @@ -55,6 +57,7 @@ namespace lib { namespace error = lumiera::error; + using error::LUMIERA_ERROR_CAPACITY; using error::LUMIERA_ERROR_INDEX_BOUNDS; @@ -75,9 +78,10 @@ namespace lib { { /** - * Wrapper to hold one Child object. + * Storage Frame to hold one Child object. * The storage will be an heap allocated * array of such Wrapper objects. + * @note doesn't manage the Child */ class ElementHolder : boost::noncopyable @@ -86,11 +90,6 @@ namespace lib { mutable char buf_[siz]; public: - ElementHolder () { } - ~ElementHolder () - { - destroy(); - } I& accessObj() const @@ -198,11 +197,18 @@ namespace lib { /* ==== internal callback API for the iterator ==== */ friend void - iterNext (const ScopedCollection*, const I* & pos) + iterNext (const ScopedCollection*, I* & pos) { ElementHolder* & storageLocation = reinterpret_cast<ElementHolder* &> (pos); ++storageLocation; } + + friend void + iterNext (const ScopedCollection*, const I* & pos) + { + const ElementHolder* & storageLocation = reinterpret_cast<const ElementHolder* &> (pos); + ++storageLocation; + } /** Implementation of Iteration-logic: detect iteration end. * @note the problem here is that this implementation chooses @@ -212,8 +218,9 @@ namespace lib { * iteration end by internal logic (\c numberz_.end() ), we * immediately transform this into the official "bottom" */ + template<typename POS> friend bool - hasNext (const ScopedCollection* src, const I* & pos) + hasNext (const ScopedCollection* src, POS & pos) { REQUIRE (src); if ((pos) && (pos < src->_access_end())) @@ -224,8 +231,8 @@ namespace lib { return false; } } - I* _access_begin() { return &elements_[0]; } - I* _access_end() { return &elements_[level_]; } + I* _access_begin() const { return & elements_[0].accessObj(); } + I* _access_end() const { return & elements_[level_].accessObj(); } @@ -292,6 +299,98 @@ namespace lib { } + /** push a new element of default type + * to the end of this container + * @note EX_STRONG */ + I& appendNewElement() { return appendNew<I>(); } + + + template< class TY > + TY& //_________________________________________ + appendNew () ///< add object of type TY, using 0-arg ctor + { + __ensureSufficientCapacity(); + TY& newElm = elements_[level_].template create<TY>(); + ++level_; + return newElm; + } + + + template< class TY + , typename A1 + > + TY& //_________________________________________ + appendNew (A1& a1) ///< add object of type TY, using 1-arg ctor + { + __ensureSufficientCapacity(); + TY& newElm = elements_[level_].template create<TY>(a1); + ++level_; + return newElm; + } + + + template< class TY + , typename A1 + , typename A2 + > + TY& //_________________________________________ + appendNew (A1& a1, A2& a2) ///< add object of type TY, using 2-arg ctor + { + __ensureSufficientCapacity(); + TY& newElm = elements_[level_].template create<TY>(a1,a2); + ++level_; + return newElm; + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + > + TY& //_________________________________________ + appendNew (A1& a1, A2& a2, A3& a3) ///< add object of type TY, using 3-arg ctor + { + __ensureSufficientCapacity(); + TY& newElm = elements_[level_].template create<TY>(a1,a2,a3); + ++level_; + return newElm; + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + , typename A4 + > + TY& //_________________________________________ + appendNew (A1& a1, A2& a2, A3& a3, A4& a4) ///< add object of type TY, using 4-arg ctor + { + __ensureSufficientCapacity(); + TY& newElm = elements_[level_].template create<TY>(a1,a2,a3,a4); + ++level_; + return newElm; + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + , typename A4 + , typename A5 + > + TY& //_________________________________________ + appendNew (A1& a1, A2& a2, A3& a3, A4& a4, A5& a5) ///< add object of type TY, using 5-arg ctor + { + __ensureSufficientCapacity(); + TY& newElm = elements_[level_].template create<TY>(a1,a2,a3,a4,a5); + ++level_; + return newElm; + } + + /* === Element access and iteration === */ I& @@ -317,9 +416,9 @@ namespace lib { /* ====== proxied vector functions ==================== */ - size_t size () const { return level_; } + size_t size () const { return level_; } // size_type max_size () const { return _Vec::max_size(); } -// size_type capacity () const { return _Vec::capacity(); } + size_t capacity () const { return capacity_; } bool empty () const { return 0 == level_; } @@ -330,6 +429,13 @@ namespace lib { // { // UNIMPLEMENTED("raw element access"); // } + void + __ensureSufficientCapacity() + { + if (level_ >= capacity_) + throw error::State ("ScopedCollection exceeding the initially defined capacity" + , LUMIERA_ERROR_CAPACITY); + } }; diff --git a/src/lib/scoped-ptrvect.hpp b/src/lib/scoped-ptrvect.hpp index dd04492a6..37dff93dd 100644 --- a/src/lib/scoped-ptrvect.hpp +++ b/src/lib/scoped-ptrvect.hpp @@ -35,6 +35,8 @@ ** - TODO: detaching of objects... ** - TODO: retro-fit with RefArray interface ** + ** @warning deliberately \em not threadsafe + ** ** @see scoped-ptrvect-test.cpp ** @see scoped-holder.hpp ** @see gui::DisplayService usage example diff --git a/tests/lib/scoped-collection-test.cpp b/tests/lib/scoped-collection-test.cpp index 8847ee15f..c79f74e89 100644 --- a/tests/lib/scoped-collection-test.cpp +++ b/tests/lib/scoped-collection-test.cpp @@ -137,9 +137,8 @@ namespace test{ { CHECK (0 == Dummy::checksum()); { -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 CollD coll(50); - for (int i=0; i<coll.capacity(); ++i) + for (uint i=0; i<coll.capacity(); ++i) coll.appendNew<Dummy>(i); int check=0; @@ -154,8 +153,9 @@ namespace test{ // Test the const iterator + CollD const& const_coll (coll); check = 0; - CollD::const_iterator cii = coll.begin(); + CollD::const_iterator cii = const_coll.begin(); while (cii) { CHECK (check == cii->getVal()); @@ -176,7 +176,6 @@ namespace test{ VERIFY_ERROR (ITER_EXHAUST, ++ii ); VERIFY_ERROR (ITER_EXHAUST, ++cii ); -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 } CHECK (0 == Dummy::checksum()); } @@ -185,7 +184,6 @@ namespace test{ void building_RAII_Style() { - UNIMPLEMENTED ("building all at once"); CHECK (0 == Dummy::checksum()); { #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 @@ -252,7 +250,6 @@ namespace test{ void building_StackStyle() { - UNIMPLEMENTED ("pushing new objects successively"); CHECK (0 == Dummy::checksum()); { #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 From 434a371c33288907055e9e0ba655a90a65a28d15 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 4 Jan 2012 04:28:14 +0100 Subject: [PATCH 348/496] take ctor args by value; test attaching sub-types passes --- src/lib/scoped-collection.hpp | 137 +++++++++++++-------------- tests/lib/scoped-collection-test.cpp | 30 +++--- 2 files changed, 78 insertions(+), 89 deletions(-) diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp index adc2339f5..d240a39ee 100644 --- a/src/lib/scoped-collection.hpp +++ b/src/lib/scoped-collection.hpp @@ -127,7 +127,7 @@ namespace lib { template<class TY, typename A1> TY& //___________________________________________ - create (A1& a1) ///< place object of type TY, using 1-arg ctor + create (A1 a1) ///< place object of type TY, using 1-arg ctor { EMBEDDED_ELEMENT_CTOR ( TY(a1) ) } @@ -138,7 +138,7 @@ namespace lib { , typename A2 > TY& //___________________________________________ - create (A1& a1, A2& a2) ///< place object of type TY, using 2-arg ctor + create (A1 a1, A2 a2) ///< place object of type TY, using 2-arg ctor { EMBEDDED_ELEMENT_CTOR ( TY(a1,a2) ) } @@ -150,7 +150,7 @@ namespace lib { , typename A3 > TY& //___________________________________________ - create (A1& a1, A2& a2, A3& a3) ///< place object of type TY, using 3-arg ctor + create (A1 a1, A2 a2, A3 a3) ///< place object of type TY, using 3-arg ctor { EMBEDDED_ELEMENT_CTOR ( TY(a1,a2,a3) ) } @@ -163,7 +163,7 @@ namespace lib { , typename A4 > TY& //___________________________________________ - create (A1& a1, A2& a2, A3& a3, A4& a4) ///< place object of type TY, using 4-arg ctor + create (A1 a1, A2 a2, A3 a3, A4 a4) ///< place object of type TY, using 4-arg ctor { EMBEDDED_ELEMENT_CTOR ( TY(a1,a2,a3,a4) ) } @@ -177,13 +177,17 @@ namespace lib { , typename A5 > TY& //___________________________________________ - create (A1& a1, A2& a2, A3& a3, A4& a4, A5& a5) ///< place object of type TY, using 5-arg ctor + create (A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) ///< place object of type TY, using 5-arg ctor { EMBEDDED_ELEMENT_CTOR ( TY(a1,a2,a3,a4,a5) ) } #undef EMBEDDED_ELEMENT_CTOR }; + + + /* ==== Storage: heap allocated array of element buffers ==== */ + typedef boost::scoped_array<ElementHolder> ElementStorage; size_t level_; @@ -194,54 +198,9 @@ namespace lib { typedef IterAdapter< I *, const ScopedCollection *> IterType; typedef IterAdapter<const I *, const ScopedCollection *> ConstIterType; - /* ==== internal callback API for the iterator ==== */ - - friend void - iterNext (const ScopedCollection*, I* & pos) - { - ElementHolder* & storageLocation = reinterpret_cast<ElementHolder* &> (pos); - ++storageLocation; - } - - friend void - iterNext (const ScopedCollection*, const I* & pos) - { - const ElementHolder* & storageLocation = reinterpret_cast<const ElementHolder* &> (pos); - ++storageLocation; - } - - /** Implementation of Iteration-logic: detect iteration end. - * @note the problem here is that this implementation chooses - * to use two representations of "bottom" (end, invalid). - * The reason is, we want the default-constructed IterAdapter - * also be the "bottom" value. Thus, when we detect the - * iteration end by internal logic (\c numberz_.end() ), we - * immediately transform this into the official "bottom" - */ - template<typename POS> - friend bool - hasNext (const ScopedCollection* src, POS & pos) - { - REQUIRE (src); - if ((pos) && (pos < src->_access_end())) - return true; - else - { - pos = 0; - return false; - } } - - I* _access_begin() const { return & elements_[0].accessObj(); } - I* _access_end() const { return & elements_[level_].accessObj(); } - public: -// typedef size_t size_type; -// typedef T & reference; -// typedef T const& const_reference; - - ~ScopedCollection () { @@ -299,6 +258,7 @@ namespace lib { } + /** push a new element of default type * to the end of this container * @note EX_STRONG */ @@ -320,7 +280,7 @@ namespace lib { , typename A1 > TY& //_________________________________________ - appendNew (A1& a1) ///< add object of type TY, using 1-arg ctor + appendNew (A1 a1) ///< add object of type TY, using 1-arg ctor { __ensureSufficientCapacity(); TY& newElm = elements_[level_].template create<TY>(a1); @@ -334,7 +294,7 @@ namespace lib { , typename A2 > TY& //_________________________________________ - appendNew (A1& a1, A2& a2) ///< add object of type TY, using 2-arg ctor + appendNew (A1 a1, A2 a2) ///< add object of type TY, using 2-arg ctor { __ensureSufficientCapacity(); TY& newElm = elements_[level_].template create<TY>(a1,a2); @@ -349,7 +309,7 @@ namespace lib { , typename A3 > TY& //_________________________________________ - appendNew (A1& a1, A2& a2, A3& a3) ///< add object of type TY, using 3-arg ctor + appendNew (A1 a1, A2 a2, A3 a3) ///< add object of type TY, using 3-arg ctor { __ensureSufficientCapacity(); TY& newElm = elements_[level_].template create<TY>(a1,a2,a3); @@ -365,7 +325,7 @@ namespace lib { , typename A4 > TY& //_________________________________________ - appendNew (A1& a1, A2& a2, A3& a3, A4& a4) ///< add object of type TY, using 4-arg ctor + appendNew (A1 a1, A2 a2, A3 a3, A4 a4) ///< add object of type TY, using 4-arg ctor { __ensureSufficientCapacity(); TY& newElm = elements_[level_].template create<TY>(a1,a2,a3,a4); @@ -382,7 +342,7 @@ namespace lib { , typename A5 > TY& //_________________________________________ - appendNew (A1& a1, A2& a2, A3& a3, A4& a4, A5& a5) ///< add object of type TY, using 5-arg ctor + appendNew (A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) ///< add object of type TY, using 5-arg ctor { __ensureSufficientCapacity(); TY& newElm = elements_[level_].template create<TY>(a1,a2,a3,a4,a5); @@ -391,6 +351,7 @@ namespace lib { } + /* === Element access and iteration === */ I& @@ -403,32 +364,23 @@ namespace lib { , LUMIERA_ERROR_INDEX_BOUNDS); } + + typedef IterType iterator; typedef ConstIterType const_iterator; - iterator begin () { return iterator (this, _access_begin()); } - const_iterator begin () const { return const_iterator (this, _access_begin()); } - iterator end () { return iterator(); } - const_iterator end () const { return const_iterator(); } + iterator begin() { return iterator (this, _access_begin()); } + const_iterator begin() const { return const_iterator (this, _access_begin()); } + iterator end () { return iterator(); } + const_iterator end () const { return const_iterator(); } - - - /* ====== proxied vector functions ==================== */ - - size_t size () const { return level_; } -// size_type max_size () const { return _Vec::max_size(); } - size_t capacity () const { return capacity_; } - bool empty () const { return 0 == level_; } + size_t size () const { return level_; } + size_t capacity () const { return capacity_; } + bool empty () const { return 0 == level_; } private: -// /** @internal element access, including range and null check */ -// T* -// get (size_type i) -// { -// UNIMPLEMENTED("raw element access"); -// } void __ensureSufficientCapacity() { @@ -437,6 +389,45 @@ namespace lib { , LUMIERA_ERROR_CAPACITY); } + + /* ==== internal callback API for the iterator ==== */ + + /** Iteration-logic: switch to next position + * @note assuming here that the start address of the embedded object + * coincides with the start of an array element (ElementHolder) + */ + friend void + iterNext (const ScopedCollection*, I* & pos) + { + ElementHolder* & storageLocation = reinterpret_cast<ElementHolder* &> (pos); + ++storageLocation; + } + + friend void + iterNext (const ScopedCollection*, const I* & pos) + { + const ElementHolder* & storageLocation = reinterpret_cast<const ElementHolder* &> (pos); + ++storageLocation; + } + + /** Iteration-logic: detect iteration end. */ + template<typename POS> + friend bool + hasNext (const ScopedCollection* src, POS & pos) + { + REQUIRE (src); + if ((pos) && (pos < src->_access_end())) + return true; + else + { + pos = 0; + return false; + } } + + + I* _access_begin() const { return & elements_[0].accessObj(); } + I* _access_end() const { return & elements_[level_].accessObj(); } + }; diff --git a/tests/lib/scoped-collection-test.cpp b/tests/lib/scoped-collection-test.cpp index c79f74e89..f4ea3db91 100644 --- a/tests/lib/scoped-collection-test.cpp +++ b/tests/lib/scoped-collection-test.cpp @@ -252,7 +252,6 @@ namespace test{ { CHECK (0 == Dummy::checksum()); { -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 int rr = rand() % 100; @@ -260,43 +259,43 @@ namespace test{ CHECK (0 == coll.size()); CHECK (0 == Dummy::checksum()); - Dummy& d0 = coll.appendNew(); + Dummy& d0 = coll.appendNewElement(); CHECK (1 == coll.size()); - Dummy& d1 = coll.appendNew<Dummy> (r1); + Dummy& d1 = coll.appendNew<Dummy> (rr); CHECK (2 == coll.size()); int sum = Dummy::checksum(); // trigger the bomb - VERIFY_ERROR (SUBVERSIVE, coll.appendNew<SubSummy>(r1,r1) ); + VERIFY_ERROR (SUBVERSIVE, coll.appendNew<SubDummy>(rr,rr) ); CHECK ( 2 == coll.size()); // the other objects survived CHECK (sum == Dummy::checksum()); - Dummy& d2 = coll.appendNew<SubDummy> (r1, r1+1); + Dummy& d2 = coll.appendNew<SubDummy> (rr, rr+1); CHECK (3 == coll.size()); - CHECK (sum + r1 == Dummy::checksum()); + CHECK (sum + rr == Dummy::checksum()); - VERIFY_ERROR (CAPACITY, coll.appendNew()); - VERIFY_ERROR (CAPACITY, coll.appendNew()); - VERIFY_ERROR (CAPACITY, coll.appendNew()); + VERIFY_ERROR (CAPACITY, coll.appendNewElement()); + VERIFY_ERROR (CAPACITY, coll.appendNewElement()); + VERIFY_ERROR (CAPACITY, coll.appendNewElement()); CHECK (3 == coll.size()); - CHECK (sum + r1 == Dummy::checksum()); + CHECK (sum + rr == Dummy::checksum()); CHECK (d0.acc(11) == coll[0].getVal() + 11 ); - CHECK (d1.acc(22) == r1 + 22); - CHECK (d2.acc(33) == r1 + 33); - CHECK (d2.acc(0) == r1 + (r1+1) ); // SubDummy's special implementation of the acc()-function + CHECK (d1.acc(22) == rr + 22); + CHECK (d2.acc(33) == rr + 33); + CHECK (d2.acc(0) == rr + (rr+1) ); // SubDummy's special implementation of the acc()-function // returns the trigger value, when the argument is zero coll.clear(); coll.appendNew<SubDummy> (11,22); - CHECK (3 == coll.size()); + CHECK ( 1 == coll.size()); CHECK (11 == Dummy::checksum()); // NOTE DANGEROUS: @@ -305,9 +304,8 @@ namespace test{ CHECK (d0.acc(0) == 11 + 22); // The others even point into obsoleted storage holding zombie objects - CHECK (d1.acc(44) == r1 + 44); + CHECK (d1.acc(44) == rr + 44); -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 } CHECK (0 == Dummy::checksum()); } From 72e8d224540cdb9e2a602a46c1a88b0a95e9c822 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 5 Jan 2012 03:40:04 +0100 Subject: [PATCH 349/496] implement the RAII-style collection ctor. test pass --- src/lib/scoped-collection.hpp | 54 +++++++------- tests/lib/scoped-collection-test.cpp | 102 +++++++++++++++++---------- 2 files changed, 93 insertions(+), 63 deletions(-) diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp index d240a39ee..52f272c06 100644 --- a/src/lib/scoped-collection.hpp +++ b/src/lib/scoped-collection.hpp @@ -64,7 +64,7 @@ namespace lib { /** * A fixed collection of noncopyable polymorphic objects. - + * * All child objects reside in a common chunk of storage * and are owned and managed by this collection holder. * Array style access and iteration. @@ -77,6 +77,7 @@ namespace lib { : boost::noncopyable { + public: /** * Storage Frame to hold one Child object. * The storage will be an heap allocated @@ -103,11 +104,6 @@ namespace lib { accessObj().~I(); } - I& - operator* () const - { - return accessObj(); - } @@ -186,21 +182,8 @@ namespace lib { - /* ==== Storage: heap allocated array of element buffers ==== */ - - typedef boost::scoped_array<ElementHolder> ElementStorage; - - size_t level_; - size_t capacity_; - ElementStorage elements_; - typedef IterAdapter< I *, const ScopedCollection *> IterType; - typedef IterAdapter<const I *, const ScopedCollection *> ConstIterType; - - - - public: ~ScopedCollection () { @@ -219,10 +202,19 @@ namespace lib { : level_(0) , capacity_(maxElements) , elements_(new ElementHolder[maxElements]) - { - UNIMPLEMENTED ("use the builder to populate the elements right away"); - } - + { + try { + while (level_ < capacity_) + { + ElementHolder& storageFrame (elements_[level_]); + builder (storageFrame); + ++level_; + }} + catch(...) + { + clear(); + throw; + } } void @@ -366,8 +358,9 @@ namespace lib { - typedef IterType iterator; - typedef ConstIterType const_iterator; + typedef IterAdapter< I *, const ScopedCollection *> iterator; + typedef IterAdapter<const I *, const ScopedCollection *> const_iterator; + iterator begin() { return iterator (this, _access_begin()); } const_iterator begin() const { return const_iterator (this, _access_begin()); } @@ -380,7 +373,18 @@ namespace lib { bool empty () const { return 0 == level_; } + private: + /* ==== Storage: heap allocated array of element buffers ==== */ + + typedef boost::scoped_array<ElementHolder> ElementStorage; + + size_t level_; + size_t capacity_; + ElementStorage elements_; + + + void __ensureSufficientCapacity() { diff --git a/tests/lib/scoped-collection-test.cpp b/tests/lib/scoped-collection-test.cpp index f4ea3db91..acfdf99e0 100644 --- a/tests/lib/scoped-collection-test.cpp +++ b/tests/lib/scoped-collection-test.cpp @@ -75,7 +75,6 @@ namespace test{ using util::isnil; -//using std::tr1::placeholders::_1; /////////////////////////////TODO using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST; typedef ScopedCollection<Dummy> CollD; @@ -115,7 +114,7 @@ namespace test{ CHECK (!isnil (container)); CHECK (5 == container.size()); CHECK (0 != Dummy::checksum()); - + container.clear(); CHECK (isnil (container)); CHECK (0 == container.size()); @@ -179,46 +178,64 @@ namespace test{ } CHECK (0 == Dummy::checksum()); } + + + /** Functor to populate the Collection */ + class Populator + { + uint i_; + int off_; + int trigg_; + + public: + Populator (int baseOffset, int triggerCode) + : i_(0) + , off_(baseOffset) + , trigg_(triggerCode) + { } + + void + operator() (CollD::ElementHolder& storage) + { + switch (i_ % 2) + { + case 0: + storage.create<Dummy> (i_+off_); + break; + + case 1: + storage.create<SubDummy> (i_+off_, trigg_); + break; + } + ++i_; + } + }; - + /** @test using the ScopedCollection according to the RAII pattern. + * For this usage style, the collection is filled right away, during + * construction. If anything goes wrong, the whole collection is + * cleared and invalidated. Consequently there is no tangible "lifecycle" + * at the usage site. Either the collection is fully usable, or not at all. + * This requires the client to provide a functor (callback) to define + * the actual objects to be created within the ScopedCollection. These + * may as well be subclasses of the base type I, provided the general + * element storage size #siz was chosen sufficiently large to hold + * those subclass instances. + * + * This test demonstrates the most elaborate usage pattern, where + * the client provides a full blown functor object, which even + * has embedded state. But, generally speaking, anything + * exposing a suitable function call operator is acceptable. + */ void building_RAII_Style() { CHECK (0 == Dummy::checksum()); { -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 - /** Functor to populate the Collection */ - class Populator - { - uint i_; - int off_; - - public: - Populator (int baseOffset) - : i_(0) - , off_(baseOffset) - { } - - void - operator() (int specialOffset, void* storage) - { - switch (i_ % 2) - { - case 0: - new(storage) Dummy(i_+off_); - break; - - case 1: - new(storage) SubDummy(i_+off_, specialOffset); - break; - } } - }; - - int rr = rand() % 100; int trigger = 101; - CollD coll (6, Populator(rr), trigger, _1 ); + CollD coll (6, Populator(rr, trigger)); CHECK (!isnil (coll)); CHECK (6 == coll.size()); @@ -237,16 +254,25 @@ namespace test{ // Verify Error handling while in creation: // SubDummy explodes on equal ctor parameters // which here happens for i==7 - VERIFY_ERROR (SUBVERSIVE, CollD(10, Populator(0), 7, _1 ) ); + VERIFY_ERROR (SUBVERSIVE, CollD(10, Populator(0, 7)) ); // any already created object was properly destroyed CHECK (0 == Dummy::checksum()); -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877 + } CHECK (0 == Dummy::checksum()); } + /** @test using the ScopedCollection to hold a variable + * and possibly increasing number of elements, within the + * fixed limits of the maximum capacity defined by the + * ctor parameter. Any new elements will be created + * behind the already existing objects. In case + * of failure while creating an element, only + * this element gets destroyed, the rest of + * the container remains intact. + */ void building_StackStyle() { @@ -258,15 +284,15 @@ namespace test{ CollD coll(3); CHECK (0 == coll.size()); CHECK (0 == Dummy::checksum()); - + Dummy& d0 = coll.appendNewElement(); CHECK (1 == coll.size()); Dummy& d1 = coll.appendNew<Dummy> (rr); CHECK (2 == coll.size()); - + int sum = Dummy::checksum(); - + // trigger the bomb VERIFY_ERROR (SUBVERSIVE, coll.appendNew<SubDummy>(rr,rr) ); From e5c42e05e6748902dd1308b9ad9048fafca3aa3f Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Thu, 5 Jan 2012 23:17:16 +0100 Subject: [PATCH 350/496] finish ScopedCollection (closes #877) --- src/lib/scoped-collection.hpp | 131 +++++++++++++- tests/40components.tests | 2 +- tests/lib/scoped-collection-test.cpp | 250 ++++++++++++++++++--------- 3 files changed, 294 insertions(+), 89 deletions(-) diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp index 52f272c06..f3a1e313f 100644 --- a/src/lib/scoped-collection.hpp +++ b/src/lib/scoped-collection.hpp @@ -29,8 +29,35 @@ ** The storage holding all those child objects is allocated in one chunk ** and never adjusted. ** - ** - TODO: retro-fit with RefArray interface + ** \par usage patterns + ** The common ground for all usage of this container is to hold and some + ** with exclusive ownership; when the enclosing container goes out of scope, + ** all the dtors of the embedded objects will be invoked. Frequently this + ** side effect is the reason for using the container: we want to own some + ** resource handles to be available exactly as long as the managing object + ** needs and accesses them. ** + ** There are two different usage patterns for populating a ScopedCollection + ** - the "stack style" usage creates an empty container (using the one arg + ** ctor just to specify the maximum size). The storage to hold up to this + ** number of objects is (heap) allocated right away, but no objects are + ** created. Later on, individual objects are "pushed" into the collection + ** by invoking #appendNewElement() to create a new element of the default + ** type I) or #appendNew<Type>(args) to create some subtype. This way, + ** the container is being filled successively. + ** - the "RAII style" usage strives to create all of the content objects + ** right away, immediately after the memory allocation. This usage pattern + ** avoids any kind of "lifecylce state". Either the container comes up sane + ** and fully populated, or the ctor call fails and any already created + ** objects are discarded. + ** @note intentionally there is no operation to discard individual objects, + ** all you can do is to #clear() the whole container. + ** @note the container can hold instances of a subclass of the type defined + ** by the template parameter I. But you need to ensure in this case + ** that the defined buffer size for each element (2nt template parameter) + ** is sufficient to hold any of these subclass instances. This condition + ** is protected by a static assertion (compilation failure). + ** @warning when using subclasses, a virtual dtor is mandatory ** @warning deliberately \em not threadsafe ** ** @see ScopedCollection_test @@ -52,6 +79,8 @@ #include <boost/noncopyable.hpp> #include <boost/scoped_array.hpp> #include <boost/static_assert.hpp> +#include <boost/type_traits/is_same.hpp> +#include <boost/type_traits/is_base_of.hpp> namespace lib { @@ -87,7 +116,7 @@ namespace lib { class ElementHolder : boost::noncopyable { - + mutable char buf_[siz]; public: @@ -107,9 +136,13 @@ namespace lib { - /** Abbreviation for placement new */ +#define TYPE_SANITY_CHECK \ + BOOST_STATIC_ASSERT ((boost::is_base_of<I,TY>::value || boost::is_same<I,TY>::value)) + + + /** Abbreviation for placement new */ #define EMBEDDED_ELEMENT_CTOR(_CTOR_CALL_) \ - BOOST_STATIC_ASSERT (siz >= sizeof(TY));\ + TYPE_SANITY_CHECK; \ return *new(&buf_) _CTOR_CALL_; \ @@ -178,6 +211,7 @@ namespace lib { EMBEDDED_ELEMENT_CTOR ( TY(a1,a2,a3,a4,a5) ) } #undef EMBEDDED_ELEMENT_CTOR +#undef TYPE_SANITY_CHECK }; @@ -197,6 +231,13 @@ namespace lib { , elements_(new ElementHolder[maxElements]) { } + /** creating a ScopedCollection in RAII-style: + * The embedded elements will be created immediately. + * Ctor fails in case of any error during element creation. + * @param builder functor to be invoked for each "slot". + * It gets an ElementHolder& as parameter, and should + * use this to create an object of some I-subclass + */ template<class CTOR> ScopedCollection (size_t maxElements, CTOR builder) : level_(0) @@ -216,6 +257,22 @@ namespace lib { throw; } } + /* == some pre-defined Builders == */ + + class FillAll; ///< fills the ScopedCollection with default constructed I-instances + + template<typename TY> + class FillWith; ///< fills the ScopedCollection with default constructed TY-instances + + template<typename IT> + class PullFrom; ///< fills by copy-constructing values pulled from the iterator IT + + template<typename IT> + static PullFrom<IT> + pull (IT iter) ///< convenience shortcut to pull from any given Lumiera Forward Iterator + { + return PullFrom<IT> (iter); + } void clear() @@ -437,5 +494,71 @@ namespace lib { + /* === Supplement: pre-defined element builders === */ + + /** \par usage + * Pass an instance of this builder functor as 2nd parameter + * to ScopedCollections's ctor. (an anonymous instance is OK). + * Using this variant of the compiler switches the collection to RAII-style: + * It will immediately try to create all the embedded objects, invoking this + * builder functor for each "slot" to hold such an embedded object. Actually, + * this "slot" is an ElementHolder instance, which provides functions for + * placement-creating objects into this embedded buffer. + */ + template<class I, size_t siz> + class ScopedCollection<I,siz>::FillAll + { + public: + void + operator() (typename ScopedCollection<I,siz>::ElementHolder& storage) + { + storage.template create<I>(); + } + }; + + template<class I, size_t siz> + template<typename TY> + class ScopedCollection<I,siz>::FillWith + { + public: + void + operator() (typename ScopedCollection<I,siz>::ElementHolder& storage) + { + storage.template create<TY>(); + } + }; + + /** \par usage + * This variant allows to "pull" elements from an iterator. + * Actually, the collection will try to create each element right away, + * by invoking the copy ctor and passing the value yielded by the iterator. + * @note anything in accordance to the Lumera Forward Iterator pattern is OK. + * This rules out just passing a plain STL iterator (because these can't + * tell for themselves when they're exhausted). Use an suitable iter-adapter + * instead, e.g. by invoking lib::iter_stl::eachElm(stl_container) + */ + template<class I, size_t siz> + template<typename IT> + class ScopedCollection<I,siz>::PullFrom + { + IT iter_; + + typedef typename iter::TypeBinding<IT>::value_type ElementType; + + public: + PullFrom (IT source) + : iter_(source) + { } + + void + operator() (typename ScopedCollection<I,siz>::ElementHolder& storage) + { + storage.template create<ElementType> (*iter_); + ++iter_; + } + }; + + + } // namespace lib #endif diff --git a/tests/40components.tests b/tests/40components.tests index d22d69d07..b1a93d5d1 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -617,7 +617,7 @@ out: ^\.throw some exceptions... END -PLANNED "Managed Collection (I)" ScopedCollection_test <<END +TEST "Managed Collection (I)" ScopedCollection_test <<END return: 0 END diff --git a/tests/lib/scoped-collection-test.cpp b/tests/lib/scoped-collection-test.cpp index acfdf99e0..76c76c12e 100644 --- a/tests/lib/scoped-collection-test.cpp +++ b/tests/lib/scoped-collection-test.cpp @@ -67,8 +67,20 @@ namespace test{ if (trigger == getVal()) throw new error::Fatal ("Subversive Bomb", LUMIERA_ERROR_SUBVERSIVE); } + + SubDummy() + : Dummy() + , trigger_(-1) + { } }; + + inline uint + sum (uint n) + { + return n*(n+1) / 2; + } + }//(End) subversive test data @@ -97,6 +109,8 @@ namespace test{ building_RAII_Style(); building_StackStyle(); iterating(); + verify_defaultPopulator(); + verify_iteratorPopulator(); } @@ -178,90 +192,6 @@ namespace test{ } CHECK (0 == Dummy::checksum()); } - - - /** Functor to populate the Collection */ - class Populator - { - uint i_; - int off_; - int trigg_; - - public: - Populator (int baseOffset, int triggerCode) - : i_(0) - , off_(baseOffset) - , trigg_(triggerCode) - { } - - void - operator() (CollD::ElementHolder& storage) - { - switch (i_ % 2) - { - case 0: - storage.create<Dummy> (i_+off_); - break; - - case 1: - storage.create<SubDummy> (i_+off_, trigg_); - break; - } - ++i_; - } - }; - - /** @test using the ScopedCollection according to the RAII pattern. - * For this usage style, the collection is filled right away, during - * construction. If anything goes wrong, the whole collection is - * cleared and invalidated. Consequently there is no tangible "lifecycle" - * at the usage site. Either the collection is fully usable, or not at all. - * This requires the client to provide a functor (callback) to define - * the actual objects to be created within the ScopedCollection. These - * may as well be subclasses of the base type I, provided the general - * element storage size #siz was chosen sufficiently large to hold - * those subclass instances. - * - * This test demonstrates the most elaborate usage pattern, where - * the client provides a full blown functor object, which even - * has embedded state. But, generally speaking, anything - * exposing a suitable function call operator is acceptable. - */ - void - building_RAII_Style() - { - CHECK (0 == Dummy::checksum()); - { - int rr = rand() % 100; - int trigger = 101; - - CollD coll (6, Populator(rr, trigger)); - - CHECK (!isnil (coll)); - CHECK (6 == coll.size()); - CHECK (0 != Dummy::checksum()); - - CHECK (coll[0].acc(0) == 0 + rr); - CHECK (coll[1].acc(0) == 1 + rr + trigger); - CHECK (coll[2].acc(0) == 2 + rr); - CHECK (coll[3].acc(0) == 3 + rr + trigger); - CHECK (coll[4].acc(0) == 4 + rr); - CHECK (coll[5].acc(0) == 5 + rr + trigger); - - coll.clear(); - CHECK (0 == Dummy::checksum()); - - // Verify Error handling while in creation: - // SubDummy explodes on equal ctor parameters - // which here happens for i==7 - VERIFY_ERROR (SUBVERSIVE, CollD(10, Populator(0, 7)) ); - - // any already created object was properly destroyed - CHECK (0 == Dummy::checksum()); - - } - CHECK (0 == Dummy::checksum()); - } /** @test using the ScopedCollection to hold a variable @@ -335,9 +265,161 @@ namespace test{ } CHECK (0 == Dummy::checksum()); } + + + /** @test using the ScopedCollection according to the RAII pattern. + * For this usage style, the collection is filled right away, during + * construction. If anything goes wrong, the whole collection is + * cleared and invalidated. Consequently there is no tangible "lifecycle" + * at the usage site. Either the collection is fully usable, or not at all. + * This requires the client to provide a functor (callback) to define + * the actual objects to be created within the ScopedCollection. These + * may as well be subclasses of the base type I, provided the general + * element storage size #siz was chosen sufficiently large to hold + * those subclass instances. + * + * This test demonstrates the most elaborate usage pattern, where + * the client provides a full blown functor object #Populator, + * which even has embedded state. Generally speaking, anything + * exposing a suitable function call operator is acceptable. + */ + void + building_RAII_Style() + { + CHECK (0 == Dummy::checksum()); + { + int rr = rand() % 100; + int trigger = 101; + + CollD coll (6, Populator(rr, trigger)); + + CHECK (!isnil (coll)); + CHECK (6 == coll.size()); + CHECK (0 != Dummy::checksum()); + + CHECK (coll[0].acc(0) == 0 + rr); + CHECK (coll[1].acc(0) == 1 + rr + trigger); + CHECK (coll[2].acc(0) == 2 + rr); + CHECK (coll[3].acc(0) == 3 + rr + trigger); + CHECK (coll[4].acc(0) == 4 + rr); + CHECK (coll[5].acc(0) == 5 + rr + trigger); + + coll.clear(); + CHECK (0 == Dummy::checksum()); + + // Verify Error handling while in creation: + // SubDummy explodes on equal ctor parameters + // which here happens for i==7 + VERIFY_ERROR (SUBVERSIVE, CollD(10, Populator(0, 7)) ); + + // any already created object was properly destroyed + CHECK (0 == Dummy::checksum()); + + } + CHECK (0 == Dummy::checksum()); + } + + /** Functor to populate the Collection */ + class Populator + { + uint i_; + int off_; + int trigg_; + + public: + Populator (int baseOffset, int triggerCode) + : i_(0) + , off_(baseOffset) + , trigg_(triggerCode) + { } + + void + operator() (CollD::ElementHolder& storage) + { + switch (i_ % 2) + { + case 0: + storage.create<Dummy> (i_+off_); + break; + + case 1: + storage.create<SubDummy> (i_+off_, trigg_); + break; + } + ++i_; + } + }; + + + + /** @test for using ScopedCollection in RAII style, + * several pre-defined "populators" are provided. + * The most obvious one being just to fill the + * collection with default constructed objects. + */ + void + verify_defaultPopulator() + { + CHECK (0 == Dummy::checksum()); + + CollD coll (25, CollD::FillAll() ); + + CHECK (!isnil (coll)); + CHECK (25 == coll.size()); + CHECK (0 != Dummy::checksum()); + + for (CollD::iterator ii = coll.begin(); ii; ++ii) + { + CHECK ( INSTANCEOF (Dummy, & (*ii))); + CHECK (!INSTANCEOF (SubDummy, & (*ii))); + } + } + + + void + verify_subclassPopulator() + { + CHECK (0 == Dummy::checksum()); + + CollD coll (25, CollD::FillWith<SubDummy>() ); + + CHECK (!isnil (coll)); + CHECK (25 == coll.size()); + CHECK (0 != Dummy::checksum()); + + for (CollD::iterator ii = coll.begin(); ii; ++ii) + CHECK (INSTANCEOF (SubDummy, & (*ii))); + } + + + void + verify_iteratorPopulator() + { + typedef ScopedCollection<uint> CollI; + + CollI source (25); + for (uint i=0; i < source.capacity(); ++i) + source.appendNew<uint>(i); // holding the numbers 0..24 + + CollI coll (20, CollI::pull(source.begin())); + // this immediately pulls in the first 20 elements + CHECK (!isnil (coll)); + CHECK (20 == coll.size()); + CHECK (25 == source.size()); + + for (uint i=0; i < coll.size(); ++i) + { + CHECK (coll[i] == i ); + CHECK (coll[i] == source[i]); + } + + // note: the iterator is assumed to deliver a sufficient amount of elements + VERIFY_ERROR (ITER_EXHAUST, CollI (50, CollI::pull (source.begin()))); + } }; + LAUNCHER (ScopedCollection_test, "unit common"); From e41065101a76d77c5f19a3ce07ac7b135bfec11e Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Fri, 6 Jan 2012 03:16:22 +0100 Subject: [PATCH 351/496] switch OutputSlot to use the ScopedCollection ..hehe, makes the code way more sane --- src/lib/scoped-collection.hpp | 2 +- src/proc/asset/meta/time-grid.cpp | 2 + src/proc/play/output-slot-connection.hpp | 44 +++++--- .../proc/play/diagnostic-output-slot.hpp | 101 ++++++++++++------ 4 files changed, 102 insertions(+), 47 deletions(-) diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp index f3a1e313f..b9174401b 100644 --- a/src/lib/scoped-collection.hpp +++ b/src/lib/scoped-collection.hpp @@ -404,7 +404,7 @@ namespace lib { /* === Element access and iteration === */ I& - operator[] (size_t index) + operator[] (size_t index) const { if (index < level_) return elements_[index].accessObj(); diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp index c46509787..89744eee0 100644 --- a/src/proc/asset/meta/time-grid.cpp +++ b/src/proc/asset/meta/time-grid.cpp @@ -75,6 +75,8 @@ namespace meta { * from the AssetManager, then attach a further * smart-ptr-to-Quantiser to that, which then can be * published via the \link advice.hpp "advice system"\endlink + * @note this allows to use a time grid just "by name", + * without explicit dependance to the Session / Assets */ inline PGrid publishWrapped (TimeGrid& newGrid) diff --git a/src/proc/play/output-slot-connection.hpp b/src/proc/play/output-slot-connection.hpp index 29e6bf8e4..71a830c7e 100644 --- a/src/proc/play/output-slot-connection.hpp +++ b/src/proc/play/output-slot-connection.hpp @@ -41,17 +41,19 @@ #include "lib/error.hpp" #include "proc/play/output-slot.hpp" +#include "lib/scoped-collection.hpp" +#include "lib/iter-adapter-stl.hpp" +#include "lib/iter-source.hpp" #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/iter-adapter-stl.hpp" //#include "lib/sync.hpp" #include <boost/noncopyable.hpp> #include <boost/scoped_ptr.hpp> //#include <string> +#include <tr1/functional> #include <vector> //#include <tr1/memory> @@ -65,7 +67,9 @@ namespace play { //using std::string; using lib::transform; using lib::iter_stl::eachElm; - + + using std::tr1::placeholders::_1; + using std::tr1::bind; using std::vector; //using std::tr1::shared_ptr; using boost::scoped_ptr; @@ -92,6 +96,7 @@ namespace play { * implementation; yet it may as well be called from a separate * service thread or some kind of callback. * @note the meaning of FrameID is implementation defined. + * @note typically the concrete connection is noncopyable */ class OutputSlot::Connection { @@ -121,6 +126,8 @@ namespace play { { public: virtual ~ConnectionState() { } + + virtual Connection& access (uint) const =0; }; @@ -143,11 +150,12 @@ namespace play { template<class CON> class ConnectionStateManager : public OutputSlot::ConnectionState - , public vector<CON> { - typedef vector<CON> Connections; + typedef lib::ScopedCollection<CON> Connections; typedef OutputSlot::OpenedSinks OpenedSinks; + Connections connections_; + /* == Allocation Interface == */ @@ -155,7 +163,7 @@ namespace play { getOpenedSinks() { REQUIRE (this->isActive()); - return transform (eachElm(*this), connectOutputSink); + return transform (eachElm(connections_), connectOutputSink); } Timings @@ -167,25 +175,29 @@ namespace play { bool isActive() const { - return 0 < Connections::size(); + return 0 < connections_.size(); + } + + CON& + access (uint chanNr) const + { + return connections_[chanNr]; } protected: /* == API for OutputSlot-Impl == */ + typedef typename Connections::ElementHolder& ConnectionStorage; + /** factory function to build the actual * connection handling objects per channel */ - virtual CON buildConnection() =0; - - void - init (uint numChannels) - { - for (uint i=0; i<numChannels; ++i) - push_back(buildConnection()); - } + virtual void buildConnection(ConnectionStorage) =0; - ConnectionStateManager() { } + ConnectionStateManager(uint numChannels) + : connections_( numChannels + , bind (&ConnectionStateManager::buildConnection, this, _1 )) + { } public: virtual diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index 9ab0f98a7..1554ce8cf 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -41,6 +41,8 @@ #include "lib/time/timevalue.hpp" #include "lib/scoped-ptrvect.hpp" #include "lib/iter-source.hpp" +#include "lib/advice.hpp" +#include "lib/symbol.hpp" #include "lib/util.hpp" #include "proc/engine/testframe.hpp" //#include "lib/sync.hpp" @@ -57,6 +59,8 @@ namespace proc { namespace play { //using std::string; + using lib::Symbol; + using util::unConst; using util::contains; using lib::time::FrameRate; using proc::asset::meta::PGrid; @@ -70,6 +74,24 @@ namespace play { using std::tr1::shared_ptr; //using boost::scoped_ptr; + namespace { // diagnostics & internals.... + + inline PGrid + getTestTimeGrid() + { + Symbol gridID("DiagnosticOutputSlot-buffer-grid"); + lib::advice::Request<PGrid> query4grid(gridID) ; + PGrid testGrid25 = query4grid.getAdvice(); + + if (!testGrid25) + testGrid25 = TimeGrid::build (gridID, FrameRate::PAL); + + ENSURE (testGrid25); + return testGrid25; + } + } + + /** * Diagnostic output connection for a single channel, * allowing to track generated frames and verify @@ -77,17 +99,20 @@ namespace play { */ class TrackingInMemoryBlockSequence : public OutputSlot::Connection + , boost::noncopyable { typedef std::tr1::unordered_set<FrameID> FrameTrackingInfo; - shared_ptr<TrackingHeapBlockProvider> buffProvider_; + TrackingHeapBlockProvider buffProvider_; BufferDescriptor bufferType_; FrameTrackingInfo frameTrackingIndex_; PGrid frameGrid_; + bool closed_; + BuffHandle trackFrame (FrameID frameNr, BuffHandle const& newBuffer) @@ -115,8 +140,9 @@ namespace play { BuffHandle claimBufferFor(FrameID frameNr) { + REQUIRE (!closed_); return trackFrame (frameNr, - buffProvider_->lockBuffer (bufferType_)); + buffProvider_.lockBuffer (bufferType_)); } @@ -132,34 +158,38 @@ namespace play { void transfer (BuffHandle const& filledBuffer) { + REQUIRE (!closed_); pushout (filledBuffer); } void pushout (BuffHandle const& data4output) { - buffProvider_->emitBuffer (data4output); - buffProvider_->releaseBuffer(data4output); + REQUIRE (!closed_); + buffProvider_.emitBuffer (data4output); + buffProvider_.releaseBuffer(data4output); } void discard (BuffHandle const& superseededData) { - buffProvider_->releaseBuffer (superseededData); + REQUIRE (!closed_); + buffProvider_.releaseBuffer (superseededData); } void shutDown () { - buffProvider_.reset(); + closed_ = true; } public: TrackingInMemoryBlockSequence() - : buffProvider_(new TrackingHeapBlockProvider()) - , bufferType_(buffProvider_->getDescriptor<TestFrame>()) + : buffProvider_() + , bufferType_(buffProvider_.getDescriptor<TestFrame>()) , frameTrackingIndex_() - , frameGrid_(TimeGrid::build ("DiagnosticOutputSlot-buffer-grid", FrameRate::PAL)) /////////////TODO should rather pass that in as part of a "timings" definition + , frameGrid_(getTestTimeGrid()) /////////////TODO should rather pass that in as part of a "timings" definition + , closed_(false) { INFO (engine_dbg, "building in-memory diagnostic output sequence"); } @@ -173,22 +203,20 @@ namespace play { /* === Diagnostic API === */ - TestFrame* + TestFrame const * accessEmittedFrame (uint frameNr) const { - REQUIRE (buffProvider_); - if (frameNr <= buffProvider_->emittedCnt()) - return &buffProvider_->accessAs<TestFrame> (frameNr); + if (frameNr <= buffProvider_.emittedCnt()) + return & accessFrame(frameNr); else return 0; ////////////////////////////////TICKET #856 } - diagn::Block* + diagn::Block const * accessEmittedBuffer (uint bufferNr) const { - REQUIRE (buffProvider_); - if (bufferNr <= buffProvider_->emittedCnt()) - return & buffProvider_->access_emitted (bufferNr); + if (bufferNr <= buffProvider_.emittedCnt()) + return & accessBlock(bufferNr); else return 0; } @@ -196,9 +224,21 @@ namespace play { bool wasAllocated (uint frameNr) const { - REQUIRE (buffProvider_); return contains (frameTrackingIndex_, frameNr); } + + private: + TestFrame const& + accessFrame (uint frameNr) const + { + return unConst(this)->buffProvider_.accessAs<TestFrame> (frameNr); + } + + diagn::Block const& + accessBlock (uint bufferNr) const + { + return unConst(this)->buffProvider_.access_emitted (bufferNr); + } }; @@ -210,17 +250,18 @@ namespace play { class SimulatedOutputSequences : public ConnectionStateManager<TrackingInMemoryBlockSequence> { - TrackingInMemoryBlockSequence - buildConnection() + typedef ConnectionStateManager<TrackingInMemoryBlockSequence> _Base; + + void + buildConnection(ConnectionStorage storage) { - return TrackingInMemoryBlockSequence(); + storage.create<TrackingInMemoryBlockSequence>(); } public: SimulatedOutputSequences (uint numChannels) - { - init (numChannels); - } + : _Base(numChannels) + { } }; @@ -276,7 +317,7 @@ namespace play { { REQUIRE (!isFree(), "diagnostic OutputSlot not (yet) connected"); REQUIRE (channel <= getOutputChannelCount()); - return static_cast<SimulatedOutputSequences&> (*state_).at(channel); + return static_cast<TrackingInMemoryBlockSequence&> (state_->access (channel)); } @@ -307,7 +348,7 @@ namespace play { * the emitted Data as a sequence of TestFrame objects. */ class OutputFramesLog - : public lib::IterSource<TestFrame> + : public lib::IterSource<const TestFrame> , boost::noncopyable { TrackingInMemoryBlockSequence const& outSeq_; @@ -366,8 +407,8 @@ namespace play { bool buffer_was_closed (uint channel, FrameID frame) { - diagn::Block* block = accessSequence(channel) - .accessEmittedBuffer(frame); + diagn::Block const *block = accessSequence(channel) + .accessEmittedBuffer(frame); return block && block->was_closed(); } @@ -376,8 +417,8 @@ namespace play { bool emitted (uint channel, FrameID frame) { - diagn::Block* block = accessSequence(channel) - .accessEmittedBuffer(frame); + diagn::Block const *block = accessSequence(channel) + .accessEmittedBuffer(frame); return block && block->was_used(); } From b64dff1ad89edc5ea4edf66af87c258ba33cabc7 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 7 Jan 2012 00:02:03 +0100 Subject: [PATCH 352/496] spelling and comments --- src/lib/meta/function-closure.hpp | 4 ++-- tests/lib/meta/function-closure-test.cpp | 2 +- tests/lib/meta/function-composition-test.cpp | 7 ++----- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/lib/meta/function-closure.hpp b/src/lib/meta/function-closure.hpp index 207c139bd..4275a5cb6 100644 --- a/src/lib/meta/function-closure.hpp +++ b/src/lib/meta/function-closure.hpp @@ -796,8 +796,8 @@ namespace func{ /* ========== function-style interface ============= */ /** build a TupleApplicator, which embodies the given - * argument tuple and can be used to apply various - * functions to them. + * argument tuple and can be used to apply them + * to various functions repeatedly. */ template<typename ARG> typename _Sig<void, ARG>::Applicator diff --git a/tests/lib/meta/function-closure-test.cpp b/tests/lib/meta/function-closure-test.cpp index bc48c07bf..a3809bdb8 100644 --- a/tests/lib/meta/function-closure-test.cpp +++ b/tests/lib/meta/function-closure-test.cpp @@ -24,7 +24,7 @@ /** @file function-closure-test.cpp ** Testing a combination of tr1::function objects and metaprogramming. ** Argument types will be extracted and represented as typelist, so they - ** can be manipulated at compile time. This test uses some functions with + ** can be manipulated at compile time. This test uses some test functions ** and systematically applies or binds them to corresponding data tuples. ** Moreover, closure objects will be constructed in various flavours, ** combining a function object and a set of parameters. diff --git a/tests/lib/meta/function-composition-test.cpp b/tests/lib/meta/function-composition-test.cpp index 38bf82a16..45a50a047 100644 --- a/tests/lib/meta/function-composition-test.cpp +++ b/tests/lib/meta/function-composition-test.cpp @@ -127,7 +127,7 @@ namespace test { /****************************************************************************** * @test this test covers some extensions and variations on function closures: * - partial application of a function, returning a partial closure - * - variation: binding a arbitrary term, might even be a nested binder + * - variation: binding an arbitrary term, might even be a nested binder * - chaining of two functions with suitable arguments ("composition") */ class FunctionComposition_test : public Test @@ -142,10 +142,7 @@ namespace test { } - /** verify the test input data - * @see TypeListManipl_test#check_diagnostics() - * for an explanation of the DISPLAY macro - */ + /** verify the test input data */ void check_diagnostics () { From c134026502394302e5a6e44257df64ee533631b8 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 7 Jan 2012 02:53:07 +0100 Subject: [PATCH 353/496] for experimenting with -O3 --- research/SConscript | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/research/SConscript b/research/SConscript index 0fef0dae8..b82f1981f 100644 --- a/research/SConscript +++ b/research/SConscript @@ -9,9 +9,11 @@ Import('env','artifacts','core') support_lib = artifacts['support'] +envR = env.Clone() +# envR.Append(CCFLAGS=' -O3 ') # build additional test and administrative tools.... -experiments = [ env.Program('try', ['try.cpp'] + support_lib) #### to try out some feature... +experiments = [ envR.Program('try', ['try.cpp'] + support_lib) #### to try out some feature... ] # From 00c08bd27b2a45e9a975b64395171011066809b5 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 7 Jan 2012 03:11:02 +0100 Subject: [PATCH 354/496] fix helper functions not declared as inline --- src/lib/itertools.hpp | 2 +- src/lib/meta/function-closure.hpp | 7 +++++++ src/lib/meta/tuple.hpp | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/itertools.hpp b/src/lib/itertools.hpp index 22a9e6c32..12b2286cb 100644 --- a/src/lib/itertools.hpp +++ b/src/lib/itertools.hpp @@ -543,7 +543,7 @@ namespace lib { /** filters away repeated values * emitted by source iterator */ template<class IT> - FilterIter<IT> + inline FilterIter<IT> filterRepetitions (IT const& source) { typedef typename IT::value_type Val; diff --git a/src/lib/meta/function-closure.hpp b/src/lib/meta/function-closure.hpp index 4275a5cb6..572eb32fa 100644 --- a/src/lib/meta/function-closure.hpp +++ b/src/lib/meta/function-closure.hpp @@ -800,6 +800,7 @@ namespace func{ * to various functions repeatedly. */ template<typename ARG> + inline typename _Sig<void, ARG>::Applicator tupleApplicator (Tuple<ARG>& args) { @@ -810,6 +811,7 @@ namespace func{ /** apply the given function to the argument tuple */ template<typename SIG, typename ARG> + inline typename _Fun<SIG>::Ret apply (SIG& f, Tuple<ARG>& args) { @@ -824,6 +826,7 @@ namespace func{ * invoked later to yield the * function result. */ template<typename SIG, typename ARG> + inline typename _Clo<SIG,ARG>::Type closure (SIG& f, Tuple<ARG>& args) { @@ -836,6 +839,7 @@ namespace func{ /** close the given function over the first argument */ template<typename SIG, typename ARG> + inline typename _PapS<SIG>::Function applyFirst (SIG& f, ARG arg) { @@ -848,6 +852,7 @@ namespace func{ /** close the given function over the last argument */ template<typename SIG, typename ARG> + inline typename _PapE<SIG>::Function applyLast (SIG& f, ARG arg) { @@ -862,6 +867,7 @@ namespace func{ /** bind the last function argument to an arbitrary term, * which especially might be a (nested) binder... */ template<typename SIG, typename TERM> + inline typename _PapE<SIG>::Function bindLast (SIG& f, TERM const& arg) { @@ -875,6 +881,7 @@ namespace func{ /** build a functor chaining the given functions */ template<typename SIG1, typename SIG2> + inline typename _Chain<SIG1,SIG2>::Function chained (SIG1& f1, SIG2& f2) { diff --git a/src/lib/meta/tuple.hpp b/src/lib/meta/tuple.hpp index 03c68384f..73e633f1b 100644 --- a/src/lib/meta/tuple.hpp +++ b/src/lib/meta/tuple.hpp @@ -283,6 +283,7 @@ namespace meta { namespace tuple { // some convenience access functions template<uint n, class TUP> + inline typename Shifted<TUP,n>::Head& element (TUP& tup) { From 90e6dae2d58c1961cf43c1fc5e66836799de0121 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 7 Jan 2012 03:11:51 +0100 Subject: [PATCH 355/496] add includes missing in some metaprogramming headers types uint and string --- src/lib/meta/typelist-manip.hpp | 2 ++ src/lib/meta/util.hpp | 17 ++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/lib/meta/typelist-manip.hpp b/src/lib/meta/typelist-manip.hpp index 22144e86f..536630e13 100644 --- a/src/lib/meta/typelist-manip.hpp +++ b/src/lib/meta/typelist-manip.hpp @@ -56,6 +56,8 @@ #include "lib/meta/typelist.hpp" +#include <sys/types.h> + namespace lib { namespace meta { diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index 67dacc400..63ae2aa47 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -37,18 +37,21 @@ #ifndef LIB_META_UTIL_H #define LIB_META_UTIL_H - + +#include <string> + + namespace lib { namespace meta { - - + + /* types for figuring out the overload resolution chosen by the compiler */ - + typedef char Yes_t; struct No_t { char more_than_one[4]; }; - - - + + + /** Compile-time Type equality: * Simple Trait template to pick up types considered From feff7537efa907a31fd7d9e6a2dc538c898bba8b Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 7 Jan 2012 03:27:31 +0100 Subject: [PATCH 356/496] reduce memory requirements for this test ...it caused out of memory frequently. --- src/lib/diagnostic-context.hpp | 4 ++++ tests/40components.tests | 2 +- tests/lib/diagnostic-context-test.cpp | 12 ++++++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/lib/diagnostic-context.hpp b/src/lib/diagnostic-context.hpp index 221f59fad..5d3167fee 100644 --- a/src/lib/diagnostic-context.hpp +++ b/src/lib/diagnostic-context.hpp @@ -124,6 +124,10 @@ namespace lib { * on the thread-local diagnostic stack. Might * be empty. Values start with frame next to * the current scope and end with outermost. + * @warning can be inefficient on very large stacks + * @todo benchmark and improve the data structure + * used for the snapshot. Vector is not + * an optimal choice here. */ static ValSequence extractStack() diff --git a/tests/40components.tests b/tests/40components.tests index b1a93d5d1..4f60cf263 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -679,7 +679,7 @@ return: 0 END -TEST "Thread-local diagnostic context" DiagrnsticContext_test <<END +TEST "Thread-local diagnostic context" DiagnosticContext_test <<END return: 0 END diff --git a/tests/lib/diagnostic-context-test.cpp b/tests/lib/diagnostic-context-test.cpp index 008c37af5..ba1183510 100644 --- a/tests/lib/diagnostic-context-test.cpp +++ b/tests/lib/diagnostic-context-test.cpp @@ -1,5 +1,5 @@ /* - DiagrnsticContext(Test) - verify thread local stack for collecting diagnostics + DiagnosticContext(Test) - verify thread local stack for collecting diagnostics Copyright (C) Lumiera.org 2011, Hermann Vosseler <Ichthyostega@web.de> @@ -39,8 +39,9 @@ namespace test{ namespace { // private test setup... + /* WARNING: memory hungry */ const uint NUM_THREADS = 40; - const uint MAX_RAND = 1000*1000; + const uint MAX_RAND = 100*1000; inline bool isOdd (uint val) @@ -80,7 +81,7 @@ namespace test{ * @see lib::ThreadLocal * @see backend::Thread */ - class DiagrnsticContext_test : public Test + class DiagnosticContext_test : public Test { virtual void @@ -147,6 +148,9 @@ namespace test{ * take a snapshot of the full ContextStack and then unwind. * Thus the captured numbers must from a decreasing sequence * of odd values. + * @warning this test case causes memory pressure. + * The reason seems to be the frequent re-allocations + * of the vector used to take the snapshots */ void verify_heavilyParallelUsage() @@ -206,7 +210,7 @@ namespace test{ /** Register this test class... */ - LAUNCHER (DiagrnsticContext_test, "function common"); + LAUNCHER (DiagnosticContext_test, "function common"); From 9bba3667633b10a349792b12568b47ac1f8e0578 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 7 Jan 2012 03:28:12 +0100 Subject: [PATCH 357/496] investigate partial application of member functions --- research/try.cpp | 178 ++++++++++++++++++------------ src/lib/meta/function-closure.hpp | 117 ++++++++++++++++++++ 2 files changed, 227 insertions(+), 68 deletions(-) diff --git a/research/try.cpp b/research/try.cpp index 763f90caa..d03b9d2da 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -22,100 +22,142 @@ // 10/11 - simple demo using a pointer and a struct // 11/11 - using the boost random number generator(s) // 12/11 - how to detect if string conversion is possible? +// 1/12 - is partial application of member functions possible? -#include "lib/util.hpp" +/** @file try.cpp + ** Research: perform a partial application of a member function. + ** The result of this partial application should be a functor expecting the remaining arguments. + ** The idea was to use this at various library functions expecting a functor or callback, so to + ** improve readability of the client code: clients could then just pass a member pointer, without + ** the need to use any tr1::bind expression. + ** + ** \par Costs in code size + ** While this turned out to be possible, even without much work, just based on the existing + ** templates for partial functor application (function-closure.hpp), the resulting code size + ** is rather sobering. Especially in debug mode, quite some overhead is created, which makes + ** usage of this convenience feature in general purpose library code rather questionable. + ** When compiling with -O3 though, most of the overhead will be removed + ** + ** The following numbers could be observed: + ** \code + ** debug / stripped // debug-O3 / stripped + ** just using a member pointer: 39013 / 7048 42061 / 7056 + ** using tr1::bind and function: 90375 / 15416 65415 / 9376 + ** partial apply, passing functor: 158727 / 23576 97479 / 11296 + ** partial apply with mem pointer: 119495 / 17816 78031 / 9440 + ** \endcode + */ + +#include "lib/meta/tuple.hpp" +#include "lib/meta/function-closure.hpp" + +//#include <tr1/functional> #include <iostream> -#include <string> + +using lib::meta::Types; +using lib::meta::Tuple; +//using std::tr1::placeholders::_1; +//using std::tr1::placeholders::_2; +using std::tr1::function; +using std::tr1::bind; using std::string; using std::cout; using std::endl; -typedef char Yes_t; -struct No_t { char more_than_one[4]; }; +namespace lib { +namespace meta{ +namespace func{ -template<typename TY> -struct _can_convertToString +template<typename SIG, uint num> +struct _PupS { - static TY & probe(); + typedef typename _Fun<SIG>::Ret Ret; + typedef typename _Fun<SIG>::Args::List Args; + typedef typename Splice<Args,NullType,num>::Front ArgsFront; + typedef typename Splice<Args,NullType,num>::Back ArgsBack; + typedef typename Types<ArgsFront>::Seq ArgsToClose; + typedef typename Types<ArgsBack>::Seq ArgsRemaining; + typedef typename _Sig<Ret,ArgsRemaining>::Type ReducedSignature; - static Yes_t check(string); - static No_t check(...); + typedef function<ReducedSignature> Function; + }; + +template<typename SIG, typename A1> +inline +typename _PupS<SIG,1>::Function +papply (SIG f, A1 a1) +{ + typedef typename _PupS<SIG,1>::ArgsToClose ArgsToClose; + typedef Tuple<ArgsToClose> ArgTuple; + ArgTuple val(a1); + return PApply<SIG,ArgsToClose>::bindFront (f, val); +} + +template<typename SIG, typename A1, typename A2> +inline +typename _PupS<SIG,2>::Function +papply (SIG f, A1 a1, A2 a2) +{ + typedef typename _PupS<SIG,2>::ArgPrefix ArgsToClose; + typedef Tuple<ArgsToClose> ArgTuple; + ArgTuple val(a1,a2); + return PApply<SIG,ArgsToClose>::bindFront (f, val); +} + + +}}} // namespace lib::meta::func + +class Something + { + int i_; + + void + privateFun(char a) + { + char aa(a + i_); + cout << "Char-->" << aa <<endl; + } public: - static const bool value = (sizeof(Yes_t)==sizeof(check(probe()))); - }; - - -class SubString - : public string - { - public: - SubString() : string("sublunar") { } - }; - -class Something { }; - -struct SomehowStringy - { - operator string() { return "No such thing"; } - }; - -struct SomehowSub - { - operator SubString() { return SubString(); } - }; - -class SomehowSubSub - : public SomehowSub - { + Something(int ii=0) + : i_(ii) + { } + + typedef function<void(char)> FunP; + + FunP + getBinding() + { +// function<void(Something*,char)> memf = bind (&Something::privateFun, _1, _2); +// return lib::meta::func::papply (memf, this); + return lib::meta::func::papply (&Something::privateFun, this); + } + +// typedef void (Something::*FunP) (char); +// +// FunP +// getBinding() +// { +// return &Something::privateFun; +// } }; -template<typename TY> -bool -investigate (TY const&) - { - return _can_convertToString<TY>::value; - } - -#define SHOW_CHECK(_EXPR_) cout << STRINGIFY(_EXPR_) << "\t : " << (investigate(_EXPR_)? "Yes":"No") << endl; int main (int, char**) { + Something some(23); + Something::FunP fup = some.getBinding(); - SHOW_CHECK (string("nebbich")); - SHOW_CHECK ("gurks"); - SHOW_CHECK (23.34); - SHOW_CHECK (23); - - string urgs("urgs"); - string & urgs_ref (urgs); - string const& urgs_const_ref (urgs); - string * urgs_ptr = &urgs; - - SHOW_CHECK (urgs_ref); - SHOW_CHECK (urgs_const_ref); - SHOW_CHECK (*urgs_ptr); - - SubString sub; - Something thing; - const SomehowStringy stringy = SomehowStringy(); - SomehowSubSub subsub; - SubString const& subRef(subsub); - - SHOW_CHECK (sub); - SHOW_CHECK (thing); - SHOW_CHECK (stringy); - SHOW_CHECK (subsub); - SHOW_CHECK (subRef); + fup ('a'); cout << "\n.gulp.\n"; diff --git a/src/lib/meta/function-closure.hpp b/src/lib/meta/function-closure.hpp index 572eb32fa..4b75a2f54 100644 --- a/src/lib/meta/function-closure.hpp +++ b/src/lib/meta/function-closure.hpp @@ -67,18 +67,26 @@ namespace func{ using tuple::element; + /** + * Helper to dissect an arbitrary function signature, + * irrespective if the parameter is given as function reference, + * function pointer, member function pointer or functor object. + * The base case assumes a (language) function reference. + */ template<typename SIG> struct _Fun { typedef typename FunctionSignature<function<SIG> >::Ret Ret; typedef typename FunctionSignature<function<SIG> >::Args Args; }; + /** Specialisation for using a function pointer */ template<typename SIG> struct _Fun<SIG*> { typedef typename FunctionSignature<function<SIG> >::Ret Ret; typedef typename FunctionSignature<function<SIG> >::Args Args; }; + /** Specialisation for passing a functor */ template<typename SIG> struct _Fun<function<SIG> > { @@ -86,6 +94,115 @@ namespace func{ typedef typename FunctionSignature<function<SIG> >::Args Args; }; + /** Specialisations for member function pointers */ + template<typename RET, class CLASS> + struct _Fun<RET (CLASS::*) (void) > + { + typedef RET Ret; + typedef Types<CLASS* const> Args; + }; + + template< typename RET, class CLASS + , typename A1 + > + struct _Fun<RET (CLASS::*) (A1) > + { + typedef RET Ret; + typedef Types<CLASS* const, A1> Args; + }; + + template< typename RET, class CLASS + , typename A1 + , typename A2 + > + struct _Fun<RET (CLASS::*) (A1,A2) > + { + typedef RET Ret; + typedef Types<CLASS* const, A1,A2> Args; + }; + + template< typename RET, class CLASS + , typename A1 + , typename A2 + , typename A3 + > + struct _Fun<RET (CLASS::*) (A1,A2,A3) > + { + typedef RET Ret; + typedef Types<CLASS* const, A1,A2,A3> Args; + }; + + template< typename RET, class CLASS + , typename A1 + , typename A2 + , typename A3 + , typename A4 + > + struct _Fun<RET (CLASS::*) (A1,A2,A3,A4) > + { + typedef RET Ret; + typedef Types<CLASS* const, A1,A2,A3,A4> Args; + }; + + template< typename RET, class CLASS + , typename A1 + , typename A2 + , typename A3 + , typename A4 + , typename A5 + > + struct _Fun<RET (CLASS::*) (A1,A2,A3,A4,A5) > + { + typedef RET Ret; + typedef Types<CLASS* const, A1,A2,A3,A4,A5> Args; + }; + + template< typename RET, class CLASS + , typename A1 + , typename A2 + , typename A3 + , typename A4 + , typename A5 + , typename A6 + > + struct _Fun<RET (CLASS::*) (A1,A2,A3,A4,A5,A6) > + { + typedef RET Ret; + typedef Types<CLASS* const, A1,A2,A3,A4,A5,A6> Args; + }; + + template< typename RET, class CLASS + , typename A1 + , typename A2 + , typename A3 + , typename A4 + , typename A5 + , typename A6 + , typename A7 + > + struct _Fun<RET (CLASS::*) (A1,A2,A3,A4,A5,A6,A7) > + { + typedef RET Ret; + typedef Types<CLASS* const, A1,A2,A3,A4,A5,A6,A7> Args; + }; + + template< typename RET, class CLASS + , typename A1 + , typename A2 + , typename A3 + , typename A4 + , typename A5 + , typename A6 + , typename A7 + , typename A8 + > + struct _Fun<RET (CLASS::*) (A1,A2,A3,A4,A5,A6,A7,A8) > + { + typedef RET Ret; + typedef Types<CLASS* const, A1,A2,A3,A4,A5,A6,A7,A8> Args; + }; + + template<typename FUN> struct is_Functor { static const bool value = false; }; template<typename SIG> From e6888f7b832857702d88e761c39216ece3a3a7bc Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 7 Jan 2012 04:11:39 +0100 Subject: [PATCH 358/496] supplement direct support for populating by member function this turns out to be the typical usage scenario for ScopedCollection: a manager object owning the collection will populate it with specially crafted components and invoke a member function for creating the individual components. This shortcut avoids using tr1::bind alltogether --- src/lib/scoped-collection.hpp | 27 +++++++++++++++ tests/lib/scoped-collection-test.cpp | 49 ++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp index b9174401b..2c9f85d7a 100644 --- a/src/lib/scoped-collection.hpp +++ b/src/lib/scoped-collection.hpp @@ -257,6 +257,33 @@ namespace lib { throw; } } + /** variation of RAII-style: using a builder function, + * which is a member of some object. This supports the + * typical usage situation, where a manager object builds + * a ScopedCollection of some components + * @param builder member function used to create the elements + * @param instance the owning class instance, on which the + * builder member function will be invoked ("this"). + */ + template<class TY> + ScopedCollection (size_t maxElements, void (TY::*builder) (ElementHolder&), TY * const instance) + : level_(0) + , capacity_(maxElements) + , elements_(new ElementHolder[maxElements]) + { + try { + while (level_ < capacity_) + { + ElementHolder& storageFrame (elements_[level_]); + (instance->*builder) (storageFrame); + ++level_; + }} + catch(...) + { + clear(); + throw; + } } + /* == some pre-defined Builders == */ class FillAll; ///< fills the ScopedCollection with default constructed I-instances diff --git a/tests/lib/scoped-collection-test.cpp b/tests/lib/scoped-collection-test.cpp index 76c76c12e..f112e79da 100644 --- a/tests/lib/scoped-collection-test.cpp +++ b/tests/lib/scoped-collection-test.cpp @@ -111,6 +111,7 @@ namespace test{ iterating(); verify_defaultPopulator(); verify_iteratorPopulator(); + verify_embeddedCollection(); } @@ -416,6 +417,54 @@ namespace test{ // note: the iterator is assumed to deliver a sufficient amount of elements VERIFY_ERROR (ITER_EXHAUST, CollI (50, CollI::pull (source.begin()))); } + + + /** @test simulate the typical situation of a manager + * owning some embedded components. Here, our ManagerDemo + * instance owns a collection of numbers 50..1. They are + * created right while initialising the manager, and this + * initialisation is done by invoking a member function + * of the manager + */ + void + verify_embeddedCollection() + { + ManagerDemo object_with_embedded_Collection(50); + CHECK (sum(50) == object_with_embedded_Collection.useMyNumbers()); + } + + class ManagerDemo + { + typedef ScopedCollection<uint> CollI; + + uint memberVar_; + const CollI my_own_Numbers_; + + void + buildNumbers (CollI::ElementHolder& storage) + { + storage.create<uint>(memberVar_); + --memberVar_; + } + + public: + ManagerDemo(uint cnt) + : memberVar_(cnt) + , my_own_Numbers_(cnt, &ManagerDemo::buildNumbers, this) + { + CHECK (0 == memberVar_); + CHECK (cnt == my_own_Numbers_.size()); + } + + uint + useMyNumbers() + { + uint sum(0); + for (CollI::const_iterator ii = my_own_Numbers_.begin(); ii; ++ii) + sum += *ii; + return sum; + } + }; }; From 5cc034d26b0a3a66399b3cfef96c4ce949c0f9eb Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 7 Jan 2012 04:44:48 +0100 Subject: [PATCH 359/496] refactor ScopedCollection to use an init() function --- src/lib/scoped-collection.hpp | 85 ++++++++++++------- src/proc/play/output-slot-connection.hpp | 12 ++- .../proc/play/diagnostic-output-slot.hpp | 4 +- 3 files changed, 65 insertions(+), 36 deletions(-) diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp index 2c9f85d7a..0ff2d37ef 100644 --- a/src/lib/scoped-collection.hpp +++ b/src/lib/scoped-collection.hpp @@ -47,7 +47,7 @@ ** the container is being filled successively. ** - the "RAII style" usage strives to create all of the content objects ** right away, immediately after the memory allocation. This usage pattern - ** avoids any kind of "lifecylce state". Either the container comes up sane + ** avoids any kind of "lifecycle state". Either the container comes up sane ** and fully populated, or the ctor call fails and any already created ** objects are discarded. ** @note intentionally there is no operation to discard individual objects, @@ -69,13 +69,9 @@ #define LIB_SCOPED_COLLECTION_H -//#include "include/logging.h" -#include "lib/iter-adapter.hpp" #include "lib/error.hpp" -//#include "lib/util.hpp" +#include "lib/iter-adapter.hpp" -//#include <vector> -//#include <algorithm> #include <boost/noncopyable.hpp> #include <boost/scoped_array.hpp> #include <boost/static_assert.hpp> @@ -139,7 +135,6 @@ namespace lib { #define TYPE_SANITY_CHECK \ BOOST_STATIC_ASSERT ((boost::is_base_of<I,TY>::value || boost::is_same<I,TY>::value)) - /** Abbreviation for placement new */ #define EMBEDDED_ELEMENT_CTOR(_CTOR_CALL_) \ TYPE_SANITY_CHECK; \ @@ -244,18 +239,8 @@ namespace lib { , capacity_(maxElements) , elements_(new ElementHolder[maxElements]) { - try { - while (level_ < capacity_) - { - ElementHolder& storageFrame (elements_[level_]); - builder (storageFrame); - ++level_; - }} - catch(...) - { - clear(); - throw; - } } + populate_by (builder); + } /** variation of RAII-style: using a builder function, * which is a member of some object. This supports the @@ -271,18 +256,8 @@ namespace lib { , capacity_(maxElements) , elements_(new ElementHolder[maxElements]) { - try { - while (level_ < capacity_) - { - ElementHolder& storageFrame (elements_[level_]); - (instance->*builder) (storageFrame); - ++level_; - }} - catch(...) - { - clear(); - throw; - } } + populate_by (builder,instance); + } /* == some pre-defined Builders == */ @@ -316,6 +291,7 @@ namespace lib { } } + /** init all elements default constructed */ void populate() try { @@ -333,6 +309,53 @@ namespace lib { throw; } + /** init all elements at once, + * invoking a builder functor for each. + * @param builder to create the individual elements + * this functor is responsible to invoke the appropriate + * ElementHolder#create function, which places a new element + * into the storage frame passed as parameter. + */ + template<class CTOR> + void + populate_by (CTOR builder) + try { + while (level_ < capacity_) + { + ElementHolder& storageFrame (elements_[level_]); + builder (storageFrame); + ++level_; + } } + catch(...) + { + WARN (progress, "Failure while populating ScopedCollection. " + "All elements will be discarded"); + clear(); + throw; + } + + /** variation of element initialisation, + * invoking a member function of some manager object + * for each collection element to be created. + */ + template<class TY> + void + populate_by (void (TY::*builder) (ElementHolder&), TY * const instance) + try { + while (level_ < capacity_) + { + ElementHolder& storageFrame (elements_[level_]); + (instance->*builder) (storageFrame); + ++level_; + } } + catch(...) + { + WARN (progress, "Failure while populating ScopedCollection. " + "All elements will be discarded"); + clear(); + throw; + } + /** push a new element of default type diff --git a/src/proc/play/output-slot-connection.hpp b/src/proc/play/output-slot-connection.hpp index 71a830c7e..332943ae5 100644 --- a/src/proc/play/output-slot-connection.hpp +++ b/src/proc/play/output-slot-connection.hpp @@ -68,8 +68,8 @@ namespace play { using lib::transform; using lib::iter_stl::eachElm; - using std::tr1::placeholders::_1; - using std::tr1::bind; +//using std::tr1::placeholders::_1; +//using std::tr1::bind; using std::vector; //using std::tr1::shared_ptr; using boost::scoped_ptr; @@ -186,6 +186,11 @@ namespace play { protected: /* == API for OutputSlot-Impl == */ + void + init() ///< derived classes need to invoke this to build the actual connections + { + connections_.populate_by (&ConnectionStateManager::buildConnection, this); + } typedef typename Connections::ElementHolder& ConnectionStorage; @@ -195,8 +200,7 @@ namespace play { ConnectionStateManager(uint numChannels) - : connections_( numChannels - , bind (&ConnectionStateManager::buildConnection, this, _1 )) + : connections_(numChannels) { } public: diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index 1554ce8cf..920630856 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -261,7 +261,9 @@ namespace play { public: SimulatedOutputSequences (uint numChannels) : _Base(numChannels) - { } + { + init(); + } }; From 73cfef69c813be499484757628e36fd478ea30c2 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 7 Jan 2012 06:40:21 +0100 Subject: [PATCH 360/496] fix some problems with OutputSlotProtocol_test still WIP... - there is a logical contradiction with the frame numbers - somehow, in diagnostics, we access the wrong sequence instance --- src/proc/asset/meta/time-grid.cpp | 1 + tests/components/proc/play/diagnostic-output-slot.hpp | 2 +- tests/components/proc/play/output-slot-protocol-test.cpp | 3 --- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp index 89744eee0..d72878f03 100644 --- a/src/proc/asset/meta/time-grid.cpp +++ b/src/proc/asset/meta/time-grid.cpp @@ -85,6 +85,7 @@ namespace meta { PQuant quantiser (dynamic_pointer_cast<const Quantiser>(gridImplementation)); Literal bindingID (cStr(newGrid.ident.name)); + advice::Provision<PGrid>(bindingID).setAdvice(gridImplementation); advice::Provision<PQuant>(bindingID).setAdvice(quantiser); return gridImplementation; } diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index 920630856..6ec40d009 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -117,7 +117,7 @@ namespace play { BuffHandle trackFrame (FrameID frameNr, BuffHandle const& newBuffer) { - REQUIRE (contains (frameTrackingIndex_,frameNr), + REQUIRE (!contains (frameTrackingIndex_,frameNr), "attempt to lock already used frame %lu", frameNr); frameTrackingIndex_.insert (frameNr); diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp index 480b664c8..a1e0fef09 100644 --- a/tests/components/proc/play/output-slot-protocol-test.cpp +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -92,15 +92,12 @@ namespace test { int64_t frameNr = 123; BuffHandle buff00 = sink1.lockBufferFor (frameNr); BuffHandle buff10 = sink2.lockBufferFor (frameNr); - buff00.create<TestFrame>(); - buff10.create<TestFrame>(); // rendering process calculates content.... buff00.accessAs<TestFrame>() = testData(0,0); // while further frames might be processed in parallel BuffHandle buff11 = sink2.lockBufferFor (++frameNr); - buff11.create<TestFrame>(); buff11.accessAs<TestFrame>() = testData(1,1); buff10.accessAs<TestFrame>() = testData(1,0); From 8de4ecc8ac6f8b30190e118676868d3e867a0a72 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sat, 7 Jan 2012 21:22:35 +0100 Subject: [PATCH 361/496] add diagnostic messages showing each connection access currently the problem seems to be we're accessing the wrong connection... --- .gitignore | 2 ++ src/proc/play/output-slot-connection.hpp | 2 ++ .../proc/play/diagnostic-output-slot.hpp | 25 ++++++++++++------- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index e2f73fc4d..5c6dc182c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ *.gch ,valgrind.log* *.pyc +/.sconf_temp +/.settings optcache Makefile.in build/* diff --git a/src/proc/play/output-slot-connection.hpp b/src/proc/play/output-slot-connection.hpp index 332943ae5..f83b789c6 100644 --- a/src/proc/play/output-slot-connection.hpp +++ b/src/proc/play/output-slot-connection.hpp @@ -215,6 +215,8 @@ namespace play { static DataSink connectOutputSink (CON& connection) { + TRACE (test, "activating Con=%p", &connection ); + DataSink newSink; newSink.activate(&connection, shutdownConnection); return newSink; diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index 6ec40d009..7e7a2f91d 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -117,6 +117,8 @@ namespace play { BuffHandle trackFrame (FrameID frameNr, BuffHandle const& newBuffer) { + TRACE (test, "Con=%p : track buffer %zu for frame-#%lu" + , this, newBuffer.entryID(), frameNr); REQUIRE (!contains (frameTrackingIndex_,frameNr), "attempt to lock already used frame %lu", frameNr); @@ -140,7 +142,9 @@ namespace play { BuffHandle claimBufferFor(FrameID frameNr) { + TRACE (test, "Con=%p : lock buffer for frame-#%lu", this, frameNr); REQUIRE (!closed_); + return trackFrame (frameNr, buffProvider_.lockBuffer (bufferType_)); } @@ -149,16 +153,22 @@ namespace play { bool isTimely (FrameID frameNr, TimeValue currentTime) { + TRACE (test, "Con=%p : timely? frame-#%lu" + , this, frameNr); + if (Time::ANYTIME == currentTime) return true; - - return currentTime < deadlineFor (frameNr); + else + return currentTime < deadlineFor (frameNr); } void transfer (BuffHandle const& filledBuffer) { + TRACE (test, "Con=%p : transfer buffer %zu" + , this, filledBuffer.entryID()); REQUIRE (!closed_); + pushout (filledBuffer); } @@ -191,13 +201,13 @@ namespace play { , frameGrid_(getTestTimeGrid()) /////////////TODO should rather pass that in as part of a "timings" definition , closed_(false) { - INFO (engine_dbg, "building in-memory diagnostic output sequence"); + INFO (engine_dbg, "building in-memory diagnostic output sequence (at %p)", this); } virtual ~TrackingInMemoryBlockSequence() { - INFO (engine_dbg, "releasing diagnostic output sequence"); + INFO (engine_dbg, "releasing diagnostic output sequence (at %p)", this); } @@ -224,6 +234,8 @@ namespace play { bool wasAllocated (uint frameNr) const { + TRACE (test, "query wasAllocated. Con=%p", this); + return contains (frameTrackingIndex_, frameNr); } @@ -424,11 +436,6 @@ namespace play { return block && block->was_used(); } - - - - private: - }; From a65963a401736118d8090db2100200d6c2a9b311 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 8 Jan 2012 00:12:29 +0100 Subject: [PATCH 362/496] RFC-generator: a better representation of the signature on comments --- admin/rfc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/rfc.sh b/admin/rfc.sh index ce0e4d0ea..43fc36132 100755 --- a/admin/rfc.sh +++ b/admin/rfc.sh @@ -293,7 +293,7 @@ function add_comment() { local name="$1" local nl=$'\n' - local comment="//edit comment$nl $(date +%c) $(git config --get user.name) <$(git config --get user.email)>$nl" + local comment="//edit comment$nl$nl$(git config --get user.name):: '$(date +%c)' ~<$(git config --get user.email)>~$nl" ed "$name" 2>/dev/null <<EOF /endof_comments:/-1i From 484149e73e07914bb0a20e72ab7d3c757e9c640e Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 8 Jan 2012 00:12:54 +0100 Subject: [PATCH 363/496] add a section for library technical documentation --- doc/technical/index.txt | 1 + doc/technical/library/index.txt | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 doc/technical/library/index.txt diff --git a/doc/technical/index.txt b/doc/technical/index.txt index ca4cffc43..d623cbfb6 100644 --- a/doc/technical/index.txt +++ b/doc/technical/index.txt @@ -1,6 +1,7 @@ Technical Documentation ======================= +// Menu : prepend child library // Menu : prepend child backend // Menu : prepend child proc // Menu : prepend child gui diff --git a/doc/technical/library/index.txt b/doc/technical/library/index.txt new file mode 100644 index 000000000..19a1ba716 --- /dev/null +++ b/doc/technical/library/index.txt @@ -0,0 +1,41 @@ +Library documentation +===================== + +//Menu: label Support Lib + +Alongside with the core Lumiera Application, we're also creating several *Libraries*. + +This section holds pages with in-depth technical documentation regarding those. Please don't +expect a coherent or complete coverage for the time being -- library functionality is typically +``discovered'' while in the middle of the implementation, then factored out, reused and remoulded +over time. + +Interface Library +----------------- +The Lumiera Application is designed deliberately in a way to be controllable by scripts. All of +the Session, the models and the backend are fully operational without the GUI. Any significant +functionality can be addressed through the primary Façade Interfaces. + +But, in order to make such an open and extensible application a reality, we need to build up +an 'interface layer' -- provided as library for client code and script adapters to link against. +This library allows to access the primary external interfaces and exposes everything needed to +work with the specifics of the application. + +[,yellow]#TODO as of 1/2012:# _this is just a nebulous plan for now._ Nothing of the work required +really to address facilities from the outside has been done. All we've managed to achieve is actually +to use clean interfaces and decouple the parts of the application appropriately. + +- we still need to identify those core concepts which need to be exposed +- we need to refactor the corresponding data types to resolve cross dependencies to the implementation +- then we could think about moving those data types into an _official Lumiera interface_ + +Support Library +--------------- +There is a huge collection of generic building blocks, re-used more or less frequently. +The're written in a generic way, but in accordance to the rest of the source base, relying only +on the general error handling and logging framework used within Lumiera. (Some facilities may depend +on other parts of the Application; this can be qualified as a bug -- please complain when you've found +something of that sort). + + + + From d2c5297a9d30afaa4151bacb20487985712af508 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 8 Jan 2012 00:13:23 +0100 Subject: [PATCH 364/496] document our custom iterator concept --- doc/devel/rfc/LumieraForwardIterator.txt | 6 + doc/technical/library/iterator.txt | 141 +++++++++++++++++++++++ doc/technical/overview.txt | 47 +++++++- 3 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 doc/technical/library/iterator.txt diff --git a/doc/devel/rfc/LumieraForwardIterator.txt b/doc/devel/rfc/LumieraForwardIterator.txt index e93ca8861..5bb298127 100644 --- a/doc/devel/rfc/LumieraForwardIterator.txt +++ b/doc/devel/rfc/LumieraForwardIterator.txt @@ -149,6 +149,12 @@ for the most common STL containers, plus Map, key and value extractors. Ichthyostega:: 'Sa 16 Apr 2011 00:20:13 CEST' +minor change: removed support for post-increment. It doesn't fit with the concept +and caused serious problems in practice. A correct implementation of post-increment +would require a ``deep copy'' of any underlying data structures. + +Ichthyostega:: 'Sa 07 Jan 2012 21:49:09 CET' ~<prg@ichthyostega.de>~ + //endof_comments: diff --git a/doc/technical/library/iterator.txt b/doc/technical/library/iterator.txt new file mode 100644 index 000000000..e06b9bf4e --- /dev/null +++ b/doc/technical/library/iterator.txt @@ -0,0 +1,141 @@ +Iterators and Pipelines +======================= + +The link:http://c2.com/cgi/wiki?IteratorPattern[Iterator Pattern] allows to +expose the contents or elements of any kind of collection, set or container +for use by client code, without exposing the implementation of the underlying +data structure. Thus, iterators are one of the primary API building blocks. + +Lumiera Forward Iterator +------------------------ +While most modern languages provide some kind of _Iterator,_ the actual semantics +and the fine points of the implementation vary greatly from language to language. +Unfortunately the C++ standard library uses a very elaborate and rather low-level +notion of iterators, which doesn't mix well with the task of building clean interfaces. + +Thus, within the Lumiera core application, we're using our own Iterator concept, +initially defined as link:{ldoc}/devel/rfc/LumieraForwardIterator.html[RfC], +which places the primary focus on interfaces and decoupling, trading off +readability and simplicity for (lesser) performance. + +.Definition +An Iterator is a self-contained token value, +representing the promise to pull a sequence of data + + - rather then deriving from an specific interface, anything behaving + appropriately _is a Lumiera Forward Iterator._ (``duck typing'') + - the client finds a typedef at a suitable, nearby location. Objects of this + type can be created, copied and compared. + - any Lumiera forward iterator can be in _exhausted_ (invalid) state, which + can be checked by +bool+ conversion. + - especially, default constructed iterators are fixed to that state. + Non-exhausted iterators may only be obtained by API call. + - the exhausted state is final and can't be reset, meaning that any iterator + is a disposable one-way-off object. + - when an iterator is _not_ in the exhausted state, it may be _dereferenced_ + (`*i`), yielding the ``current'' value + - moreover, iterators may be incremented (`++i`) until exhaustion. + + +Motivation +~~~~~~~~~~ +The Lumiera Forward Iterator concept is a blend of the STL iterators and +iterator concepts found in Java, C#, Python and Ruby. The chosen syntax should +look familiar to C++ programmers and indeed is compatible to STL containers and +ranges. To the contrary, while a STL iterator can be thought of as being just +a disguised pointer, the semantics of Lumiera Forward Iterators is deliberately +reduced to a single, one-way-off forward iteration, they can't be reset, +manipulated by any arithmetic, and the result of assigning to an dereferenced +iterator is unspecified, as is the meaning of post-increment and stored copies +in general. You _should not think of an iterator as denoting a position_ -- +just a one-way off promise to yield data. + +Another notable difference to the STL iterators is the default ctor and the ++bool+ conversion. The latter allows using iterators painlessly within +for+ +and +while+ loops; a default constructed iterator is equivalent to the STL +container's +end()+ value -- indeed any _container-like_ object exposing +Lumiera Forward Iteration is encouraged to provide such an `end()`-function, +additionally enabling iteration by `std::for_each` (or Lumiera's even more +convenient `util::for_each()`). + +Implementation +~~~~~~~~~~~~~~ +As pointed out above, within Lumiera the notion of ``Iterator'' is a concept +(generic programming) and doesn't mean a supertype (object orientation). Any +object providing a suitable set of operations can be used for iteration. + +- must be default constructible to _exhausted state_ +- must be a copyable value object +- must provide a `bool` conversion to detect _exhausted state_ +- must provide a pre-increment operator (`++i`) +- must allow dreferentiation (`*i`) to yield the current object +- must throw on any usage in _exhausted state_. + +But, typically you wouldn't write all those operations again and again. +Rather, there are two basic styles of iterator implementations, each of which +is supported by some pre defined templates and a framework of helper functions. + +Iterator Adapters +^^^^^^^^^^^^^^^^^ +Iterators built based on these adaptor templates are lightweight and simple to use +for the implementor. But they don't decouple from the actual implementation, and +the resulting type of the iterator usually is rather convoluted. So the typical +usage scenario is, when defining some kind of custom container, we want to add +a `begin()` and `end()` function, allowing to make it behave similar to a STL +container. There should be an embedded typedef `iterator` (and maybe `const_iterator`). +This style is best used within generic code at the implementation level, but is not +well suited for interfaces. + +-> see 'lib/iter-adapter.hpp' + +Iteration Sources +^^^^^^^^^^^^^^^^^ +Here we define a classical abstract base class to be used at interfaces. The template +`lib::IterSource<TY>` is an abstract promise to yield elements of type TY. It defines +an embedded type `iterator` (which is an iterator adapter, just only depending on +this abstract interface). Typically, interfaces declare to return an +`IterSource<TY>::iterator` as the result of some API call. These iterators will +hold an embedded back-reference to ``their'' source, while the exact nature of this +source remains opaque. Obviously, the price to pay for this abstraction barrier is +calling through virtual functions into the actual implementation of the ``source''. + +Helpers to define iterators +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +For both kinds of iterator implementation, there is a complete set of adaptors based +on STL containers. Thus, it's possible to expose the contents of such a container, +or the keys, the values or the unique values just with a single line of code. Either +as iterator adapter (-> see 'lib/iter-adapter-stl.hpp'), or as iteration source +(-> see 'lib/iter-source.hpp') + + +Pipelines +--------- +The extended use of iterators as an API building block naturally leads to building +_filter pipelines_: This technique form functional programming completely abstracts +away the actual iteration, focussing solely on the selecting and processing of +individual items. For this to work, we need special manipulation functions, which +take an iterator and yield a new iterator incorporating the manipulation. (Thus, +in the terminology of functional programming, these would be considered to be +``higher order functions'', i.e. functions processing other functions, not values). +The most notable building blocks for such pipelines are + +filtering:: + each element yielded by the _source iterator_ is evaluated by a _predicate function,_ + i.e. a function taking the element as argument and returning a `bool`, thus answering + a ``yes or no'' question. Only elements passing the test by the predicate can pass + on and will appear from the result iterator, which thus is a _filtered iterator_ + +transforming:: + each element yielded by the _source iterator_ is passed through a _transformnig function,_ + i.e. a function taking an source element and returing a ``transformed'' element, which + thus may be of a completely different type than the source. + +Since these elements can be chained up, such a pipeline may pass several abstraction barriers +and APIs, without either the source or the destination being aware of this fact. The actual +processing only happens _on demand_, when pulling elements from the end of the pipeline. +Oten, this end is either a _collecting step_ (pulling all elements and filling a new container) +or again a IterSource to expose the promise to yield elements of the target type. + +Pipelines work best on _value objects_ -- special care is necessary when objects with _reference +semantics_ are involved. + diff --git a/doc/technical/overview.txt b/doc/technical/overview.txt index 3e389544c..e6a8f0b5c 100644 --- a/doc/technical/overview.txt +++ b/doc/technical/overview.txt @@ -670,17 +670,58 @@ _tbw_ Iterators ~~~~~~~~~ +Iterators serve to decouple a collection of elements from the actual data type +implementation used to manage those elements. The use of iterators is a +design pattern. +-> see link:{ldoc}/technical/library/iterator.html[detailed library documentation] + Lumiera Forward Iterator ^^^^^^^^^^^^^^^^^^^^^^^^ -_tbw_ +Within Lumiera, we don't treat _Iterator_ as a base class -- we treat it as a _concept_ +for generic programming, similar to the usage in the STL. But we use our own definition +of the iterator concept, placing the primary focus on interfaces and decoupling. +Our ``Lumiera Forward Iterator'' concept deliberately removes most of the features +known from the STL. Rather, such an iterator is just the promise for pulling values +_once_. The iterator can be disposed when _exhausted_ -- there is no way of resetting, +moving backwards or doing any kind of arithmetic with such an object. The _exhausted +state can be detected by a +bool+ conversion (contrast this with STL iterators, where +you need to compare to an +end+ iterator). Beyond that, the usage is quite similar, +even compatible to +std::for_each+. Iterator Adapters ^^^^^^^^^^^^^^^^^ -_tbw_ +We provide a collection of pre defined adapter templates to ease building +Lumiera Forward Iterators. + +- a generic solution using a _iteration control_ callback API +- the `lib::RangeIter` just wraps up a pair of iterators for ``current position'' + and ``and'' -- compatible with the STL +- there is a variant for automatically dereferencing pointers +- plus a set of adapters for STL containers, allowing to expose each value, each + key, distinct values and so on. + +Iterator Adapters are designed for ease of use, they don't conceal the underlying +implementation (and the actual type is often quite convoluted). + +Iteration Sources +^^^^^^^^^^^^^^^^^ +To the contrary, the `lib::IterSource<TY>` template is an abstract base class. +This allows to expose the promise to deliver values through any kind of API, without +disclosing the actual implementation. Obviously, this requires the use of virtual +functions for the actual iteration. + +Again, there are pre-defined adaptors for STL containers, but the actual container +is concealed in this case. Itertools ^^^^^^^^^ -_tbw_ +Iterators can be used to build pipelines. This technique from functional programming +allows to abstract away the actual iteration completely, focussing only on the way +individual elements are processed. To support this programming style, several support +templates are provided to build _filtering iterators, transforming iterators,_ to pick +only _unique values,_ to _take a snapshot on-the-fly_ etc. There are convenience +builder functions for those operations, figuring out the actual source and destination +types by template metaprogramming. Front-end for boost::format From 2bc5d3b3676f3b2d765ceff86ea3ad50caed91c0 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 8 Jan 2012 00:14:05 +0100 Subject: [PATCH 365/496] document further library facilities, some clean-up --- doc/technical/overview.txt | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/doc/technical/overview.txt b/doc/technical/overview.txt index e6a8f0b5c..919d365c8 100644 --- a/doc/technical/overview.txt +++ b/doc/technical/overview.txt @@ -743,7 +743,7 @@ implementation confined to a single translation unit, while still being able to use all primitive types as usual with boost::format or printf. Additionally, our frontend automatically invokes a custom or built-in string conversion, if applicable, it dereferences pointers and catches all errors encountered while -the formatting. So it's well suited for usage in error handling code. +in formatting. So it's well suited for usage in error handling code. [source,C] ------------------------------------------------------------ @@ -776,7 +776,18 @@ objects also implement a front-end interface for client access. _tbw_ .ownership managing collection -_tbw_ +When implementing services, frequently we encountered the situation that +a manager object creates and owns some component elements or sub-services. +The library provides two special collections, which also take ownership +of their contents and care for automatic clean-up. Especially, contrary +to the STL containers, those custom containers support use of +_non-copyable object_ (as a rule, all objects with reference semantics +are defined non-copyable in Lumiera). + +- the `ScopedPtrVect` is a vector taking ownership of heap allocated objects +- the `ScopedCollection` is a fixed-size collection, holding all the child + objects within a single (heap allocated) storage block + .opaque holder There is a family of holder objects, all based on placement-new of the @@ -793,7 +804,8 @@ fit well with the chosen approach to object orientation, where polymorphism reli on reference semantics. Thus, most of the fundamental design patterns drive us into having an object manager somewhere hidden within the implementation level, to manage the memory for maintaining the subclass instances to be concealed -at the usage site. + +at the usage site. + To avoid this dilemma, we utilise the technique of the opaque holder to provide objects with value semantics, while actually placing the instance of a subclass into the inline buffer. Clients access this embedded object by automatic type From d732e7e211d09b98c5ad5f2ec0124f2335fce2bd Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 8 Jan 2012 01:14:36 +0100 Subject: [PATCH 366/496] Lumiera Forward Iterators: remove support for post-increment --- src/lib/iter-adapter.hpp | 22 ------ src/proc/mobject/session/defs-registry.hpp | 1 - .../session/defs-registry-impl-test.cpp | 76 +++++++++---------- .../proc/play/output-slot-protocol-test.cpp | 10 +-- tests/lib/iter-adapter-test.cpp | 7 +- 5 files changed, 48 insertions(+), 68 deletions(-) diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index 1b92fd8ac..9cb5f6bee 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -191,15 +191,6 @@ namespace lib { return *this; } - IterAdapter - operator++(int) - { - _maybe_throw(); - IterAdapter oldPos(*this); - iterate(); - return oldPos; - } - bool isValid () const { @@ -334,13 +325,6 @@ namespace lib { return *this; } - RangeIter - operator++(int) - { - _maybe_throw(); - return RangeIter (p_++,e_); - } - bool isValid () const { @@ -551,12 +535,6 @@ namespace lib { return *this; } - PtrDerefIter - operator++(int) - { - return PtrDerefIter (i_++); - } - bool isValid () const { diff --git a/src/proc/mobject/session/defs-registry.hpp b/src/proc/mobject/session/defs-registry.hpp index 53bfa7a93..d72c92014 100644 --- a/src/proc/mobject/session/defs-registry.hpp +++ b/src/proc/mobject/session/defs-registry.hpp @@ -236,7 +236,6 @@ namespace session { public: P<TAR> operator* () { return ptr; } bool hasNext () { return next || findNext(); } - Iter operator++ (int) { Iter tmp=*this; operator++(); return tmp; } Iter& operator++ () { ptr=findNext(); diff --git a/tests/components/proc/mobject/session/defs-registry-impl-test.cpp b/tests/components/proc/mobject/session/defs-registry-impl-test.cpp index 756001376..6ec3ed9b7 100644 --- a/tests/components/proc/mobject/session/defs-registry-impl-test.cpp +++ b/tests/components/proc/mobject/session/defs-registry-impl-test.cpp @@ -172,33 +172,33 @@ namespace test { { Iter13 i (reg_->candidates(Q13 ("irrelevant query"))); CHECK ( i.hasNext()); - CHECK ( *i++ == o1); // ordered according to the degree of the queries - CHECK ( *i++ == o2); - CHECK ( *i++ == o3); - CHECK ( *i++ == o3); - CHECK ( *i++ == o2); - CHECK ( *i == o1); + CHECK ( *i == o1); ++i; // ordered according to the degree of the queries + CHECK ( *i == o2); ++i; + CHECK ( *i == o3); ++i; + CHECK ( *i == o3); ++i; + CHECK ( *i == o2); ++i; + CHECK ( *i == o1); CHECK (!i.hasNext()); CHECK (! *++i ); // null after end i = reg_->candidates(q3); - CHECK ( *i++ == o3); // found by direct match - CHECK ( *i++ == o1); // followed by the ordered enumeration - CHECK ( *i++ == o2); - CHECK ( *i++ == o3); - CHECK ( *i++ == o3); - CHECK ( *i++ == o2); - CHECK ( *i++ == o1); + CHECK ( *i == o3); ++i; // found by direct match + CHECK ( *i == o1); ++i; // followed by the ordered enumeration + CHECK ( *i == o2); ++i; + CHECK ( *i == o3); ++i; + CHECK ( *i == o3); ++i; + CHECK ( *i == o2); ++i; + CHECK ( *i == o1); ++i; CHECK (!i.hasNext()); i = reg_->candidates(Q13()); - CHECK ( *i++ == o1); // found by direct match to the empty query - CHECK ( *i++ == o1); - CHECK ( *i++ == o2); - CHECK ( *i++ == o3); - CHECK ( *i++ == o3); - CHECK ( *i++ == o2); - CHECK ( *i++ == o1); + CHECK ( *i == o1); ++i; // found by direct match to the empty query + CHECK ( *i == o1); ++i; + CHECK ( *i == o2); ++i; + CHECK ( *i == o3); ++i; + CHECK ( *i == o3); ++i; + CHECK ( *i == o2); ++i; + CHECK ( *i == o1); ++i; CHECK (!i.hasNext()); uint d=0; @@ -230,12 +230,12 @@ namespace test { Iter13 i (reg_->candidates(q4)); CHECK ( i.hasNext()); - CHECK ( *i++ == o1); // ordered according to the degree of the queries - // but the o2 entries are missing - CHECK ( *i++ == o3); - CHECK ( *i++ == o3); + CHECK ( *i == o1); ++i; // ordered according to the degree of the queries + // but the o2 entries are missing + CHECK ( *i == o3); ++i; + CHECK ( *i == o3); ++i; // missing - CHECK ( *i == o1); + CHECK ( *i == o1); CHECK (!i.hasNext()); o3.reset(); // killing the only reference.... @@ -243,33 +243,33 @@ namespace test { i = reg_->candidates(Q13 ("something")); CHECK ( i.hasNext()); - CHECK ( *i++ == o1); // ordered according to the degree of the queries - // but now also the o3 entries are missing... - CHECK ( *i == o1); + CHECK ( *i == o1); ++i; // ordered according to the degree of the queries + // but now also the o3 entries are missing... + CHECK ( *i == o1); CHECK (!i.hasNext()); CHECK ( reg_->put (o1, q5)); // trying to register the same object at the same place // doesn't change anything (but counts as "success") i = reg_->candidates(q5); - CHECK ( *i++ == o1); // direct match - CHECK ( *i++ == o1); - CHECK ( *i++ == o1); + CHECK ( *i == o1); ++i; // direct match + CHECK ( *i == o1); ++i; + CHECK ( *i == o1); ++i; CHECK (!i.hasNext()); CHECK (!reg_->put (o2, q5)); // trying to (re)register o2 with a existing query // counts as failure (nothing changes) i = reg_->candidates(q5); - CHECK ( *i++ == o1); // direct match - CHECK ( *i++ == o1); - CHECK ( *i++ == o1); + CHECK ( *i == o1); ++i; // direct match + CHECK ( *i == o1); ++i; + CHECK ( *i == o1); ++i; CHECK (!i.hasNext()); CHECK ( reg_->put (o2, q2)); // trying to (re)register o2 with another query succeeds i = reg_->candidates(q2); - CHECK ( *i++ == o2); // direct match - CHECK ( *i++ == o1); - CHECK ( *i++ == o2); // inserted here in the dataset - CHECK ( *i++ == o1); + CHECK ( *i == o2); ++i; // direct match + CHECK ( *i == o1); ++i; + CHECK ( *i == o2); ++i; // inserted here in the dataset + CHECK ( *i == o1); ++i; CHECK (!i.hasNext()); CHECK ( reg_->forget (o1)); diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp index a1e0fef09..18b54388f 100644 --- a/tests/components/proc/play/output-slot-protocol-test.cpp +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -84,8 +84,8 @@ namespace test { // "calculation streams" for the individual // Channels to be output through this slot. OutputSlot::OpenedSinks sinks = alloc.getOpenedSinks(); - DataSink sink1 = *sinks++; - DataSink sink2 = *sinks++; + DataSink sink1 = *sinks; + DataSink sink2 = *++sinks; // within the frame-calculation "loop" // we perform an data exchange cycle @@ -127,12 +127,12 @@ namespace test { DiagnosticOutputSlot::OutFrames stream1 = checker.getChannel(1); CHECK ( stream0); - CHECK (*stream0++ == testData(0,0)); + CHECK (*stream0 == testData(0,0)); ++stream0; CHECK (!stream0); CHECK ( stream1); - CHECK (*stream1++ == testData(1,0)); - CHECK (*stream1++ == testData(1,1)); + CHECK (*stream1 == testData(1,0)); ++stream1; + CHECK (*stream1 == testData(1,1)); ++stream1; CHECK (!stream1); #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #819 #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #819 diff --git a/tests/lib/iter-adapter-test.cpp b/tests/lib/iter-adapter-test.cpp index 6376c49bc..ef6f2c9da 100644 --- a/tests/lib/iter-adapter-test.cpp +++ b/tests/lib/iter-adapter-test.cpp @@ -215,7 +215,10 @@ namespace test{ // now for example the client could.... while ( range ) - cout << "::" << *range++; + { + cout << "::" << *range; + ++range; + } cout << endl; CHECK (isnil (range)); @@ -317,7 +320,7 @@ namespace test{ TestContainer::ref_iterator rI (elms.begin_ref()); CHECK (0 == *rI ); - CHECK (0 == *rI++); + ++rI; CHECK (1 == *rI ); CHECK (2 == *++rI); From 24a1060ae9cd8d8b187de598b943c49d378f8b1a Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 8 Jan 2012 01:32:45 +0100 Subject: [PATCH 367/496] fix inconsistency in OutputSlot protocol test need to distinguish between nominal frame number and the sequence number in the output frame stream --- .../proc/play/diagnostic-output-slot.hpp | 35 ++++++++----------- .../proc/play/output-slot-protocol-test.cpp | 24 ++++++------- 2 files changed, 25 insertions(+), 34 deletions(-) diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index 7e7a2f91d..e6e971a9d 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -404,38 +404,31 @@ namespace play { bool - buffer_was_used (uint channel, FrameID frame) + frame_was_allocated (uint channel, FrameID nominalFrame) { return accessSequence(channel) - .wasAllocated(frame); + .wasAllocated(nominalFrame); } bool - buffer_unused (uint channel, FrameID frame) - { - return !buffer_was_used(channel, frame); - } - - - bool - buffer_was_closed (uint channel, FrameID frame) + output_was_emitted (uint channel, FrameID outputFrame) { diagn::Block const *block = accessSequence(channel) - .accessEmittedBuffer(frame); - return block - && block->was_closed(); - } - - - bool - emitted (uint channel, FrameID frame) - { - diagn::Block const *block = accessSequence(channel) - .accessEmittedBuffer(frame); + .accessEmittedBuffer(outputFrame); return block && block->was_used(); } + + + bool + output_was_closed (uint channel, FrameID outputFrame) + { + diagn::Block const *block = accessSequence(channel) + .accessEmittedBuffer(outputFrame); + return block + && block->was_closed(); + } }; diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp index 18b54388f..028346593 100644 --- a/tests/components/proc/play/output-slot-protocol-test.cpp +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -109,19 +109,19 @@ namespace test { // 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.frame_was_allocated (0,123)); + CHECK (!checker.frame_was_allocated (0,124)); + CHECK ( checker.frame_was_allocated (1,123)); + CHECK ( checker.frame_was_allocated (1,124)); - CHECK (checker.buffer_was_closed (0,0)); - CHECK (checker.buffer_was_closed (1,0)); - CHECK (checker.buffer_was_closed (1,1)); + CHECK (checker.output_was_closed (0,0)); + CHECK (checker.output_was_closed (1,0)); + CHECK (checker.output_was_closed (1,1)); - CHECK ( checker.emitted (0,0)); - CHECK (!checker.emitted (0,1)); - CHECK ( checker.emitted (1,0)); - CHECK ( checker.emitted (1,1)); + CHECK ( checker.output_was_emitted (0,0)); + CHECK (!checker.output_was_emitted (0,1)); + CHECK ( checker.output_was_emitted (1,0)); + CHECK ( checker.output_was_emitted (1,1)); DiagnosticOutputSlot::OutFrames stream0 = checker.getChannel(0); DiagnosticOutputSlot::OutFrames stream1 = checker.getChannel(1); @@ -134,8 +134,6 @@ namespace test { CHECK (*stream1 == testData(1,0)); ++stream1; CHECK (*stream1 == testData(1,1)); ++stream1; CHECK (!stream1); -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #819 -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #819 } }; From f5290a99a31e13ef9b49de7e6383e6d6b8a51276 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Sun, 8 Jan 2012 03:06:32 +0100 Subject: [PATCH 368/496] OutputSlot : simulated usage protocol passes unit test OutputSlotProtocol_test Some parts are still missing - timings _ initialisation --- src/proc/play/output-slot-connection.hpp | 2 ++ tests/47playout.tests | 2 +- .../components/proc/play/diagnostic-output-slot.hpp | 4 ++-- .../proc/play/output-slot-protocol-test.cpp | 13 +------------ 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/proc/play/output-slot-connection.hpp b/src/proc/play/output-slot-connection.hpp index f83b789c6..ed61ef864 100644 --- a/src/proc/play/output-slot-connection.hpp +++ b/src/proc/play/output-slot-connection.hpp @@ -162,6 +162,7 @@ namespace play { OpenedSinks getOpenedSinks() { + //////////////////////////TICKET #878 not re-entrant, lifecycle isn't clear REQUIRE (this->isActive()); return transform (eachElm(connections_), connectOutputSink); } @@ -189,6 +190,7 @@ namespace play { void init() ///< derived classes need to invoke this to build the actual connections { + //////////////////////////TICKET #878 really build all at once? or on demand? connections_.populate_by (&ConnectionStateManager::buildConnection, this); } diff --git a/tests/47playout.tests b/tests/47playout.tests index d59689f3b..0bdc2a9ad 100644 --- a/tests/47playout.tests +++ b/tests/47playout.tests @@ -2,6 +2,6 @@ TESTING "Component Test Suite: Player and Output" ./test-components --group=play -PLANNED "Output Slot Protocol" OutputSlotProtocol_test <<END +TEST "Output Slot Protocol" OutputSlotProtocol_test <<END return: 0 END diff --git a/tests/components/proc/play/diagnostic-output-slot.hpp b/tests/components/proc/play/diagnostic-output-slot.hpp index e6e971a9d..e78062589 100644 --- a/tests/components/proc/play/diagnostic-output-slot.hpp +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -216,7 +216,7 @@ namespace play { TestFrame const * accessEmittedFrame (uint frameNr) const { - if (frameNr <= buffProvider_.emittedCnt()) + if (frameNr < buffProvider_.emittedCnt()) return & accessFrame(frameNr); else return 0; ////////////////////////////////TICKET #856 @@ -225,7 +225,7 @@ namespace play { diagn::Block const * accessEmittedBuffer (uint bufferNr) const { - if (bufferNr <= buffProvider_.emittedCnt()) + if (bufferNr < buffProvider_.emittedCnt()) return & accessBlock(bufferNr); else return 0; diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp index 028346593..7c1e04f7c 100644 --- a/tests/components/proc/play/output-slot-protocol-test.cpp +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -22,33 +22,22 @@ #include "lib/test/run.hpp" -//#include "lib/util.hpp" + #include "proc/play/diagnostic-output-slot.hpp" #include "proc/engine/buffhandle.hpp" #include "proc/engine/testframe.hpp" -//#include <boost/format.hpp> -//#include <iostream> - -//using boost::format; -//using std::string; -//using std::cout; namespace proc { namespace play { namespace test { -// using lib::AllocationCluster; -// using mobject::session::PEffect; using proc::engine::BuffHandle; using proc::engine::test::testData; using proc::engine::test::TestFrame; - namespace { // Test fixture - - } /******************************************************************* From 0e08f269f5b73415cfbfd99068c0ee771b67ac68 Mon Sep 17 00:00:00 2001 From: Ichthyostega <prg@ichthyostega.de> Date: Wed, 11 Jan 2012 06:55:54 +0100 Subject: [PATCH 369/496] remove the superfluous TiddlyWikis ..after integrating all still relevant asciidoced content into the main website. --- doc/design/backend/Scheduler.txt | 25 + doc/design/backend/index.txt | 111 +- doc/design/plugins/PluginBrainstorm.txt | 96 +- doc/design/plugins/PluginVersioning.txt | 44 + doc/technical/build/Dependencies.txt | 90 + doc/technical/build/index.txt | 5 +- wiki/backend.html | 11700 --------------------- wiki/compatibility.html | 11655 --------------------- wiki/index.html | 1918 +--- wiki/support_library.html | 11816 ---------------------- wiki/todo.html | 11668 --------------------- 11 files changed, 367 insertions(+), 48761 deletions(-) create mode 100644 doc/design/backend/Scheduler.txt create mode 100644 doc/design/plugins/PluginVersioning.txt create mode 100644 doc/technical/build/Dependencies.txt delete mode 100644 wiki/backend.html delete mode 100644 wiki/compatibility.html delete mode 100644 wiki/support_library.html delete mode 100644 wiki/todo.html diff --git a/doc/design/backend/Scheduler.txt b/doc/design/backend/Scheduler.txt new file mode 100644 index 000000000..49793e06f --- /dev/null +++ b/doc/design/backend/Scheduler.txt @@ -0,0 +1,25 @@ +The Scheduler +------------- +:Author: CehTeh +:Date: 6/2007 + +//MENU: label Scheduler + +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 plus some job specific identified. + +There are following (non exhaustive) kinds of jobs: + +* started job +* job to be canceled +* unscheduled job +* dependency providing jobs + +Jobs implement a kind of future. We try hard to avoid any blocking waits. +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 pre configured behaviour for the case of failure or deadline miss. +Any canceling and expireing jobs gets noted in *Statistics* to adjust performance and timings +for optimal performance and I/O throughput. + diff --git a/doc/design/backend/index.txt b/doc/design/backend/index.txt index 44c57ca8b..19de56002 100644 --- a/doc/design/backend/index.txt +++ b/doc/design/backend/index.txt @@ -1,5 +1,114 @@ Design Documents: Backend ========================= -Eventually, this will have design documentation for the Backend. +What follows is a summary regarding the design of Lumiera's *Data Handling Backend* + +This is the foundation layer responsible for any high performance or high volume +data access. Within Lumiera, ther are two main kinds how data is handled: + +* The Session and the object models manipulated through the GUI is kept in memory. + It is backed by a _storage backend,_ which provides database like storage and + especially logging, replaying and ``Undo'' of all ongoing modifications.. +* Media data is handled _frame wise_ -- as described below. + +The backend uses *memory mapping* to make data available to the program. +This is somewhat different to the 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 might access more files +than the OS can handle at a any time, thus the for _Files used by the Backend,_ it needs a +*FilehandleCache* to manage file handle dynamically. + +Which parts of a file are actually mapped to physical RAM is managed by the kernel; +it keeps a *FileMapCache* to manage the *FileMaps* we've set up. +In the End, the application itself only requests *Data Frames* 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_. The intention +is to manage the rendering _just in time_. + +The prefetcher keeps *Statistics* for optimizing performance. + + +Accessing Files +--------------- + ++FileDescriptor+ is the superclass of all possible filetypes, it has a weak reference to a ++FileHandle+ which is managed in within the +FilehandleCache+. On creation, only the existence +(when reading) or access for write for new files are checked. The +FileDescriptor+ stores some +generic metadata about the underlying file and intended use. But the actual opening is done on demand. + +The _content of files is memory mapped_ into the process address space. +This is managed by +FileMap+ entries and a +FileMapCache+. + +File Handles +~~~~~~~~~~~~ +A +FilehandleCache+ serves to store a finite maximum number of +FileHandles+ as a MRU list. +FileHandles are managed by the +FilehandleCache+; basically 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. + +File Mapping +~~~~~~~~~~~~ +The +FileMapCache+ keeps a list of +FileMaps+, which are currently not in use and subject of aging. +Each +FileMap+ object contains many +Frames+. The actual layout depends on the type of the File. +Mappings need to be _page aligned_ while Frames can be anywhere within a file and dynamically sized. + +All established ++FileMap++s are managed together in a central +FileMapCache+. +Actually, +FileMap+ objects are transparent to the application. The upper layers will just +request Frames by position and size. Thus, the +File+ entities associate a filename with the underlying +low level File Descriptor and access + +Frames +~~~~~~ ++Frames+ are the smallest datablocks handled by the Backend. The application tells the Backend to make +Files available and from then on just requests Frames. Actually, those 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). Frames are referenced by a smart-pointer like object which manages the lifetime +and caching behavior. + +Each frame referece can be in one out of three states: + +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 + +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 whenever a frame is not available. The backend can't serve a cached +version of the frame, a (probably recursive) rendering request will be issued. + +Prefetching +~~~~~~~~~~~ +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 Frames must eventually be rendered to the cache. + The Backend will send +Renderjobs+ to the +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. + + +-> more about link:Scheduler.html[the Scheduling of calculation jobs] + diff --git a/doc/design/plugins/PluginBrainstorm.txt b/doc/design/plugins/PluginBrainstorm.txt index 0d07ca97e..b145cdf08 100644 --- a/doc/design/plugins/PluginBrainstorm.txt +++ b/doc/design/plugins/PluginBrainstorm.txt @@ -3,12 +3,81 @@ Plugin Brainstorm :Author: Christian Thäter :Date: 2008-09-15 -Raw Version ------------ +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. -[cpp] -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Interface Definition +-------------------- + +Interfaces are declared in header files. They use some tool macros to give a convenient definition language. +Basically, Interfaces are fixed -- with the exception that new functions may be added. +link:PluginVersioning.html[Plugin Version Management] should stay out of the view most of the time. + +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. + +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. +Each function prototype must be given with its different parts: + +- return type +- name +- arguments list +- version. + + +.Example +[source,c] +----------------------------------------- +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 interface/plugin framework will expand the above definitions into: + +[source,c] +----------------------------------------- +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); +}; +----------------------------------------- + + +Implementation of Interfaces +---------------------------- +Interfaces can be implemented either in core code or through plugins. +In each case, such an _instantiation_ of an interface means that actual functions are mapped +to the corresponding slots in the interface structure. + + +.Implementing an interface +[source,c] +----------------------------------------- LUMIERA_INTERFACE_DECLARE (interface_descriptor, 0, /* The following slots are some human-readable descriptions of certain properties */ LUMIERA_INTERFACE_SLOT (const char*, name, (LumieraInterface)), @@ -18,4 +87,21 @@ LUMIERA_INTERFACE_DECLARE (interface_descriptor, 0, LUMIERA_INTERFACE_SLOT (const char*, license, (LumieraInterface)) /* TODO add more things here, dependencies, provisions etc */ ); -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +----------------------------------------- + + +Lumiera Plugin API +------------------- +The Lumiera Interface/Plugin framework provides some 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. + +loading and opening a Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Plugins are looked up in `$LUMIERA_PLUGIN_PATH`, which is a colon separated list of directories, +and then in a specific ``Lumiera plugin dir'', where standard plugins get installed alongside +with the Application + + diff --git a/doc/design/plugins/PluginVersioning.txt b/doc/design/plugins/PluginVersioning.txt new file mode 100644 index 000000000..3bdffa0c2 --- /dev/null +++ b/doc/design/plugins/PluginVersioning.txt @@ -0,0 +1,44 @@ + PluginVersioningCases +====================== +:Author: MichaelPloujnikov +:Date Created: 200707121127 +:Date Changed: 200707160404 +:Count Changes: 46 + +//MENU: label Plugin Version + + +Compatibility matrix +-------------------- + +.Source compatibility +[grid="rows"] +`100`200`200~~~~ +*~CALLER~ \ ^CALLEE^**,OLD^**^,NEW^**^ +OLD,works,works but a recent interface definition must be available +NEW,works,works +~~~~ + +.Binary compatibility +[grid="rows"] +`100`200`200~~~~ +*~CALLER~ \ ^CALLEE^**,OLD^**^,NEW^**^ +OLD,works,works +NEW,caller gets "revision not sufficient" at runtime 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. diff --git a/doc/technical/build/Dependencies.txt b/doc/technical/build/Dependencies.txt new file mode 100644 index 000000000..92cd38db7 --- /dev/null +++ b/doc/technical/build/Dependencies.txt @@ -0,0 +1,90 @@ +Dependencies +------------ +:Author: CehTeh +:Date: 3/2008 + + +Lumiera is written for GNU/Linux. We try to make the best out of modern system programming techniques +to reach the best possible performance. 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. + +Porting to other more or less similar platforms will be possible, if -- by coincidence -- +Someone(TM) helps with porting. + +Having said that -- for the time being, the core team won't spend much effort on porting. + +Platform +-------- +We work and test on PC hardware, 32 and 64 bit. It is intended that Lumiera supports +other platforms running run GNU/Linux. +Lumiera expects a 'standard' desktop installation running a Xserver. + +Graphics:: + There are no special requirements for the graphic system. + Hardware accelleration will likely be added later through extensions, + but will remain strictyl optional. (For now we'll watch the evolution + in that area and might revisit that topic when there are more compelling + and widely supported solutions available) + +Disks:: + Video editing requires decent disk speed, so it is suggested to use a + fast/big array of disks configured as raid. + +Special Hardware:: + Sopport for special hardware would be possible, but depends on certain conditions ++ +* we need access / donations for the hardware +* specs and APIs must be open. +* someone to do the actual interfacing and support needs to join the team + + +Languages and Tools +------------------- + +* C / C++ + + - a C99 / C++98 compatible compiler + - GCC 4.4 or better is fine. Basically we try to use just the stock language. + On rare occasions, we _did_ use some GCC extensions, but there would be workarounds, + should this become a problem. + - std::tr1 extensions for C++ (smart-ptrs, hashtables, function objects) + +* BOOST (listed below are the DEBIAN package names) + - libboost-dev (at least 1.40) + - libboost-program-options-dev + - libboost-program-options-dev + - libboost-filesystem-dev + - libboost-regex-dev + +* Script languages + - Python (2.5) for build scripts + - bash (some test scripts use bash specific extensions) + - Lua is planned to become a general glue and binding language + +Build Tools +~~~~~~~~~~~ + +* Git +* SCons +* pkg-config +* Doxygen + +We maintain a Debian package (debhelper, CDBS, git-buildpackage) + +Libraries +~~~~~~~~~ + +* BOOST +* NoBug +* http://gmerlin.sourceforge.net/gavl.html[GAVL] (for raw media support) +* for the GUI: gtkmm-2.4 gdl-1.0 libglibmm-2.4 cairomm-1.0 xv + - libgtkmm-2.4-dev + - libcairomm-1.0-dev + - libgdl-1-dev + - libglibmm-2.4-dev, requiring glib2.0 and gthread-2.0 + - libxv-dev + - librsvg-2.0 and librsvg2-dev for rendering Icons + + diff --git a/doc/technical/build/index.txt b/doc/technical/build/index.txt index 83805ba4e..a92d215b4 100644 --- a/doc/technical/build/index.txt +++ b/doc/technical/build/index.txt @@ -7,9 +7,8 @@ As work progresses, we will add more information on the Lumiera build system. build -- continuous integration -- packaging -* SCons -* Autotools -* Dependencies +* link:SCons.html[Buildsystem] +* link:Dependencies.html[Dependencies] * link:BuildDroneDraft.html[»Builddrone« concept from 2008] * Packaging: link:LumieraDebianPackage.html[Debian] RPM * Lumiera link:../infra/debianDepot.html/[debian depot] diff --git a/wiki/backend.html b/wiki/backend.html deleted file mode 100644 index 49bfb6727..000000000 --- a/wiki/backend.html +++ /dev/null @@ -1,11700 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> -<script type="text/javascript"> -//<![CDATA[ -var version = {title: "TiddlyWiki", major: 2, minor: 2, revision: 3, date: new Date("Jun 17, 2007"), extensions: {}}; -//]]> -</script> -<!-- -TiddlyWiki created by Jeremy Ruston, (jeremy [at] osmosoft [dot] com) - -Copyright (c) UnaMesa Association 2004-2007 - -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 the UnaMesa Association 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. ---> -<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> -<!--PRE-HEAD-START--> -<!--{{{--> -<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> -<!--PRE-HEAD-END--> -<title> 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 370/496] 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 371/496] 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 372/496] 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 373/496] 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 374/496] 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 375/496] 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 376/496] 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 377/496] 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 378/496] 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 379/496] 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 380/496] 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 381/496] 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 382/496] 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 383/496] 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 384/496] 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 385/496] 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 386/496] 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 387/496] 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 388/496] 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. From ca3838cb7501bc7e52336b5d91a7e33a96008945 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 12 Jan 2012 21:08:40 +0100 Subject: [PATCH 389/496] mention January meeting in the summaries page --- doc/devel/meeting_summary/index.txt | 36 ++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/doc/devel/meeting_summary/index.txt b/doc/devel/meeting_summary/index.txt index 6a1d1cb06..ed3cf1f5a 100644 --- a/doc/devel/meeting_summary/index.txt +++ b/doc/devel/meeting_summary/index.txt @@ -12,6 +12,40 @@ Anyone interested in Lumiera development is also encouraged to read mailing list archives and other documentation. +11 Jan 2012 +----------- + +Topics +~~~~~~ + * Website status. Clean-up of TiddlyWikis + * cross-links and link checking for the website + * promotion: Libre Graphics Magazine + * rendering to file: library solutions + +Summary +^^^^^^^ + * link:2012-01-11.html[Summary (by cehteh)] + + + + +14 Dec 2011 +----------- + +Topics +~~~~~~ + * discussion about build systems. + * review of the SCons build scripts + * some details about the scheduler and invoking render jobs + +//// +Summary +^^^^^^^ + * link:2011-12-14.html[IRC Transcript (prepared by XXXX)] +//// + + +************************ During Summer 2011 we kept up our monthly IRC meetings -- rather casual exchange for the time being. @@ -21,7 +55,7 @@ for the time being. - 10 Aug 2011 - 13 Jul 2011 - 8 Jun 2011 - +************************ 11 May 2011 ----------- From c30f5add7fe57e2587e244f45900e08bb22423ed Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Sat, 25 Aug 2012 13:23:41 +0200 Subject: [PATCH 390/496] Add 'none' target to scons 'scons none' will not build anythinf but runs the configuration checks to determine if the system and libraries are sane to build Lumiera, this will be used in builddrone later. --- SConstruct | 1 + 1 file changed, 1 insertion(+) diff --git a/SConstruct b/SConstruct index d2468b374..8fe12e998 100644 --- a/SConstruct +++ b/SConstruct @@ -77,6 +77,7 @@ env.Default('build') env.Alias ('all', build + testsuite + doxydoc) env.Alias ('doc', doxydoc) +env.Alias ('none') env.Alias('install', gui) env.Alias('install', '$DESTDIR') From 92df23495a0938b74e2ca05baf2b050ecae5be1b Mon Sep 17 00:00:00 2001 From: Benny Lyons Date: Sun, 29 Jul 2012 17:17:46 +0100 Subject: [PATCH 391/496] Glossery terms sorted --- doc/user/intro/Glossary.txt | 335 ++++++++++++++++++------------------ 1 file changed, 170 insertions(+), 165 deletions(-) diff --git a/doc/user/intro/Glossary.txt b/doc/user/intro/Glossary.txt index 9f5f7c2ea..aa9834231 100644 --- a/doc/user/intro/Glossary.txt +++ b/doc/user/intro/Glossary.txt @@ -5,6 +5,108 @@ Glossary 'NOTE Draft, please help rephrase/review and sort this terms, shorten explanations, the long explanation is the topic of the document above..' + anchor:AssetsView[] link:#AssetsView[->]Assets View:: + The windows showing and managing the available things to work with. + This are the ingested footage, already composed Clips, available + Sub-Projects, Effects, Transitions and internal artefacts. + + anchor:Builder[] link:#Builder[->]Builder:: + A kind of compiler which creates Low Level/Processing Graphs, by + traversing and evaluating the relevant parts of the high-level-model + and using the Rules System. + + anchor:Busses[] link:#Busses[->]Busses:: + A list of 'global Pipes' representing the possible outputs (master + busses) similar to audio mixing desk. A bus defines the properties of + the rendered output (Framerate, Resolution, Colorformat and so on). + Busses are part of a Timeline. + + anchor:ConfigSystem_Preferences[] link:#ConfigSystem_Preferences[->]Config System/Preferences:: + TODO: agree on one term here + Provides defaults for all kinds of application configurations. These + include machine specific configurations for performance + characteristics, File and Plugins Paths and configuration data and so + on. Note that this only provides defaults for otherwise not yet set + data. Many settings will then be stored within the project and the + Config/Preferences becomes overridden by that. + + anchor:ControllerGui[] link:#ControllerGui[->]Controller Gui:: + This can be either a full Software implementation for a Transport + control (Widgets for Start/Stop/Rev/Ffw etc) or some Gui managing an + Input Device. They share some feature to attach them to controllable + gui-entities (Viewers, Timeline Views) + + anchor:Cursor[] link:#Cursor[->]Cursor:: + playback- or edit position + + anchor:Focus[] link:#Focus[->]Focus:: + TBD + + anchor:HighLevelModel[] link:#HighLevelModel[->]High Level Model:: + All the session content to be edited and manipulated by the user + through the GUI. The high-level-model will be translated by the + Builder into the Low Level Model for rendering. + + anchor:InputDevice[] link:#InputDevice[->]Input Device:: + some hardware controler, like a extra Keyboard, Midi Mixer, Jog, .. + TODO: decide if the main keyboard as special (global) state. + + anchor:LowLevelModel[] link:#LowLevelModel[->]Low Level Model:: + The generated Processing Graph, to be ``performed'' within the engine + to yield rendered output + + anchor:MediaStream[] link:#MediaStream[->]MediaStream:: + Media data is supposed to appear structured as stream(s) over time. + While there may be an inherent internal structuring, at a given + perspective any stream is a unit and homogeneous. In the context of + digital media data processing, streams are always quantized, which means + they appear as a temporal sequence of data chunks called frames. + + anchor:OutputDesignation[] link:#OutputDesignation[->]OutputDesignation:: + A specification denoting where to connect the output of a pipe. + It might either be given _absoulutely_, i.e as Pipe-ID, + or by an _relative_ or _indirect_ specification + + anchor:OutputManager[] link:#OutputManager[->]OutputManager:: + manages all external outputs of the application and provides output + slots targetting these. + + anchor:OutputMapping[] link:#OutputMapping[->]OutputMapping:: + translates one output designation into another one, e.g. when hooking + up a sequence as virtual clip within another sequence + + anchor:OutputSlot[] link:#OutputSlot[->]OutputSlot:: + opaque descriptor for an output facility, ready to dispose frames + of data to be output. + + anchor:Pipe[] link:#Pipe[->]Pipe:: + Conceptual building block of the high-level model. It can be thought + off as simple linear processing chain. A stream can be 'sent to' a + pipe, in which case it will be mixed in at the input, and you can + 'plug' the output of a pipe to another destination. Further, effects + or processors can be attached to the pipe. Besides the global pipes + (busses) in each Timeline, each clip automatically creates N pipes + (one for each distinct content stream. Typically N=2, for video and + audio) + + anchor:Placement[] link:#Placement[->]Placement:: + A Placement represents a relation: it is always linked to a Subject + (this being a Media Object) and has the meaning to place this Subject + in some manner, either relatively to other Media Objects, by some + Constraint or simply absolute at (time, output). Placements are used + to stitch together the objects in the high-level-model. Placements + thus are organised hierarchically and need to be _resolved_ to obtain + a specific value (time point, output routing, layering, fade,...) + + anchor:PlayController[] link:#PlayController[->]PlayController:: + coordinating playback, cueing and rewinding of a playback position, + visible as 'Playhead' cursor in the GUI. When in play state, a + PlayController requests and directs a render process to deliver the + media data needed for playback. + + anchor:ProcessingGraph[] link:#ProcessingGraph[->]Processing Graph:: + Rendering is expressed as detailed network of Nodes, each defining a + processing step. anchor:Project[] link:#Project[->]Project:: the top-level context in which all edit work is done over an extended @@ -12,12 +114,80 @@ explanations, the long explanation is the topic of the document above..' comprised of the collection of all things the user is working on, it contains all informations, assets, state and objects to be edited. + anchor:RenderTask[] link:#RenderTask[->]RenderTask:: + basically a PlayController, but collecting output directly, without + moving a PlayheadCursor (maybe a progress indicator) and not operating + in a timed fashion, but freewheeling or in background mode + + anchor:RulesSystem[] link:#RulesSystem[->]Rules System:: + Translating the Timeline to the underlying Processing Graphs involves + some logic and knowledge about handling/converting data. This may be + configued with this Rules System. Typically Lumiera will provide sane + defaults for most purposes but may extended/refined for site specific + things. + + anchor:Sequence[] link:#Sequence[->]Sequence:: + A collection of *Media Objects* (clips, effects, transitions, labels, + automation) placed onto a tree of tracks. By means of this placement, + the objects could be anchored relative to each other, relative to + external objects, absolute in time. A sequence can connect to the + global pipes when used as top-level sequence within a timeline, or + alternatively it can act as a virtual-media when used within a + meta-clip (nested sequence). In the default configuration, a Sequence + contains just a single root track and sends directly to the master bus + of the timeline. + anchor:Session[] link:#Session[->]Session:: the current in-memory representation of the Project when opened within an instance of Lumiera. This is an implementation-internal term. For the GUI and the users POV we should always prefer the term "Project" for the general concept. + anchor:StreamType[] link:#StreamType[->]StreamType:: + Classification of a media stream. StreamType is a descriptor record. + While external media processing libraries usually do provide some kind + of classification already, within lumiera we rely on an uniform yet + abstract classification which is owned by the project and geared to + fit the internal needs, especially for the wiring and connecting. + A Lumiera stream type is comprised of the parts + - media kind (Video, Image, Audio, MIDI, Text,... ) + - prototype (open ended collection of semantical kinds of media, + examples being stereoscopic, periphonic, monaural, binaural, + film quality, TV, youtube). + - implementation type (e.g. 96kHz 24bit PCM, 2 channels muxed) + - intention tag (Source, Raw, Intermediary and Target) + + anchor:TimeAxis[] link:#TimeAxis[->]Time Axis:: + An entity defining the temporal properties of a timeline. A time axis + defines the time base, kind of timecode and absolute anchor point. + Besides, it manages a set of frame quantisation grids, corresponding + to the outputs configured for this timeline (through the global + busses). The GUI representation is a time ruler with configurable time + ticks showed on top of the timeline view + + anchor:Timeline[] link:#Timeline[->]Timeline:: + the top level element(s) within the Project. It is visible within a + 'timeline view' in the GUI and represents the effective (resulting) + arrangement of media objects, resolved to a finite time axis, to be + rendered for output or viewed in a Monitor (viewer window). + Timeline(s) are top-level and may not be further combined. A timeline + is comprised of: + * Time axis, defining the time base + * Play Controller (WIP: discussion if thats belongs to the timeline + and if we want a 1:N relation here). Note by Ichthyo: yes, our + current discussion showed us that a play controller rather gets + allocated to a timeline, but isn't contained therein. + * global pipes, i.e. global busses like in a mixing desk + * exactly one top level Sequence + + anchor:TimelineSegment[] link:#TimelineSegment[->]Timeline Segment:: + A range in the timeline which yields in one Processing graph, commonly + the range between cut points (which require a reconfiguration of the + graph). + +// Note by Ichthyo: "Extent" sounds somewhat cool, just it didn't occur to me as a term. +// We may well agree on it, if "extent" communicates the meaning better. Up to now, I called it "segment" + anchor:TimelineView[] link:#TimelineView[->]Timeline View:: A view in the GUI featuring a given Timeline. There might be multiple views of the same timeline, all sharing the same PlayController. A @@ -38,120 +208,6 @@ explanations, the long explanation is the topic of the document above..' patchbay, that is not the main purpose and they can do things beyond that.. - anchor:Timeline[] link:#Timeline[->]Timeline:: - the top level element(s) within the Project. It is visible within a - 'timeline view' in the GUI and represents the effective (resulting) - arrangement of media objects, resolved to a finite time axis, to be - rendered for output or viewed in a Monitor (viewer window). - Timeline(s) are top-level and may not be further combined. A timeline - is comprised of: - * Time axis, defining the time base - * Play Controller (WIP: discussion if thats belongs to the timeline - and if we want a 1:N relation here). Note by Ichthyo: yes, our - current discussion showed us that a play controller rather gets - allocated to a timeline, but isn't contained therein. - * global pipes, i.e. global busses like in a mixing desk - * exactly one top level Sequence - - anchor:TimeAxis[] link:#TimeAxis[->]Time Axis:: - An entity defining the temporal properties of a timeline. A time axis - defines the time base, kind of timecode and absolute anchor point. - Besides, it manages a set of frame quantisation grids, corresponding - to the outputs configured for this timeline (through the global - busses). The GUI representation is a time ruler with configurable time - ticks showed on top of the timeline view - - anchor:Busses[] link:#Busses[->]Busses:: - A list of 'global Pipes' representing the possible outputs (master - busses) similar to audio mixing desk. A bus defines the properties of - the rendered output (Framerate, Resolution, Colorformat and so on). - Busses are part of a Timeline. - - anchor:Sequence[] link:#Sequence[->]Sequence:: - A collection of *Media Objects* (clips, effects, transitions, labels, - automation) placed onto a tree of tracks. By means of this placement, - the objects could be anchored relative to each other, relative to - external objects, absolute in time. A sequence can connect to the - global pipes when used as top-level sequence within a timeline, or - alternatively it can act as a virtual-media when used within a - meta-clip (nested sequence). In the default configuration, a Sequence - contains just a single root track and sends directly to the master bus - of the timeline. - - anchor:Placement[] link:#Placement[->]Placement:: - A Placement represents a relation: it is always linked to a Subject - (this being a Media Object) and has the meaning to place this Subject - in some manner, either relatively to other Media Objects, by some - Constraint or simply absolute at (time, output). Placements are used - to stitch together the objects in the high-level-model. Placements - thus are organised hierarchically and need to be _resolved_ to obtain - a specific value (time point, output routing, layering, fade,...) - - anchor:Pipe[] link:#Pipe[->]Pipe:: - Conceptual building block of the high-level model. It can be thought - off as simple linear processing chain. A stream can be 'sent to' a - pipe, in which case it will be mixed in at the input, and you can - 'plug' the output of a pipe to another destination. Further, effects - or processors can be attached to the pipe. Besides the global pipes - (busses) in each Timeline, each clip automatically creates N pipes - (one for each distinct content stream. Typically N=2, for video and - audio) - - anchor:MediaStream[] link:#MediaStream[->]MediaStream:: - Media data is supposed to appear structured as stream(s) over time. - While there may be an inherent internal structuring, at a given - perspective any stream is a unit and homogeneous. In the context of - digital media data processing, streams are always quantized, which means - they appear as a temporal sequence of data chunks called frames. - - anchor:StreamType[] link:#StreamType[->]StreamType:: - Classification of a media stream. StreamType is a descriptor record. - While external media processing libraries usually do provide some kind - of classification already, within lumiera we rely on an uniform yet - abstract classification which is owned by the project and geared to - fit the internal needs, especially for the wiring and connecting. - A Lumiera stream type is comprised of the parts - - media kind (Video, Image, Audio, MIDI, Text,... ) - - prototype (open ended collection of semantical kinds of media, - examples being stereoscopic, periphonic, monaural, binaural, - film quality, TV, youtube). - - implementation type (e.g. 96kHz 24bit PCM, 2 channels muxed) - - intention tag (Source, Raw, Intermediary and Target) - - anchor:OutputDesignation[] link:#OutputDesignation[->]OutputDesignation:: - A specification denoting where to connect the output of a pipe. - It might either be given _absoulutely_, i.e as Pipe-ID, - or by an _relative_ or _indirect_ specification - - anchor:OutputMapping[] link:#OutputMapping[->]OutputMapping:: - translates one output designation into another one, e.g. when hooking - up a sequence as virtual clip within another sequence - - anchor:OutputSlot[] link:#OutputSlot[->]OutputSlot:: - opaque descriptor for an output facility, ready to dispose frames - of data to be output. - - anchor:OutputManager[] link:#OutputManager[->]OutputManager:: - manages all external outputs of the application and provides output - slots targetting these. - - anchor:PlayController[] link:#PlayController[->]PlayController:: - coordinating playback, cueing and rewinding of a playback position, - visible as 'Playhead' cursor in the GUI. When in play state, a - PlayController requests and directs a render process to deliver the - media data needed for playback. - - anchor:RenderTask[] link:#RenderTask[->]RenderTask:: - basically a PlayController, but collecting output directly, without - moving a PlayheadCursor (maybe a progress indicator) and not operating - in a timed fashion, but freewheeling or in background mode - - anchor:ControllerGui[] link:#ControllerGui[->]Controller Gui:: - This can be either a full Software implementation for a Transport - control (Widgets for Start/Stop/Rev/Ffw etc) or some Gui managing an - Input Device. They share some feature to attach them to controllable - gui-entities (Viewers, Timeline Views) - anchor:Viewer[] link:#Viewer[->]Viewer:: the display destination showing video frame and possibly some effect overlays (masking etc.). When attached to a timeline, a viewer @@ -164,59 +220,8 @@ explanations, the long explanation is the topic of the document above..' mere control for sending video to a dedicated monitor (separate X display or firewire) - anchor:HighLevelModel[] link:#HighLevelModel[->]High Level Model:: - All the session content to be edited and manipulated by the user - through the GUI. The high-level-model will be translated by the - Builder into the Low Level Model for rendering. - anchor:LowLevelModel[] link:#LowLevelModel[->]Low Level Model:: - The generated Processing Graph, to be ``performed'' within the engine - to yield rendered output - anchor:Builder[] link:#Builder[->]Builder:: - A kind of compiler which creates Low Level/Processing Graphs, by - traversing and evaluating the relevant parts of the high-level-model - and using the Rules System. - anchor:TimelineSegment[] link:#TimelineSegment[->]Timeline Segment:: - A range in the timeline which yields in one Processing graph, commonly - the range between cut points (which require a reconfiguration of the - graph). -// Note by Ichthyo: "Extent" sounds somewhat cool, just it didn't occur to me as a term. -// We may well agree on it, if "extent" communicates the meaning better. Up to now, I called it "segment" - anchor:AssetsView[] link:#AssetsView[->]Assets View:: - The windows showing and managing the available things to work with. - This are the ingested footage, already composed Clips, available - Sub-Projects, Effects, Transitions and internal artefacts. - - anchor:RulesSystem[] link:#RulesSystem[->]Rules System:: - Translating the Timeline to the underlying Processing Graphs involves - some logic and knowledge about handling/converting data. This may be - configued with this Rules System. Typically Lumiera will provide sane - defaults for most purposes but may extended/refined for site specific - things. - - anchor:ProcessingGraph[] link:#ProcessingGraph[->]Processing Graph:: - Rendering is expressed as detailed network of Nodes, each defining a - processing step. - - anchor:ConfigSystem_Preferences[] link:#ConfigSystem_Preferences[->]Config System/Preferences:: - TODO: agree on one term here - Provides defaults for all kinds of application configurations. These - include machine specific configurations for performance - characteristics, File and Plugins Paths and configuration data and so - on. Note that this only provides defaults for otherwise not yet set - data. Many settings will then be stored within the project and the - Config/Preferences becomes overridden by that. - - anchor:InputDevice[] link:#InputDevice[->]Input Device:: - some hardware controler, like a extra Keyboard, Midi Mixer, Jog, .. - TODO: decide if the main keyboard as special (global) state. - - anchor:Focus[] link:#Focus[->]Focus:: - TBD - - anchor:Cursor[] link:#Cursor[->]Cursor:: - playback- or edit position From 8d43c00712deab6ea7a782c86b7e0d0b8f70a488 Mon Sep 17 00:00:00 2001 From: Benny Lyons Date: Sun, 29 Jul 2012 17:24:43 +0100 Subject: [PATCH 392/496] English corrected --- doc/user/intro/Glossary.txt | 60 +++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/doc/user/intro/Glossary.txt b/doc/user/intro/Glossary.txt index aa9834231..403c8c309 100644 --- a/doc/user/intro/Glossary.txt +++ b/doc/user/intro/Glossary.txt @@ -2,17 +2,19 @@ Glossary ======== -'NOTE Draft, please help rephrase/review and sort this terms, shorten +'NOTE Draft, please help rephrase/review and shorten explanations, the long explanation is the topic of the document above..' anchor:AssetsView[] link:#AssetsView[->]Assets View:: - The windows showing and managing the available things to work with. - This are the ingested footage, already composed Clips, available - Sub-Projects, Effects, Transitions and internal artefacts. + Windows displaying and managing various things that can be edited, + worked on, etc are collectively known as the Assets View. + Ingested footage, clips that have already been composed, available + sub-projects, effects, transitions and internal artifacts are typical + examples. anchor:Builder[] link:#Builder[->]Builder:: - A kind of compiler which creates Low Level/Processing Graphs, by - traversing and evaluating the relevant parts of the high-level-model + This is a kind of compiler that creates low-level, or processing, graphs + by traversing and evaluating the relevant parts of the high-level-model and using the Rules System. anchor:Busses[] link:#Busses[->]Busses:: @@ -26,9 +28,9 @@ explanations, the long explanation is the topic of the document above..' Provides defaults for all kinds of application configurations. These include machine specific configurations for performance characteristics, File and Plugins Paths and configuration data and so - on. Note that this only provides defaults for otherwise not yet set - data. Many settings will then be stored within the project and the - Config/Preferences becomes overridden by that. + on. Note that this only provides defaults for data that has not already + been set. Many settings will then be stored within the project which + override Config/Preferences. anchor:ControllerGui[] link:#ControllerGui[->]Controller Gui:: This can be either a full Software implementation for a Transport @@ -37,7 +39,7 @@ explanations, the long explanation is the topic of the document above..' gui-entities (Viewers, Timeline Views) anchor:Cursor[] link:#Cursor[->]Cursor:: - playback- or edit position + Playback- or edit position anchor:Focus[] link:#Focus[->]Focus:: TBD @@ -48,8 +50,8 @@ explanations, the long explanation is the topic of the document above..' Builder into the Low Level Model for rendering. anchor:InputDevice[] link:#InputDevice[->]Input Device:: - some hardware controler, like a extra Keyboard, Midi Mixer, Jog, .. - TODO: decide if the main keyboard as special (global) state. + Some hardware controler, like an extra keyboard, midi mixer, Jog, .. + TODO: decide on whether the main keyboard is in a special (global) state. anchor:LowLevelModel[] link:#LowLevelModel[->]Low Level Model:: The generated Processing Graph, to be ``performed'' within the engine @@ -64,27 +66,27 @@ explanations, the long explanation is the topic of the document above..' anchor:OutputDesignation[] link:#OutputDesignation[->]OutputDesignation:: A specification denoting where to connect the output of a pipe. - It might either be given _absoulutely_, i.e as Pipe-ID, - or by an _relative_ or _indirect_ specification + It might either be given _absoulutely_, i.e as a Pipe-ID, + or by a _relative_ or an _indirect_ specification anchor:OutputManager[] link:#OutputManager[->]OutputManager:: - manages all external outputs of the application and provides output + Manages all external outputs of the application and provides output slots targetting these. anchor:OutputMapping[] link:#OutputMapping[->]OutputMapping:: - translates one output designation into another one, e.g. when hooking - up a sequence as virtual clip within another sequence + Diverts one output designation into another designation, e.g. when hooking + up a sequence as a virtual clip within another sequence. anchor:OutputSlot[] link:#OutputSlot[->]OutputSlot:: - opaque descriptor for an output facility, ready to dispose frames + Opaque descriptor for an output facility, ready to dispose frames of data to be output. anchor:Pipe[] link:#Pipe[->]Pipe:: Conceptual building block of the high-level model. It can be thought - off as simple linear processing chain. A stream can be 'sent to' a + of as simple linear processing chain. A stream can be 'sent to' a pipe, in which case it will be mixed in at the input, and you can - 'plug' the output of a pipe to another destination. Further, effects - or processors can be attached to the pipe. Besides the global pipes + 'plug' the output of a pipe to another destination. Furthermore, effects + or processors can be attached to the pipe. In addition to global pipes (busses) in each Timeline, each clip automatically creates N pipes (one for each distinct content stream. Typically N=2, for video and audio) @@ -130,12 +132,11 @@ explanations, the long explanation is the topic of the document above..' A collection of *Media Objects* (clips, effects, transitions, labels, automation) placed onto a tree of tracks. By means of this placement, the objects could be anchored relative to each other, relative to - external objects, absolute in time. A sequence can connect to the - global pipes when used as top-level sequence within a timeline, or + external objects, absolute in time. A sequence can connect to + global pipes when used as a top-level sequence within a timeline, or alternatively it can act as a virtual-media when used within a - meta-clip (nested sequence). In the default configuration, a Sequence - contains just a single root track and sends directly to the master bus - of the timeline. + meta-clip (nested sequence). A Sequence by default contains just a + single root track and directly sends to the master bus of the Timeline. anchor:Session[] link:#Session[->]Session:: the current in-memory representation of the Project when opened within @@ -209,7 +210,7 @@ explanations, the long explanation is the topic of the document above..' that.. anchor:Viewer[] link:#Viewer[->]Viewer:: - the display destination showing video frame and possibly some effect + The display destination showing video frame and possibly some effect overlays (masking etc.). When attached to a timeline, a viewer reflects the state of the timeline's associated PlayController, and it attaches to the timeline's global pipes (stream-type match or @@ -220,8 +221,3 @@ explanations, the long explanation is the topic of the document above..' mere control for sending video to a dedicated monitor (separate X display or firewire) - - - - - From 8e8c515272ed2e24395c232c17e5db0001d154d8 Mon Sep 17 00:00:00 2001 From: Benny Lyons Date: Fri, 27 Jul 2012 20:49:14 +0100 Subject: [PATCH 393/496] Section: Plugins/Interfaces; introductory text. First piece of introductory text for the Plugins/Interfaces section. Only a somewhat long introduction of what plugins are. TODO: discussed the 'what' of plugins, and possibly a little of the 'why' of plugins, still require the 'how'. --- doc/user/intro/intro.txt | 55 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/doc/user/intro/intro.txt b/doc/user/intro/intro.txt index dcbf377ab..1e846a3f1 100644 --- a/doc/user/intro/intro.txt +++ b/doc/user/intro/intro.txt @@ -355,11 +355,58 @@ Configuration Plugins/Interfaces ~~~~~~~~~~~~~~~~~~ -[red]#to be written# -// explain whats it is -// portability -// versioning + +What are Plugins? +^^^^^^^^^^^^^^^^^ + +A Plug-in is a kind of generalisation of a library. + +All applications use, to varying degrees of intensity, libraries. A programmer +will not reinvent the wheel each time he sits down to programme an +application. A programmer will typically borrow and use features and +functionality from other programmers---or even borrow from himself, stuff +written long ago in the past. Such features are collected together in +libraries. + +A library is used in an application by _linking_ the library into the +application. (There are other things to be done, but we'll call these 'details', +which wont concern us here.) There are different ways to _link_ a library +into an application: statically linking and dynamically linking. + +_Staticall Linking_ is done while the application is being built, or +compiled. It is performed by the linker. The linker can perform some checks +(mostly checks on syntax) and warn the user that some particular feature is +being used incorrectly. The user can then correct the offending code, and +recompile. +There are a number of disadvantages associated with static linking. Features and +libraries are being constantly improved. If the application wants to use new +features, it will have to be recompiled with the new library which provides the +new features. + +_Dynamic Linking_ helps rectify the necessity of having to recompile. If a +new, improved library becomes available, all the user has to do is to install +the new library onto the operating system, restart the application and the new +features can be used by the application. The features provided by a dynamic +library are loaded when the application starts to run. + +However both methods exibit a number of shortcomings. Wouldn't it be better if +all features could be loaded only when needed? If features could be loaded only +when needed, then they could also be unloaded when not required, thus saving +memory and possibly increasing performance. This scheme of making features +available to an application is known as run-time linking, aka plug-ins. +Plug-ins offer other benifits: the application can continue to use both the old +features and the new features together, side-by-side, by using the version +number associated with the plug-in. This saves the application from considerable +headaches associated with other linking methods, havocked library version +incompatibility. + +Most modern applications use plug-ins, some are heavily dependent on plug-ins +and only provide limited functionality without any plug-ins. +Lumiera will not reinvent the wheel. One major goal is to provide considerable +functionality via well-designed, external code supplied to Lumiera by plug-ins. + + From 6eee5287d86a2d97abdcc51f88a82f7999097cc9 Mon Sep 17 00:00:00 2001 From: Benny Lyons Date: Sun, 29 Jul 2012 18:02:14 +0100 Subject: [PATCH 394/496] Glossary entries reworked and slightly expanded - ProcessingGraph: text slightly expanded - Project: corrcted and text slightly modified - RenderTask: corrected - Viewer: corrected and somewhat modified - AssetsView corrected. - InputDevice --- doc/user/intro/Glossary.txt | 42 +++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/doc/user/intro/Glossary.txt b/doc/user/intro/Glossary.txt index 403c8c309..cd2ba0636 100644 --- a/doc/user/intro/Glossary.txt +++ b/doc/user/intro/Glossary.txt @@ -7,10 +7,9 @@ explanations, the long explanation is the topic of the document above..' anchor:AssetsView[] link:#AssetsView[->]Assets View:: Windows displaying and managing various things that can be edited, - worked on, etc are collectively known as the Assets View. - Ingested footage, clips that have already been composed, available - sub-projects, effects, transitions and internal artifacts are typical - examples. + worked on, etc are collectively known as the Assets View. + AssetsView consists of ingested footage, clips that have already been composed, available + sub-projects, effects, transitions and internal artifacts. anchor:Builder[] link:#Builder[->]Builder:: This is a kind of compiler that creates low-level, or processing, graphs @@ -51,7 +50,9 @@ explanations, the long explanation is the topic of the document above..' anchor:InputDevice[] link:#InputDevice[->]Input Device:: Some hardware controler, like an extra keyboard, midi mixer, Jog, .. - TODO: decide on whether the main keyboard is in a special (global) state. + TODO: we still need to decide on whether we can treat the main keyboard + as special global state or whether we can handle the keyboard as a generic + input devide. anchor:LowLevelModel[] link:#LowLevelModel[->]Low Level Model:: The generated Processing Graph, to be ``performed'' within the engine @@ -107,18 +108,18 @@ explanations, the long explanation is the topic of the document above..' media data needed for playback. anchor:ProcessingGraph[] link:#ProcessingGraph[->]Processing Graph:: - Rendering is expressed as detailed network of Nodes, each defining a - processing step. + Rendering is expressed as a detailed network of nodes. The edges can be + envisaged as data flow, while the nodes represent data processing. anchor:Project[] link:#Project[->]Project:: - the top-level context in which all edit work is done over an extended - period of time. The Project can be saved and re-opened. It is - comprised of the collection of all things the user is working on, it - contains all informations, assets, state and objects to be edited. + The top-level context in which all editing is done over an extended + period of time. A Project can be saved and re-opened. It consists of + various things a user is working on: user-information, assets, state and + objects to be edited. anchor:RenderTask[] link:#RenderTask[->]RenderTask:: - basically a PlayController, but collecting output directly, without - moving a PlayheadCursor (maybe a progress indicator) and not operating + This is basically a PlayController, but directly collects output without + moving a PlayheadCursor (could be a progress indicator) and not operating in a timed fashion, but freewheeling or in background mode anchor:RulesSystem[] link:#RulesSystem[->]Rules System:: @@ -214,10 +215,11 @@ explanations, the long explanation is the topic of the document above..' overlays (masking etc.). When attached to a timeline, a viewer reflects the state of the timeline's associated PlayController, and it attaches to the timeline's global pipes (stream-type match or - explicitly), showing video as monitor image and sending audio to the - system audio port. Possible extensions are for a viewer to be able to - attach to probe points within the render network, to show a second - stream as (partial) overlay for comparison, or to be collapsed to a - mere control for sending video to a dedicated monitor (separate X - display or firewire) - + explicitly), showing video as a monitor image and sending audio to the + system audio port. A number of supplimentary features are possible: the + viewer could support the ability to attach to view points within the + render network; the capability to display a second stream as an overlay + (partially) which would enable user comparison or it might even be + possible to allow the Viewer to be collapsed to a control, thus, + allowing a video to be sent to a dedicated monitor, e.g., to a separate + X/-display or to a firewire. From a8e4b57548920fd93f4ddf4a4ce88616f716f5e1 Mon Sep 17 00:00:00 2001 From: Benny Lyons Date: Tue, 31 Jul 2012 23:16:41 +0100 Subject: [PATCH 395/496] Comments and Doxygen improved --- src/common/interface.h | 103 +++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 45 deletions(-) diff --git a/src/common/interface.h b/src/common/interface.h index 3a8e2fb5c..82dabddf3 100644 --- a/src/common/interface.h +++ b/src/common/interface.h @@ -28,69 +28,82 @@ /* TODO better doxygen formating */ /** - * @file - * Lumiera interface macros and structures. + * @file interface.h + * @brief Lumiera interface macros and structures. * - * Instead just simple function/library bindings, Lumiera uses a system of - * versioned interfaces. This interfaces are C-binding compatible and thus - * can be used by any language which can bind to C. This interfaces are versioned - * to provide exceptional forward and backward compatibility for both, source and - * binary deployment of modules. This interfaces play a central role on the Lumiera - * architecture, other facilities, like serializing sessions and distributed computing + * Lumiera uses a system of versioned interfaces instead of just employing a + * simple library (containg the functions and data) and header file strategy. + * The interfaces defined here are C compatible and, thus, can be used by any + * language able to bind to C. The interfaces are versioned to provide forward + * and backward compatibility for both source and binary deployment of + * modules. The interfaces play a central role in the Lumiera architecture. + * Other facilities, such as serializing sessions and distributed computing * will use them extensively. * * Overview * - * Interfaces are used for two purposes in Lumiera: - * 1. The core uses them internally and exports its functionality though them. - * 2. Plugins (effects,...) extend Lumiera by providing interfaces + * Interfaces are used for 2 reasons in Lumiera: + * -# The Lumiera core system uses them internally and exports its functionality though them. + * -# Plugins (effects, ...) extend Lumiera by providing interfaces * - * We define some macros here which ease the declaration and definition of interfaces. + * To make an interface available to code so that the code can use the + * interface, the interface needs to be declared and then defined. We provide a + * number of macros here which ease this process. * - * Declaration of an interface happens in a header and has the form: - * LUMIERA_INTERFACE_DECLARE(name, version, - * LUMIERA_INTERFACE_SLOT(ret, name, params), - * ... - * ) - * Any code which want to use this interface must then include its declaration. - * - * Basic definition of an interface is done by mapping functions to slots or giving - * inline definitions for slot functions: - * LUMIERA_INTERFACE_INSTANCE(iname, version, name, descriptor, acquire, release, - * LUMIERA_INTERFACE_MAP (slot, luid, function), - * LUMIERA_INTERFACE_INLINE (slot, luid, ret, params, {body}), + * The interface is declared by placing the following macro in a + * header file: + * \code + * LUMIERA_INTERFACE_DECLARE(name, version, + * LUMIERA_INTERFACE_SLOT(ret, name, params), * ... * ) + * + * \endcode + * Any code that wants to use this interface must then include the header file. * - * There are 2 ways to define collections of interfaces: + * The interface is defined by mapping interface functions to slots, or + * providing inline definitions for slot functions. Defining the interface has + * the following form: + * \code + * LUMIERA_INTERFACE_INSTANCE(iname, version, name, descriptor, acquire, release, + * LUMIERA_INTERFACE_MAP (slot, luid, function), + * LUMIERA_INTERFACE_INLINE (slot, luid, ret, params, {body}), + * ... + * ) + * \endcode + * + * A collection of interfaces can be defined in 2 different ways depending on + * where whether the interface is exported by the core, or by a plugin: + * \code * LUMIERA_EXPORT(queryfunc, * LUMIERA_INTERFACE_DEFINE(...), * ... - * ) - * to export interfaces from the core. - * - * LUMIERA_PLUGIN(descriptor, acquire, release, luid, - * LUMIERA_INTERFACE_DEFINE(...), - * ... - * ) - * is used to export interfaces from a plugin. + * ) // Exporting from the core + * + * LUMIERA_PLUGIN(descriptor, acquire, release, luid, + * LUMIERA_INTERFACE_DEFINE(...), + * ... + * ) // Exporting from an interface + * \endcode * * Naming and Versioning + * * Interfaces have unique names and a major and minor version. The name and the major version - * is used to construct a C identifier for the interface, the minor version is implicit defined - * by the number of functions a interface. Interface instances are not versioned by the - * interface system, versioning these shall be defined somewhere else. + * is used to construct a C identifier for the interface, the minor version is implicitly defined + * by the number of functions in the interface. Interface instances are not versioned by the + * interface system, versioning these wii be defined somewhere else. * * Slot names are normal C identifiers, how these shall be versioned has to be defined somewhere - * else and is not subject of the interface system. Each function can has its own unique uuid. + * else and is not the subject of the interface system. Each function can have its own unique uuid. */ /* - Interface declaration macros + Macros to Declare an Interface */ /** + * * Construct a type identifier for an interface * @param name name of the interface * @param version major version of this interface @@ -107,7 +120,7 @@ #define LUMIERA_INTERFACE_DNAME(iname, version, dname) PPMPL_CAT (LUMIERA_INTERFACE_INAME (iname, version), _##dname) /** - * Construct a definition string r for an interface + * Construct a definition string for an interface * @param iname name of the interface * @param version major version of the interface * @param dname name for the instance @@ -145,14 +158,14 @@ /** * Declare an interface. * @param name name of the interface - * @param version major version of this interface declaration. 0 denotes a experimental interface, - * otherwise this shall be counting from 1 upwards for each new (incompatible) change of an interface. + * @param version major version of this interface declaration. 0 denotes an experimental interface, + * otherwise this will begin at 1 onwards for each new (incompatible) change to an interface. * The older interface declarations may still be maintained in parallel (backwards compatibility!). * @param ... Slot declarations for the functions provided by this interface @see LUMIERA_INTERFACE_SLOT * The number of Slots in an interface defines its 'minor' version. * New slots must be added at the end. The prototype and order of existing slots must not be changed. * Slots may be renamed, for example a slot 'foo' can be renamed to 'foo_old' when a new 'foo' slot is - * added. Binary modules will then still use the 'foo_old' slot which was the 'foo' slot at their + * added. Binary modules will then still use the 'foo_old' slot which was the 'foo' slot at the * compile time while compiling modules from source will use the new 'foo' slot. This may be * intentionally used to break compilation and force the update of modules to a new api. */ @@ -164,7 +177,7 @@ LUMIERA_INTERFACE_TYPE(name, version) \ } /** - * Declare function slot inside an interface. + * Declare a function slot inside an interface. * @param ret return type of the function * @param name name of this slot * @param params parentized list of parameters for the function @@ -175,11 +188,11 @@ LUMIERA_INTERFACE_TYPE(name, version) \ /* - Interface definition macros + Macros to Define an Interface */ /** - * Define a interface instance. + * Define an interface instance. * @param iname name of the interface to instance * @param version major version of the interface to instance * @param name name of the instance From 39386cb31f9ca3a1b7268f5821ca3b6b878e2467 Mon Sep 17 00:00:00 2001 From: Lumiera Project Date: Sat, 20 Aug 2011 19:31:26 +0100 Subject: [PATCH 396/496] fix a grammar error --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 95f86c7c3..7f7519ea9 100644 --- a/README +++ b/README @@ -42,7 +42,7 @@ The Application can be installed and started to bring up an GTK GUI outline, but the GUI is very preliminary and not connected to core functionality. The video processing pipeline is still not complete. -See the http://issues.lumiera.org/roadmap[Project roadmap] +See http://issues.lumiera.org/roadmap[Project roadmap] Build Requirements From 66cd1f1835d5f17d735786393b441b1bd1e0c580 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Sat, 25 Aug 2012 13:23:41 +0200 Subject: [PATCH 397/496] Add 'none' target to scons 'scons none' will not build anythinf but runs the configuration checks to determine if the system and libraries are sane to build Lumiera, this will be used in builddrone later. --- SConstruct | 1 + 1 file changed, 1 insertion(+) diff --git a/SConstruct b/SConstruct index d2468b374..8fe12e998 100644 --- a/SConstruct +++ b/SConstruct @@ -77,6 +77,7 @@ env.Default('build') env.Alias ('all', build + testsuite + doxydoc) env.Alias ('doc', doxydoc) +env.Alias ('none') env.Alias('install', gui) env.Alias('install', '$DESTDIR') From 7d964825d34aec6d2ab425aea1e616c164d418c8 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 30 Aug 2012 20:48:26 +0200 Subject: [PATCH 398/496] internal RFC reorganization * rfcs are now all stored in the rfc folder (formerly hold the final rfcs) * a new rfc_final folder is created * the state folders (rfc_final|pending|parked|dropped) now contain symlinks back to the rfc pool --- admin/rfc.sh | 48 +++++++++++++++++++++++------------ doc/devel/rfc/DIR_INFO | 4 ++- doc/devel/rfc/index.txt | 9 +++---- doc/devel/rfc_final/DIR_INFO | 1 + doc/devel/rfc_final/index.txt | 11 ++++++++ doc/technical/infra/rfcsh.txt | 6 ----- 6 files changed, 51 insertions(+), 28 deletions(-) create mode 100644 doc/devel/rfc_final/DIR_INFO create mode 100644 doc/devel/rfc_final/index.txt diff --git a/admin/rfc.sh b/admin/rfc.sh index c464be687..5e76eaf28 100755 --- a/admin/rfc.sh +++ b/admin/rfc.sh @@ -2,6 +2,14 @@ shopt -s extglob +# +# RFC's are created from ./doc/devel/template/new_rfc.sh and stored in +# ./doc/devel/rfc/ . There are directories for the various states +# ./doc/devel/rfc_final, ./doc/devel/rfc_pending, ./doc/devel/rfc_parked, +# ./doc/devel/rfc_dropped . Which contain symlinks back to ../rfc/ +# + + function usage() { grep -v '^// ' <<"EOF" | less -F @@ -199,7 +207,7 @@ function find_rfc() local globstate=$(shopt -p nocasematch) shopt -s nocasematch - for file in $(find ./doc/devel/rfc* -name '*.txt'); + for file in $(find ./doc/devel/rfc/ -name '*.txt'); do local name="/${file##*/}" if [[ "$name" =~ $match ]]; then @@ -229,28 +237,37 @@ function process_file() local file="$1" local path="${1%/*}" local basename="${1##*/}" - local destpath="$path" + local linkdest="$path" local state=$(grep '^\*State\* *' "$file") case "$state" in *Final*) - destpath="./doc/devel/rfc" + linkdest="./doc/devel/rfc_final" ;; *Idea*|*Draft*) - destpath="./doc/devel/rfc_pending" + linkdest="./doc/devel/rfc_pending" ;; *Parked*) - destpath="./doc/devel/rfc_parked" + linkdest="./doc/devel/rfc_parked" ;; *Dropped*) - destpath="./doc/devel/rfc_dropped" + linkdest="./doc/devel/rfc_dropped" ;; + *) + echo "Unknown State: '$state'" >&2 + exit 1 esac - if [[ "$path" != "$destpath" ]]; then - git mv "$file" "$destpath" + local oldpath=./doc/devel/rfc_*/$basename + + if [[ "$oldpath" != './doc/devel/rfc_*/'"$basename" ]]; then + if [[ "$oldpath" != "$linkdest/$basename" ]]; then + git mv "$oldpath" "$linkdest/$basename" + fi + elif [[ ! -s "$linkdest/$basename" ]]; then + ln -s "../rfc/$basename" "$linkdest/" + git add "$linkdest/$basename" fi - git add "$destpath/$basename" } @@ -349,7 +366,7 @@ shift case "$command" in process) # for all rfc's - for file in $(find ./doc/devel/rfc* -name '*.txt'); + for file in $(find ./doc/devel/rfc -name '*.txt'); do echo "process $file" process_file "$file" @@ -373,14 +390,13 @@ show|less|more) create) TITLE="$@" name=$(camel_case "$TITLE") - if [[ -f "./doc/devel/rfc/${name}.txt" || - -f "./doc/devel/rfc_pending/${name}.txt" || - -f "./doc/devel/rfc_dropped/${name}.txt" ]]; then + if [[ -f "./doc/devel/rfc/${name}.txt" ]]; then echo "$name.txt exists already" else - source ./doc/devel/template/new_rfc.sh >"./doc/devel/rfc_pending/${name}.txt" - edit "./doc/devel/rfc_pending/${name}.txt" 2 abstract - process_file "./doc/devel/rfc_pending/${name}.txt" + source ./doc/devel/template/new_rfc.sh >"./doc/devel/rfc/${name}.txt" + edit "./doc/devel/rfc/${name}.txt" 2 abstract + git add "./doc/devel/rfc/${name}.txt" + process_file "./doc/devel/rfc/${name}.txt" fi ;; edit) diff --git a/doc/devel/rfc/DIR_INFO b/doc/devel/rfc/DIR_INFO index 393ae51b8..205119027 100644 --- a/doc/devel/rfc/DIR_INFO +++ b/doc/devel/rfc/DIR_INFO @@ -1 +1,3 @@ -accepted design proposals +RFC pool + +all other rfc dirs (sorted by state) link back to here diff --git a/doc/devel/rfc/index.txt b/doc/devel/rfc/index.txt index c9e8a4515..23814933a 100644 --- a/doc/devel/rfc/index.txt +++ b/doc/devel/rfc/index.txt @@ -1,11 +1,10 @@ -Accepted Design Proposals -========================= +Design Proposal Pool +==================== -//Menu: label accepted +//Menu: off //Menu: sort children -> read link:../rfc.html[more about Lumiera RfC] and the Design Process -The RfC entries listed here where discussed, maybe modified and finally agreed upon -during some developers meeting in the past. So they represent design decisions taken. +This is the poll where all Design Proposals are stored. diff --git a/doc/devel/rfc_final/DIR_INFO b/doc/devel/rfc_final/DIR_INFO new file mode 100644 index 000000000..393ae51b8 --- /dev/null +++ b/doc/devel/rfc_final/DIR_INFO @@ -0,0 +1 @@ +accepted design proposals diff --git a/doc/devel/rfc_final/index.txt b/doc/devel/rfc_final/index.txt new file mode 100644 index 000000000..c9e8a4515 --- /dev/null +++ b/doc/devel/rfc_final/index.txt @@ -0,0 +1,11 @@ +Accepted Design Proposals +========================= + +//Menu: label accepted +//Menu: sort children + +-> read link:../rfc.html[more about Lumiera RfC] and the Design Process + +The RfC entries listed here where discussed, maybe modified and finally agreed upon +during some developers meeting in the past. So they represent design decisions taken. + diff --git a/doc/technical/infra/rfcsh.txt b/doc/technical/infra/rfcsh.txt index 07790f7c2..a75fb3640 100644 --- a/doc/technical/infra/rfcsh.txt +++ b/doc/technical/infra/rfcsh.txt @@ -1,12 +1,6 @@ Lumiera RFC maintenance script ============================== -// Note: the source of this documentation is maintained -// directly admin/rfc.sh in its usage() function -// edit it only there and then regenerate -// doc/devel/technical/infra/ with: -// ./admin/rfc.sh help >doc/technical/infra/rfcsh.txt - Usage ----- From aa6048606661a99eabb464d3af50d7078aab90b7 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 30 Aug 2012 20:49:16 +0200 Subject: [PATCH 399/496] apply the new RFC structure to existing RFC's --- doc/devel/rfc/ApplicationInstall.txt | 225 +++++++++ doc/devel/rfc/ApplicationStructure.txt | 200 ++++++++ doc/devel/rfc/ClipCatalogingSystem.txt | 128 ++++++ doc/devel/rfc/DelectusShotEvaluator.txt | 397 ++++++++++++++++ doc/devel/rfc/DesignParamAutomation.txt | 84 ++++ doc/devel/rfc/DesignRenderNodesInterface.txt | 128 ++++++ .../rfc/DeveloperDocumentationStructure.txt | 136 ++++++ doc/devel/rfc/EngineInterfaceOverview.txt | 271 +++++++++++ doc/devel/rfc/EngineInterfaceSpec.txt | 234 ++++++++++ .../rfc/FeatureBundle_PluggableModules.txt | 195 ++++++++ doc/devel/rfc/GitSubmoduleTransistion.txt | 87 ++++ doc/devel/rfc/MistakestoAvoid.txt | 427 +++++++++++++++++ doc/devel/rfc/NormalizedDeviceCoordinates.txt | 112 +++++ doc/devel/rfc/OfficialAssemblyLanguage.txt | 161 +++++++ doc/devel/rfc/RefactorLiblumieraOut.txt | 240 ++++++++++ doc/devel/rfc/RenderOptimizer.txt | 100 ++++ doc/devel/rfc/ResourceManagementBudgeting.txt | 121 +++++ doc/devel/rfc/ResourceManagementProfiling.txt | 182 ++++++++ doc/devel/rfc/SkillsCollection.txt | 80 ++++ doc/devel/rfc/StreamTypeSystem.txt | 268 +++++++++++ doc/devel/rfc/TagCloudsOnResources.txt | 81 ++++ doc/devel/rfc/TodoLists.txt | 49 ++ doc/devel/rfc/UnitTests_Python.txt | 82 ++++ doc/devel/rfc/UseCases.txt | 310 +++++++++++++ doc/devel/rfc/WebsiteNavigation.txt | 160 +++++++ .../rfc_dropped/ApplicationStructure.txt | 201 +------- doc/devel/rfc_dropped/MistakestoAvoid.txt | 428 +----------------- .../rfc_dropped/OfficialAssemblyLanguage.txt | 162 +------ .../rfc_dropped/TagCloudsOnResources.txt | 82 +--- doc/devel/rfc_dropped/UnitTests_Python.txt | 83 +--- .../rfc_final/AllPluginInterfacesAreC.txt | 1 + doc/devel/rfc_final/ArchitectureOverview.txt | 1 + doc/devel/rfc_final/CCodingStyleGuide.txt | 1 + doc/devel/rfc_final/CodingStyle.txt | 1 + doc/devel/rfc_final/DataBackend.txt | 1 + doc/devel/rfc_final/DevelopmentFramework.txt | 1 + .../DistributedDevelopmentFramework.txt | 1 + doc/devel/rfc_final/EDLsAreMetaClips.txt | 1 + doc/devel/rfc_final/GloballInitialization.txt | 1 + doc/devel/rfc_final/HowToProceed.txt | 1 + doc/devel/rfc_final/InterfaceNamespaces.txt | 1 + doc/devel/rfc_final/LumieraDesignProcess.txt | 1 + .../rfc_final/LumieraForwardIterator.txt | 1 + .../MakeSconsTheOfficialBuildSystem.txt | 1 + doc/devel/rfc_final/Manifest.txt | 1 + doc/devel/rfc_final/MarbleMode.txt | 1 + doc/devel/rfc_final/MasterRepositorySetup.txt | 1 + .../MonthlyDeveloperMeetingOnIRC.txt | 1 + doc/devel/rfc_final/NoBugFlags.txt | 1 + doc/devel/rfc_final/ProcBuilder.txt | 1 + doc/devel/rfc_final/ProcHighLevelModel.txt | 1 + doc/devel/rfc_final/ProcPlacementMetaphor.txt | 1 + doc/devel/rfc_final/RepositorySetup.txt | 1 + doc/devel/rfc_final/Roadmap-first.txt | 1 + doc/devel/rfc_final/ScriptingLanguage.txt | 1 + ...eadsSignalsAndImportantManagementTasks.txt | 1 + doc/devel/rfc_final/TimeHandling.txt | 1 + .../rfc_final/TimelineSequenceOutput.txt | 1 + doc/devel/rfc_final/VersionNumberScheme.txt | 1 + doc/devel/rfc_parked/ClipCatalogingSystem.txt | 129 +----- .../rfc_parked/DelectusShotEvaluator.txt | 398 +--------------- .../rfc_parked/DesignRenderNodesInterface.txt | 129 +----- .../FeatureBundle_PluggableModules.txt | 196 +------- .../rfc_parked/GitSubmoduleTransistion.txt | 88 +--- .../NormalizedDeviceCoordinates.txt | 113 +---- doc/devel/rfc_parked/RenderOptimizer.txt | 101 +---- doc/devel/rfc_parked/SkillsCollection.txt | 81 +--- doc/devel/rfc_parked/TodoLists.txt | 50 +- doc/devel/rfc_parked/UseCases.txt | 311 +------------ doc/devel/rfc_pending/ApplicationInstall.txt | 226 +-------- .../rfc_pending/DesignParamAutomation.txt | 85 +--- .../DeveloperDocumentationStructure.txt | 137 +----- .../rfc_pending/EngineInterfaceOverview.txt | 272 +---------- doc/devel/rfc_pending/EngineInterfaceSpec.txt | 235 +--------- .../rfc_pending/RefactorLiblumieraOut.txt | 241 +--------- .../ResourceManagementBudgeting.txt | 122 +---- .../ResourceManagementProfiling.txt | 183 +------- doc/devel/rfc_pending/StreamTypeSystem.txt | 269 +---------- doc/devel/rfc_pending/WebsiteNavigation.txt | 161 +------ 79 files changed, 4512 insertions(+), 4458 deletions(-) create mode 100644 doc/devel/rfc/ApplicationInstall.txt create mode 100644 doc/devel/rfc/ApplicationStructure.txt create mode 100644 doc/devel/rfc/ClipCatalogingSystem.txt create mode 100644 doc/devel/rfc/DelectusShotEvaluator.txt create mode 100644 doc/devel/rfc/DesignParamAutomation.txt create mode 100644 doc/devel/rfc/DesignRenderNodesInterface.txt create mode 100644 doc/devel/rfc/DeveloperDocumentationStructure.txt create mode 100644 doc/devel/rfc/EngineInterfaceOverview.txt create mode 100644 doc/devel/rfc/EngineInterfaceSpec.txt create mode 100644 doc/devel/rfc/FeatureBundle_PluggableModules.txt create mode 100644 doc/devel/rfc/GitSubmoduleTransistion.txt create mode 100644 doc/devel/rfc/MistakestoAvoid.txt create mode 100644 doc/devel/rfc/NormalizedDeviceCoordinates.txt create mode 100644 doc/devel/rfc/OfficialAssemblyLanguage.txt create mode 100644 doc/devel/rfc/RefactorLiblumieraOut.txt create mode 100644 doc/devel/rfc/RenderOptimizer.txt create mode 100644 doc/devel/rfc/ResourceManagementBudgeting.txt create mode 100644 doc/devel/rfc/ResourceManagementProfiling.txt create mode 100644 doc/devel/rfc/SkillsCollection.txt create mode 100644 doc/devel/rfc/StreamTypeSystem.txt create mode 100644 doc/devel/rfc/TagCloudsOnResources.txt create mode 100644 doc/devel/rfc/TodoLists.txt create mode 100644 doc/devel/rfc/UnitTests_Python.txt create mode 100644 doc/devel/rfc/UseCases.txt create mode 100644 doc/devel/rfc/WebsiteNavigation.txt mode change 100644 => 120000 doc/devel/rfc_dropped/ApplicationStructure.txt mode change 100644 => 120000 doc/devel/rfc_dropped/MistakestoAvoid.txt mode change 100644 => 120000 doc/devel/rfc_dropped/OfficialAssemblyLanguage.txt mode change 100644 => 120000 doc/devel/rfc_dropped/TagCloudsOnResources.txt mode change 100644 => 120000 doc/devel/rfc_dropped/UnitTests_Python.txt create mode 120000 doc/devel/rfc_final/AllPluginInterfacesAreC.txt create mode 120000 doc/devel/rfc_final/ArchitectureOverview.txt create mode 120000 doc/devel/rfc_final/CCodingStyleGuide.txt create mode 120000 doc/devel/rfc_final/CodingStyle.txt create mode 120000 doc/devel/rfc_final/DataBackend.txt create mode 120000 doc/devel/rfc_final/DevelopmentFramework.txt create mode 120000 doc/devel/rfc_final/DistributedDevelopmentFramework.txt create mode 120000 doc/devel/rfc_final/EDLsAreMetaClips.txt create mode 120000 doc/devel/rfc_final/GloballInitialization.txt create mode 120000 doc/devel/rfc_final/HowToProceed.txt create mode 120000 doc/devel/rfc_final/InterfaceNamespaces.txt create mode 120000 doc/devel/rfc_final/LumieraDesignProcess.txt create mode 120000 doc/devel/rfc_final/LumieraForwardIterator.txt create mode 120000 doc/devel/rfc_final/MakeSconsTheOfficialBuildSystem.txt create mode 120000 doc/devel/rfc_final/Manifest.txt create mode 120000 doc/devel/rfc_final/MarbleMode.txt create mode 120000 doc/devel/rfc_final/MasterRepositorySetup.txt create mode 120000 doc/devel/rfc_final/MonthlyDeveloperMeetingOnIRC.txt create mode 120000 doc/devel/rfc_final/NoBugFlags.txt create mode 120000 doc/devel/rfc_final/ProcBuilder.txt create mode 120000 doc/devel/rfc_final/ProcHighLevelModel.txt create mode 120000 doc/devel/rfc_final/ProcPlacementMetaphor.txt create mode 120000 doc/devel/rfc_final/RepositorySetup.txt create mode 120000 doc/devel/rfc_final/Roadmap-first.txt create mode 120000 doc/devel/rfc_final/ScriptingLanguage.txt create mode 120000 doc/devel/rfc_final/ThreadsSignalsAndImportantManagementTasks.txt create mode 120000 doc/devel/rfc_final/TimeHandling.txt create mode 120000 doc/devel/rfc_final/TimelineSequenceOutput.txt create mode 120000 doc/devel/rfc_final/VersionNumberScheme.txt mode change 100644 => 120000 doc/devel/rfc_parked/ClipCatalogingSystem.txt mode change 100644 => 120000 doc/devel/rfc_parked/DelectusShotEvaluator.txt mode change 100644 => 120000 doc/devel/rfc_parked/DesignRenderNodesInterface.txt mode change 100644 => 120000 doc/devel/rfc_parked/FeatureBundle_PluggableModules.txt mode change 100644 => 120000 doc/devel/rfc_parked/GitSubmoduleTransistion.txt mode change 100644 => 120000 doc/devel/rfc_parked/NormalizedDeviceCoordinates.txt mode change 100644 => 120000 doc/devel/rfc_parked/RenderOptimizer.txt mode change 100644 => 120000 doc/devel/rfc_parked/SkillsCollection.txt mode change 100644 => 120000 doc/devel/rfc_parked/TodoLists.txt mode change 100644 => 120000 doc/devel/rfc_parked/UseCases.txt mode change 100644 => 120000 doc/devel/rfc_pending/ApplicationInstall.txt mode change 100644 => 120000 doc/devel/rfc_pending/DesignParamAutomation.txt mode change 100644 => 120000 doc/devel/rfc_pending/DeveloperDocumentationStructure.txt mode change 100644 => 120000 doc/devel/rfc_pending/EngineInterfaceOverview.txt mode change 100644 => 120000 doc/devel/rfc_pending/EngineInterfaceSpec.txt mode change 100644 => 120000 doc/devel/rfc_pending/RefactorLiblumieraOut.txt mode change 100644 => 120000 doc/devel/rfc_pending/ResourceManagementBudgeting.txt mode change 100644 => 120000 doc/devel/rfc_pending/ResourceManagementProfiling.txt mode change 100644 => 120000 doc/devel/rfc_pending/StreamTypeSystem.txt mode change 100644 => 120000 doc/devel/rfc_pending/WebsiteNavigation.txt diff --git a/doc/devel/rfc/ApplicationInstall.txt b/doc/devel/rfc/ApplicationInstall.txt new file mode 100644 index 000000000..44ed4cdea --- /dev/null +++ b/doc/devel/rfc/ApplicationInstall.txt @@ -0,0 +1,225 @@ +ApplicationInstall +================== + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Draft_ +*Date* _Di 11 Jan 2011 17:00:55 CET_ +*Proposed by* Ichthyostega +------------------------------------- + +[abstract] +********************************************************************************* +Lumiera should be a _freely relocatable_ application bundle. +Relying only on the relative folder structure within this bundle, the application +will be fully functional at any location, provided that the external library +dependencies are resolvable using the standard mechanisms of the platform. +The setup mechanism must be obvious, self-explanatory and must not rely +on compiled in magic or buildsystem trickery. Yet packaging into a FSH conforming +installation location should be supported by the same mechanisms. +********************************************************************************* + +Description +----------- +//description: add a detailed description: +Lumiera is planned to become a large professional application bundle, relying +on several external resources for proper operation. An installed Lumiera +application will be more like Gimp, Blender, OpenOffice or Eclipse, +not like bash, autotools or emcas. + +Besides that, it can be expected that Lumiera frequently will be used in a +project or studio like setup, where the application isn't installed, but just +unZIPped / unTARed and used as-is. Thus, it should be sufficient to unpack +the application bundle and point it to the session file and maybe the +media storage. + +The Eclipse platform can serve as a model for the setup of an modern +application of that style: It can be just unpacked, and when looking +into the folder structure, the meaning of the parts is obvious, and the +basic bootstrap is controlled by two short text based INI files. +While Lumiera presumably won't get _that_ heavyweight and is clearly +not intended to become a general business application platform like OSGi -- +the underlying principles can serve as a point of reference for modern +development standards. + +This leads to the following conclusions: + +- we need to define a standard folder layout for the bundle +- the application must not rely on any compiled-in absolute paths +- the executable should fetch the directly accompanying shared modules +- all other lib dependencies should be handled by the system mechanisms +- the bootstrap configuration likewise must not be compiled-in +- this configuration must be human readable and clearly exhibit its purpose +- the same system must be able to adapt to a FSH conforming installation layout + +Judging from our current planning and the existing codebase, Lumiera +is on a good way in that direction, yet some cleanup needs to be done, +especially removing convenience shortcuts from the early days of development +and catching up with the repair of some traits of sloppyness here and there. + +Library resolution +~~~~~~~~~~~~~~~~~~ +In former days, it was common habit to compile-in a hard wired absolute ++RPATH+. This can be considered obsolete practice; for example, the Debian +policy forbids doing so. This is the result from numerous maintainability +problems in the past. On the other hand, the GNU linker and other modern +linkers support a relative resolution of shared modules directly accompanying +an specific executable. The Debian policy allows this, if and only if these +shared modules are installed with the same binary package and only used by +this specific executable(s). Together, this is exactly what we need to +solve our requirement. + +Thus, the build process enables the new-style DT-tags in the Elf binary +and sets the +DT_RUNPATH+ with an value relative to +$ORIGIN+, which resolves +to the path of the currently executing binary. Moreover, it is _sufficient_ +to set this on the initial executable _only,_ because this creates a common +searchpath for all lib resolution events in the scope of that loaded executable. +Besides that, we need to care that our private libraries have a unique +SONAME+, +in this case all starting with the prefix +liblumiera*+. Note moreover that this +new-style +DT_RUNPATH+ indeed _can_ be overridden by an +LD_LIBRARY_PATH+ in the +environment, should there be the need for very special experiments. + +Bootstrap location +~~~~~~~~~~~~~~~~~~ +Thus, a single relative library folder becomes the only hard wired start +configuration. In our case, the folder +$ORIGIN/modules+ was chosen. The +root of the package then holds all the binaries depending on these common +internal libraries, that is the +lumiera+ executable and any accompanying +special tools. As usual with such large application bundles, these get +only _symlinked_ into the +/usr/bin+ folder on installation. + +For sake of clarity, after starting the executable, the _same location_ +is used to load the bootstrap configuration. This configuration in turn +defines all further locations like the extended configuration, project +templates, plugin search path, the GUI module to load, the search path +for icons and GUI resources, project templates and similar basics. + +Relative paths and the location of the executable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +According to the general requirements outlined above, most paths should +be given in a relative fashion. Unfortunately there is no portable solution +for self-discovering the currently running executable. But at least there +is a solution for all current major platforms. Under Linux, this information +can be retrieved from the kernel through the +/proc+ filesystem. + +Again for sake of clarity, the same token +$ORIGIN+ was chosen to denote +this executable location (note: this is _not_ the current working directory). +Moreover, due to the folder layout detailed above, this coincides with the +root of the application bundle, thus making for a self-explanatory convention. +Besides +$ORIGIN+, these search paths later on likely will contain locations +below the user's home directory, e.g. +~/.lumiera/themes+ + + +Tasks +~~~~~ +// List what needs to be done to implement this Proposal: +* identify what impedes such a modern setup procedure ([green]#✔ done#) +* rectify the folder structure created in the build target + directory ([green]#✔ done#) +* build the executables in a way to allow relative resolution of the + internal shared modules ([green]#✔ done#) +* replace the compiled-in path definitions for plugin loading by a + configurable bootstrap ([green]#✔#) +* add an working library implementation for a config loader ([green]#✔ done#) +* add a mechanism for establishing the path of the current execubable. + + This is _non-portable_ ([green]#✔ done#) +* wire the prepared API in the GUI to use this working config loader + for resolving GUI resources ([green]#✔ done#) +* try to extract the path search code from the existing config loader, + or build a new solution based on standard libraries ([green]#✔ done#) +* introduce an output root directory into the buildsystem, allowing + for package builds ([green]#✔#) +* define a _Debian packaging_ as proof-of-concept ([green]#✔ done#) + + +Discussion +~~~~~~~~~~ + +Pros +^^^^ +* self-contained +* self-explanatory +* based on _best practices_ +* conforming with FSH and Debian policy + + +Cons +^^^^ +* requires work +* raises the bar at the implementation side +* requires an bootstrap sequence to be explicitly performed + on application startup +* breaks with some beloved habits of the Unix community + + +Alternatives +^^^^^^^^^^^^ +//alternatives: explain alternatives and tell why they are not viable: +I can think of two alternatives + +. dealing with all those problems _later_ +. not making an concept, rather sticking to UNIX habits + +The first alternative is indeed worth considering, because we're settling +some things to be really implemented way later, which bears some dangers. +But, on the other hand, it is a common practice known from extreme programming +to deliver early and regularly, which effectively means to set up the deploy +path of an application really early in the development cycle. The rationale +is that -- according to general experience -- the deployment always turns +up some very specific problems and constraints, which can be a serious +threat when discovered late in the development process. + +The second alternative isn't really applicable IMHO. The original UNIX philosophy +breeds on an academic setup and really excels with small nifty commandline utils +combined by pipes, each specialised to do a single thing very well. These utils +are more like the objects within our implementation. The concept of large +application software bundles and desktop software was always a bit alien +within the classic UNIX environment. + + + +Rationale +--------- +//rationale: Give a concise summary why it should be done *this* way: + +This RfC can be seen as an commitment to an professional approach and as +clarification: Traditionally, the Unix community hailed a lot of _black magic_ +practices like compiled-in installation paths, macro magic, +sed+ and +awk+ +trickery, inline code compiled on-the-fly, relying on very specific and +un-obvious behaviour of some build script, configuration via environment +variables and a lot of similar idioms. These practices might be adequate +in a quickly moving Research & Development setup, but turned out to be +not so helpful when it comes to industrial strength development, +as they are known to lead to maintenance problems. + + + + +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: + + + + +Comments +-------- +//comments: append below + +.State -> Draft +There is now a complete implementation of this concept on my ``proc'' branch. + +Moreover, I was able to define an initial Debian packaging for Lumiera on top +of that implementation. + +During that work, I had opportunity to visit various areas of the existing codebase, +which reminded me of several small issues, which seem to become unhealthy when lying +around unfixed for such a long time. Probably I'll start a clean-up initiative and +try to bring these points to discussion separately. + + So 13 Feb 2011 20:04:00 CET Ichthyostega + + +//endof_comments: diff --git a/doc/devel/rfc/ApplicationStructure.txt b/doc/devel/rfc/ApplicationStructure.txt new file mode 100644 index 000000000..3b18ab92b --- /dev/null +++ b/doc/devel/rfc/ApplicationStructure.txt @@ -0,0 +1,200 @@ +Design Process : Application Structure +====================================== + +[grid="all"]] +`------------`---------------------- +*State* _Dropped_ +*Date* _2008-11-05_ +*Proposed by* link:ct[] +------------------------------------ + +Application Structure +--------------------- +Here I am going to propose some more refined structure of the application and +its components. + + +Description +~~~~~~~~~~~ +So far we came up with a simplified BACKEND/PROC/GUI structure where each of +this entities defines its own sub subcomponents. We agreed to glue that all +together with some portable versioned interfaces system, but details where not +laid out yet. At the time of this writing the interface system and plugin +loader are reasonable finished to be usable (some small refinements to do). We +recently discussed some details on IRC on how to engage this without a +definitive decision. The topic of this proposal is to make a detailed +description towards how the application components being glued together. + +In the discussion mentioned above we concluded that we want a 'lumiera' binary +which in turn loads the optional parts as plugins. There was no consent what +this parts are actually be, except that the GUI should be optional for headless +operation. I suggested to make as much as possible pluginable to make it easier +to validate our interfaces and try different things out. + +Now I introduce 'lumiera' here, this will become a new component in +./src/lumiera being the driver application for bootstraping all the rest: + +Then our application structure looks somewhat like (please refine): + + * the 'lumiera' loader + - commandline handling + - interface & plugin system + - session manager core + - configuration system + - lua scripting + * backend + - file and io handling + - caches + - streams + - threads + - scheduler + * proc + - asset management + - config rules system + - builder + - render graph management + * gui + - timelines + - viewers + - resources + - preferences + - ... + +Furthermore the interface&plugin system is flexible enough to provide things +independently of their origin (if it is build in or a plugin/dynamic library). +So deployment (where to link these things) is secondary. + +'lumiera' will then be the executable the user starts up, what exactly gets + initialized and booted up is then matter +of configuration and commmandline options (and maybe lua scripting?). + + + +Tasks +^^^^^ + + * create the 'lumiera' directory + - setup the build system + - move config, plugin and interfaces therein + - lua support can be done later + * write the main() part of the application + - start config system + - parse commandline opts + * librificate all other components (backend, proc gui) + - define their lumiera interfaces + - decide if they shall be statically linked, becoming shared libs or plugins + +This are rather distributed tasks, after the 'lumiera' being set up, all other +components have to be adapted to be loadable from it. + + +Pros +^^^^ + + * flexible plugin based architecture + - later: loads only things which are necessary for a given task + * very fast startup + * things which cant be used on a given environment can be left out (no gui on + a headless system, no $DISPLAY set) + * inter dependencies between interfaces and plugins are automatically tracked. + + +Cons +^^^^ + +Ichthyo raised concerns that this kind of flexibility might attract other +people to write things which are not in our intention and break future design +and compatibility. We need to carefully document and define interfaces that +people don't abuse those! + + + +Alternatives +^^^^^^^^^^^^ + +We discussed the startup/main() through the GUI as it is currently done, it +would be also possible to produce some more executables (lumigui, luminode, +lumiserver, ....). But I think we agreed that a common loader is the best way +to go. + + +Rationale +~~~~~~~~~ + +I just think this is the best way to ensure a enduring design even for future +changes we can not forsee yet. + + + + + +Comments +-------- +We discussed this issue lately on IRC and I got the feeling we pretty much +agreed on it. + + * we don't want to build a bunch of specialized executables, rather we build + one core app which pulls up optional parts after parsing the config + * we change the GUI to be loaded via the module/interfaces system + +From reading the above text, this proposal seems to capture that. But I am +somewhat unsure if the purpose of this proposal isn't rather to load just a +micro kernel and the pull up components according to configuration. Because I +wouldn't accept such an architecture, and I clearly stated so right at the +beginning of our project. I accepted a very flexible and language neutral +plugin system on the condition the core remains in control, stays +''reasonable'' monolithic and componentization doesn't handicap us in creating +an architecture based on abstractions and exploiting the proven design +patterns. + +It has that flexibility, yes. But that means not that we have to abuse it in +any way. The main() there and thus the bootstrap of the application is under +our tight control, if we want to reject scriptable/highly configurable +bootstrapping there then we can just do so. Thats more a social than a +technical decision. I personally don't like if a design is 'nannying' and puts +too much constraints into unforeseen areas. If the computer can do some task +better than we, it shall do it. This still means that I want to stay very much +in control, it should only do some tedious, error-prone managing tasks for me. +For example the interfaces system already tracks inter-dependencies between +plugins and interfaces automatically, without the programmer needs to care or +define anything. The interface system gets it right and we wont need to care +for the order initialization. I added that because I consider such as +absolutely important for plugins which might be supplied by third parties where +we have no control over. But I now realized that we can nicely use that for our +own internal things too. Imo thats some very valuable service. + -- link:ct[] [[DateTime(2008-11-08T06:26:18Z)]] + +Some further minor details: We didn't finish the discussion about namespaces on +the last meeting. (I know I still have to write up a proposal showing the two +or three alternatives I see regarding namespace organisation). But probably, +"lumiera::" will be our top level interface namespace and then probably the +lumiera directory will be taken by that. I see no problem also putting some +startup facilities in there, but generally, it shouldn't contain implementation +code, only headers and abstract classes. If that's going to become a problem, +we should consider to use a separate package for the startup, e.g. "src/boot". + +Another point is, you need not write a main, because there is already one. +Please have a look at it, especially with regards to the +[wiki:self:../GlobalInitialization global initialisation]. Further, last year +I've investigated boost::program_options and think it's fine. I use it for my +test class runner since then. I don't think there is any reason why we should +bother with parsing options (most config is pulled up from the session). I +don't think we get much program options, maybe something to set a GUI skin. +Moreover, I've written last year a thin wrapper around the commandline and +integrated it with the boost options parser such that user code can receive the +remaining options as a vector of std::strings. Please have a look at +link:http://git.lumiera.org/gitweb?p=LUMIERA;a=blob;f=tests/common/mainsuite.cpp;h=455bfd98effd0b7dbe6597f712a1bdfa35232308;hb=80e1e382f42512ebf2e10a802f77e50327b8fb73[the test class runner main] +for an usage example. I really want our Lumiera main to be clean and expressive +in the way showed there. Probably the most important part of the startup is +pulling up the session core; because of that I think most of the startup +process falls into the realm of the Proc-Layer. Within Proc, I don't want any +significant string manipulations done with C-strings and I don't want raw +arrays when we can use std::vector. + -- link:Ichthyostega[] [[DateTime(2008-11-06T19:28:13Z)]] + +I 'dropped' this now because we do it somewhat differently now and I dont want +to document this here :P + -- link:ct[] [[DateTime(2009-02-03T17:28:28Z)]] + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/ClipCatalogingSystem.txt b/doc/devel/rfc/ClipCatalogingSystem.txt new file mode 100644 index 000000000..8149c23ac --- /dev/null +++ b/doc/devel/rfc/ClipCatalogingSystem.txt @@ -0,0 +1,128 @@ +Design Process : Clip Cataloging System +======================================= + +[grid="all"] +`------------`----------------------- +*State* _Parked_ +*Date* _2008-07-26_ +*Proposed by* link:JordanN[] +------------------------------------- + +Clip Cataloging System +----------------------- + +A system for storing, organizing, and retrieving assets, such as images and +videos. + + +Description +~~~~~~~~~~~ + +Organizations that work with video, and even home users, tend to have massive +collections of stock videos and images that they will need to find and use in +their projects. A Linux-based system is needed to help them to organize, tag, +and retrieve assets from those collections. Being able to find the clips the +user needs and bring them into his timeline, will mean that the user will be +able to more rapidly complete his project. + +This could be implemented as a separate application, but integrated for use in +a Linux-based video workflow suite, including apps like Lumiera and Blender. + + +Tasks +~~~~~ + +* Identify ways in which existing groups organize their collections. +* Determine pros / cons of each method +* Implement a solution that will be modular enough for other content creation + projects to also use + + +Pros +~~~~ + +* Faster, more efficient workflow + + +Cons +~~~~ +Not directly a part of Lumiera. If not implemented separately, could cause +undue bloat. + + +Alternatives +~~~~~~~~~~~~ +Storage-based organization. User must remember where files are, and must store +them correctly. Not clip-based, so the entire video must be imported and the +desired portion selected. + + +Rationale +~~~~~~~~~ + + +Comments +-------- + +* Such is planned, but as you pointed out, this would be a rather standalone + application which needs a lot of efforts to be implemented. We don't have the + development power to do that now. If someone wants to work on that, please + contact me. General idea is to put all kinds of resources (Footage, Clips, + Effects, Subprojects, Sounds ....) into a database with then gets + tagged/attributed in different ways (implicit things like 'filename', 'type', + 'length'; automatic deduceable things like 'Exposure', 'Timecode', ...; And + manual tags like: who was on set, location, ....). Then present this all in a + *good* GUI (by default just showing filesysten like) but one can define + queries on this database and the generated views will then be storeable. +Back to Lumiera, for now we will likely just use 'normal' file open dialogs +until the above system becomes available. + -- link:ct[] [[DateTime(2008-07-26T08:31:42Z)]] +* Yes, it's indeed an important feature we should care for. But cehteh is + right, we have more important things to do first. But feel free to target it. +* Also, we'd need integration with production support systems, for example + http://celtx.com/[CELTX]. +* The interface to the Lumiera App would be to populate the asset manager with + the required assets + -- link:Ichthyostega[] [[DateTime(2008-07-27T22:19:38Z)]] + + +Videos, Audio, Clips and Resources Manager by using plugins for FOSS GPL +"Library & Collections Management" programs. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The video and audio raw material, clips, etc could be managed using code that +is already available in project that carry out the same tasks. For example as +library managers, or media (video, audio or CD) collections, Integrated +Library Systems (ILS). + +Examples of a library management program ; + +. Kete - http://kete.net.nz/[] +. Koha - http://www.koha.org/[] +. link:GreenStone[] - http://www.greenstone.org/[] +. Evergreen - http://open-ils.org/faq.php[] + +An additional benefit to using "library" managers, is that it can handle +interloans, referencing of "other" (people's/organization's) libraries, +numbering systems, descriptions, and classifications, thousands to millions of +items, search systems, review and comment systems, plus the benefits of open +source that allow the expansion of features easily. The use of task oriented +programs in this way, makes use of established code, that has been developed by +experts in their field. Any database system would be useful for managing all +these media. But one that has been developed by the people that have been +working with cataloging systems for a long time is likely to do well. Plus it +can be readily improved, by people who do not have to know the first thing +about how to design video editing programs. The program also gets improved +because of it own community, which adds features or performance to Lumiera, +without even having to "drive" the development.. +--link:Tree[][[DateTime(2008-08-27T20:38:00NZ)]]. + +'''' + +Parked until someone cares +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Decided on Developer meeting + + Do 14 Apr 2011 02:52:30 CEST Christian Thaeter + + +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/DelectusShotEvaluator.txt b/doc/devel/rfc/DelectusShotEvaluator.txt new file mode 100644 index 000000000..0d5b55a88 --- /dev/null +++ b/doc/devel/rfc/DelectusShotEvaluator.txt @@ -0,0 +1,397 @@ +[grid="all"] +`------------`----------------------- +*State* _Parked_ +*Date* _2008-09-21_ +*Proposed by* link:nasa[] +------------------------------------- + + +Delectus Shot Evaluator +----------------------- +This is a brain dump about the shot evaluator subproject. + + +Description +~~~~~~~~~~~ + + +Brainstorm on Delectus +~~~~~~~~~~~~~~~~~~~~~~ +Some (many) of the ideas presented herein come from the various parties +involved in the Lumiera discussion list and IRC channel #lumiera. +http://lists.lumiera.org/pipermail/lumiera/2008-September/000053.html[] -- the +main discussion thread + +Additionally, a lot of great concepts for how to streamline the interface are +derived in part from link:KPhotoAlbum[]. + +I use tags, keywords, and metadata almost interchangeably, with the exception +that metadata includes computer generated metadata as well. These are not tags +in the conventional sense -- they don't have to be text. In fact the planned +support (please add more!) is: + + * Text -- both simple strings (tags) and blocks + * Audio -- on the fly (recorded from the application) or pregenerated + * Video -- same as audio + * Link -- back to a Celtx or other document resource, forward to a final cut, + URL, etc + * Still image -- inspiration image, on set details, etc + * ID -- such as the serial number of a camera used, the ISBN of a book to be + cited, etc + +As such, the tags themselves can have metadata. You can see where this is +going... + +Also, the tags are applied to "clips" -- which I use interchangeably between +source material imported into the application and slice of that material that +tags are applied to. Any section of a video or audio source can have tags +applied to it. + + +Two key functions: assign metadata and filter by metadata. + +clips are one thing; but in reality most clips are much longer than their +interesting parts. Especially for raw footage, the interesting sections of a +clip can be very slim compared to the total footage. Here is a typical workflow +for selecting footage: + +. Import footage. +. Remove all footage that is technically too flawed to be useful. +. Mark interesting sections of existing clips, possibly grouped into different + sections. +. Mark all other footage as uninteresting. +. Repeat 3-4 as many times as desired. + +Some key points: + + * Import and export should be as painless and fast as possible. + * Technically flawed footage can be both manual and computer classified. + * In some cases (e.g. documentaries, dialog) audio and video clips/footage can + follow different section processes. + It is possible to use video from footage with useless audio or use audio + from footage with useless video. + * "Interesting" is designed to be broad and is explained below. + * steps 2-5 can be performed in parallel by numerous people and can span many + different individual clips. + +In simple editors like Kino or iMovie, the fundamental unit used to edit video +is the clip. This is great for a large number of uses, such as home videos or +quick Youtube postings, but it quickly limits the expressive power of more +experienced engineers in large scale productions (which are defined for the +purposes of this document to include more than 2 post-production crew members). +The clip in those editors is trimmed down to include only the desired footage, +and these segments are coalesced together into some sort of coherent mess. + +The key to adequate expressive power is as follows: + + * Well designed, fast metadata entry. Any data that can be included should by + default, and ideally the metadata entry process should run no less than + about 75% as fast as simple raw footage viewing. Powerful group commands + that act on sections of clips and also grouping commands that recognize the + differences between takes and angles (or individual mics) enhance and speed + up the process. + * Good tools to classify the metadata into categories that are actually + useful. Much of the metadata associated with a clip is not actively used in + any part of the footage generation. + * Merging and splicing capabilities. The application should be smart enough to + fill in audio if the existing source is missing. For example, in a recent + project I was working on a camera op accidently set the shotgun mike to test + mode, ruining about 10% of the audio for the gig. I was running sound, and + luckily I had a backup copy of the main audio being recorded. This + application should, when told that these two are of the same event at the + same time, seamlessly overlay the backup audio over the section of the old + audio that has been marked bad and not even play the bad audio. This is just + background noise, and streamlining the immense task of sorting through + footage needs to be simplified as much as possible. + * Connection to on site documentation and pre-production documentation. When + making decisions about what material to use and how to classify it, it is + essential to use any tools and resources available. The two most useful are + onsite documentation (what worked/didn't work, how the weather was, pictures + of the setup, etc all at the shoot) and pre-production (what the ideal scene + would be, what is intended, etc). Anything else that would be useful should + be supported as well. + * Be easily accessible when making the final cut. Lumiera is, if the + application gets up to speed, going to serve primarily to render effects, + finalize the cut, and fine tune what material best fits together. Any + metadata, and certainly any clipping decisions, should be very visible in + Lumiera. + * Notes, notes, notes! The application should support full multimedia notes. + These differ from (4) in that they are generated during the CLASSIFICATION + process, not before. This fits in with (5) as well -- Lumiera should display + these notes prominently on clip previews. The main way for multiple parties + to communicate and even for a single person to stay organized is to add in + notes about tough decisions made and rationale, questionable sections, etc. + These notes can be video, audio, text, etc from one of the clips, from the + machine used to edit (such as using a webcam or microphone), or over the + network (other people's input). + + +Too technically flawed +^^^^^^^^^^^^^^^^^^^^^^ +A clip is said to be too technically flawed if it has no chance of making it to +the final product whatsoever. This does not, however, preclude its use +throughout the post-production process; for example, part of a clip in which +the director describes his vision of the talent's facial expression in a +particular scene is never going to make it into the final product, but is +invaluable in classifying the scene. In this case, the most reasonable place to +put the clip would be as a multimedia note referenced by all takes/angles of +the scene it refers to. + +As mentioned above, flawed video doesn't necessarily mean flawed audio or +vice-versa. + + +Interesting +^^^^^^^^^^^ +An "interesting" clip is one that has potential -- either as a metadata piece +(multimedia note, talent briefing, etc) or footage (for the final product OR +intermediary step). The main goal of the application is to find and classify +interesting clips of various types as quickly as possible. + + +Parallel Processing +^^^^^^^^^^^^^^^^^^^ +Many people, accustomed to different interfaces and work styles, should be able +to work on the same project and add interactive metadata at the same time. + + +Classification interface +++++++++++++++++++++++++ +The classification interface is divided into two categories: technical and +effective. Technical classification is simply facts about a clip or part of a +clip: what weather there is, who is on set, how many frames are present, the +average audio level, etc. Effective classification allows the artist to express +their feelings of the subjective merits (or failures) of a clip. + + +DCMS +^^^^ +The project is organized around a distributed content management system which +allows access to all existing materials at all times. Content narrowing allows +for a more digestible amount of information to process, but everything is +non-destructive; every change to the clip structure and layout is recorded, +preferably with a reason as to why it was necessary or desired. + + +Content narrowing +^^^^^^^^^^^^^^^^^ +With all of the information of an entire production available from a single +application, information overload is easy. Content narrowing is designed to fix +that by having parts of individual clips, metadata, or other files be specific +to one aspect of the overall design. This allows for much more successful use +of the related information and a cleaner, streamlined layout. As an example, +metadata involving file size has no effect whatsoever on the vast majority of +most major decisions -- the answer is almost always "whatever it takes." Thus, +it would not appear most of the time. Content narrowing means that it is easy +to add back footage -- "widen the view" one step, add it back, and "narrow the +view" again. + + +Multiple cuts +^^^^^^^^^^^^^ +There is no need to export a final cut from this application; it merely is the +first step in the post-production chain. It is the missing link between +receiving raw footage from the camera and adding the well executed scenes to +the timeline. What should come out of the application is a classification of + + +Situational, take, and instance tagging +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +This is VERY powerful. The first step to using the application is to mark which +scenes are the same in all source clips -- where same means that they contain +sections which would both not run. This can include multiple takes, different +microphones or camera angles, etc. The key to fast editing is that the +application can edit metadata for the situation (what is actually going on IN +THE SCENE), take (what is actually going on IN THIS SPECIFIC RUN), and instance +(what is actually going on IN THIS CLIP). If editing a situation, the other +referenced clips AUTOMATICALLY add metadata and relevant sections. This can be +as precise and nested as desired, though rough cuts for level one editing +(first watchthrough after technically well executed clips have been selected) +and more accurate ones for higher levels is the recommended method. + + +Subtitling +^^^^^^^^^^ +This came up on the discussion list for Lumiera, and it will be supported, +probably as a special tag. + + +nasa's Laws of Tagging +^^^^^^^^^^^^^^^^^^^^^^ +. There is always more variety in data than tags. There are always more + situations present in the data than can be adequately expressed with any + (reasonable) number of tags. This is OK. All that is needed is the minimum + set of unique tags to progress to the next cycle without losing editing + intent or the ability to rapidly evaluate many situations. +. Many tags are used many times. "Outdoors" will be a very, very common tag; so + will "redub." If conventional names are decided upon and stuck to, it is + significantly easier to map the complex interactions between different + content situations. +. Avoid compound tags. Do not have "conversation_jill_joe" as a tag; use + "conversation," "jill," and "joe" instead. It is very easy to search for + multiple tags and very hard to link data that doesn't use overlapping tags. + + + + + + + + + + +The interface -- random idea +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This is not meant to be a final interface design, just something I wrote up to +get ideas out there. + +key commands + mutt/vim-style -- much faster than using a mouse, though GUI supported. + Easy to map to joystick, midi control surface, etc. +Space stop/start and tag enter Tab (auto pause) adds metadata special Tracks +have letters within scenes -- Audio[a-z], Video[a-z], Other[a-z] (these are not +limits) -- or names. Caps lock adds notes. This is really, really fast. It +works anywhere. This means that up to 26 different overlapping metadata +sections are allowed. + +Prompting Prompting for metadata is a laborious, time-consuming process. There +is no truly efficient way to do it. This application uses a method similar to +link:KPhotoAlbum[]. When the space key is held and a letter is pressed, the tag +that corresponds to that letter is assigned to the track for the duration of +the press. (If the space is pressed and no other key is pressed at the same +time, it stops the track.) For example, suppose that the following mapping is +present: +o = outside +x = extra +p = protagonist +c = closeup + +Then holding SPACE over a section and pressing one of these keys would assign +the tag to the audio AND video of the section over which the space was held. If +instead just the key is pressed (without space being held), that tag is +assigned to the section over which it is held. This is very fast and maps well +to e.g. PS3 controller or MIDI control. + +If LALT is held down instead of SPACE, the audio is effected instead. If RALT +is held, just the video is effected. + +In order to support scenario/take/clip tagging: + The default is situation. If the keybinding to x is: + x = t:extra ; effect only take + x = ts:extra ; effect take and scenario + x = c:extra ; extra only visible in this clip! + x = tc:extra ; this take and clip show the extra + etc + +Other keyargs (the part in front of the colon) can be added to account for +other uses (e.g. l = all taken on the same location). + +Tab is pressed to add metadata mappings. Tab is pressed to enter metadata edit +mode; this pauses video. Then press any key to map; and type the tag to +associate (with space, multiple tags can be added.). The following specials are +defined: + [:keyarg:]:TAG is special tagging for scenario/take/clip. + !TAG removes TAG if it is present. This is useful because it allows huge + sections of the clip to be defined as a certain tag, then have parts + removed later. + a:TAG applies TAG only to the audio. + v:TAG applies TAG only to the video. + p:PATH adds a link to PATH as a special tag. + +(This will have a nice GUI as well, I just will always use the keyboard method + so I am describing it first. Mapping configurations can be stored in a + separate file, as a user config, or in the specific project.) + +If ESC is pressed, all currently ranged tags are ended. + +Finally, if single_quote is pressed without SPACE or {L,R}ALT down, it marks an +"interesting location." Pressing SHIFT+single_quote goes to the next +"interesting location" and pressing CNTRL+' goes to the previous "interesting +location." This allows for very quick review of footage. + + + + + + + + + + + + + + + +Comments +-------- + + +Rating - Quantitative Rating as well as Qualitative Tagging +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The importance/value of the video for various factors uses, can vary through +the video. It would be helpful to have the ability to create continuous ratings +over the entire track. Ratings would be numerical. Automatic clip +selection/suggestion could be generated by using algorithms to compute the +usefulness of video based on these ratings (aswell as "boolean +operations"/"binary decisions" done with tags). The ratings could be viewed +just like levels are - color coded and ovelayed on track thumbnails. + +- Tree 2008-10-25 + + +link:MultiView[] - useful for concurrent ratings input +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It would be convenient to have an ability to view the different tracks (of the +same scene/time sequence) at once, so the viewer can input their ratings of the +video "on the fly", including a priority parameter that helps decide which +video is better than what other video.See the GUI brainstorming for a viewer +widget, and key combinations that allow both right and left hand input, that +could be used for raising/lowing ratings for up to six tracks at once. + +- Tree 2008-10-25 + + +I like the idea of rating clips (or rather, takes) a lot. It would be cool to +include both "hard," "relative," and "fuzzy" rating. Hard is an exactly defined +value (scaled 0-1) that puts the clip in an exact location in the queue. +Relative means that one is higher or lower rated than another. Fuzzy is a +slider which is approximate value, and there is some randomness. The best part +is that these can be assigned to hardware sliders/faders. Pressure sensitive +buttons + fuzzy ratings = really easy entry interface. Just hit as hard as +needed! Multiple tracks at once also an astounding idea. I could image some +sort of heap (think binary heap, at least for the data structure) which +determines the priorities and decides which clips are played. Then the highest +rated clips are played first, down to the worst. + +- link:NicholasSA[] 2009-01-04 + + +Possible Collaboration with the people from Ardour? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +I guess if the thing can do all the things we talked about here, it would be +perfectly suitable for sound classification too, and maybe could fill another +gap in FOSS: Audio Archival Software, like this: +http://www.soundminer.com/SM_Site/Home.html[] (which is very expensive)... +maybe the Ardour people would be interested in a collaboration on this? + +I like the suggestion of sound classification with a similar (or, even better, +identical) evaluator. link:SoundMiner[] looks interesting, but like you say +very expensive. I'm a sound guy, so I feel your pain... + +- link:NicholasSA[] 2009-01-04 + + +Parked +~~~~~~ + +Decided on Developer meeting, until someone wants to investigate this further. + + Do 14 Apr 2011 02:52:30 CEST Christian Thaeter + + +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/DesignParamAutomation.txt b/doc/devel/rfc/DesignParamAutomation.txt new file mode 100644 index 000000000..9ed2d9b4a --- /dev/null +++ b/doc/devel/rfc/DesignParamAutomation.txt @@ -0,0 +1,84 @@ +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _2008-03-06_ +*Proposed by* link:Ichthyostega[] +------------------------------------- + + +Design the handling of Parameters and Automation +------------------------------------------------ +Parameters of Plugin Components and/or Render Nodes play a role at various +levels of the application. + + +Thus it seems reasonable to do a formal requirements analysis and design prior +to coding. + + +Description +~~~~~~~~~~~ +Regarding components directly participating in the render (which may be +implemented by plugins), we distinguish between *configuration* (static) and +*parameters* (dynamic). The point of reference for this distinction is the +render process: a plugin configuration may well be variable in some manner, +e.g. the plugin may provide different flavours of the same algorithm. But this +choice has to be fixed prior to feeding the corresponding plugin asset to the +builder. Contrary to such fixed configuration setup, the _parameters_ are +considered to be _variable_ during the rendering process. They can be changed +on-the-fly from GUI, and they may be automated. Probably, each Render Node will +have at least one such _parameter_ -- namely a bypass switch. + + +Tasks +^^^^^ + + * we need to work out an introspection mechanism for parameters + - asses what different types of parameters we need + - find out how much structured parameters will be (do simple values + suffice?) + - define how parameters can be discovered/enumerated + - define a naming scheme for parameters, so they can be addressed + unambiguously + * value parameters have a value range. Work out how to handle this + * parameters may need a specific presentation in the GUI + - linear/logarithmic scale, scale reference + - selecting the right widget + +So... + +. find out to which extend we need these properties +. find out what parts of the App will have what requirements? +. chose a best fitting implementation based on this information + +A closely related issue is the handling of *Automation*. The current draft +calls for an abstract interface "ParamProvider", which just allows the +link:Plugin/RenderComponent[] to pull a current value, without knowing if the +ParamProvider is a GUI widget or an automation data set with interpolation. The +component using the param value should not need to do any interpolation. We +should re-asses and refine this draft as needed. Note: Render Nodes are +stateless; this creates some tricky situations. + + + + + + + +Alternatives +^^^^^^^^^^^^ +?? (any ideas?) + + +Rationale +~~~~~~~~~ + + + + + + +Comments +-------- + + +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/DesignRenderNodesInterface.txt b/doc/devel/rfc/DesignRenderNodesInterface.txt new file mode 100644 index 000000000..201c34a72 --- /dev/null +++ b/doc/devel/rfc/DesignRenderNodesInterface.txt @@ -0,0 +1,128 @@ +[grid="all"] +`------------`----------------------- +*State* _Parked_ +*Date* _2008-03-06_ +*Proposed by* link:Ichthyostega[] +------------------------------------- + + +Design the Render Nodes interface +--------------------------------- +In the current design, the low-level model is comprised of "Render Nodes"; +Proc-Layer and Backend carry out some colaboration based on this node network. + + +Three different interfaces can be identified + * the node wiring interface + * the node invocation interface + * the processing function interface + + +Description +~~~~~~~~~~~ +Render Nodes are created and wired by the Builder in the Proc-Layer. On the +other hand, the rendering process is controlled by the backend, which also +provides the implementation for the individual data processing tasks. To create +a result, output nodes are ''pulled'' via the invocation interface, resulting +in the affected nodes to recursively pull their predecessor(s). In the course +of this call sequence, the nodes activate their processing function to work on +a given set of buffers. Moreover, we plan to use the render network also for +gathering statistics. + +'''Note''': Render Node is an internal interface used by Proc-Layer and + activated by the Backend. Plugins are planned to be added via Adapter nodes. + Thus the Render Node interface needs ''not'' to be exported. + + +the wiring interface +^^^^^^^^^^^^^^^^^^^^ +This part of the design defines how nodes can be combined and wired up by the +builder to form a network usable for rendering. For this purpose, the +link:ProcNode[] is used as a shell / container, which is then configured by a +const WiringDescriptor. Thus, the node gets to know its predecessor(s) and is +preselected to use a combination of specific working modes: + + * participate in caching + * calculate in-place + * source reading + * (planned) use hardware acceleration + * (planned) remote dispatched calculation + +Most nodes will just have a single predecessor, but we can't limit nodes to a +single input, because there are some calculation algorithms which natively need +to work on several data streams simultaneously. This means, a single node can +be involved into the calculations for multiple streams (several pull calls on +the same frame number but for different channel, and in each case maybe a +different output node). I decided to rely solely on the cache for avoiding +duplicate calculations caused by this complication, because I deem it to be an +corner case. + + +the invocation interface +^^^^^^^^^^^^^^^^^^^^^^^^ +this is intended to be a rather simple "call-style" interface, without much +possibilites to influence the way things are happening. You pull a node and +will find the results in a provided buffer or the cache, but you can't even +change the frame data type type of the result. Besides the node invocation, +functions for collecting statistics will be accessible here too (Probably these +functions will be ''implemented'' in a classic-OO fashion by virtual functions, +but that's another story) + + +the processing interface +^^^^^^^^^^^^^^^^^^^^^^^^ +the individual nodes are configured to call a plain-C {{{process()}}} function +and provide an array of buffer pointers to be used within this function. For +the purpose of invoking actual data processing, it is irrelevant if this +function is implemented somewhere in the backend or provided by a plugin. At +this point, no type- and other meta-information is passed, rather the +processing function is supposed to do The Right Thing ^TM^ + + + +Tasks +^^^^^ + * What services do we expect from Render Nodes. What do we plan to do with a + render node? + * What different kinds (if any) of Render Nodes can be foreseen? + * order the required functionality by Proc / Backend. Find out specific + implementation constraints. + * work out a design based on this informations + + + + + + + + + +Rationale +~~~~~~~~~ +The purpose of this Design Entry is to give a summary; the questions and the +details of carrying out the operations are much more involved. + + +Please see the +http://www.lumiera.org/wiki/renderengine.html#Rendering[Proc-Layer impl +documentation (TiddlyWiki)] and the +http://www.lumiera.org/gitweb?p=lumiera/ichthyo;a=blob;f=src/proc/engine/procnod +.hpp;h=9cf3a2ea8c33091d0ee992ec0fc8f37bb5874d34;hb=refs/heads/proc[Source Code] +for details +(and/or contact Ichthyo for in-depth discussion of those technical details) + + + + + + +Comments +-------- + + +Parked +~~~~~~ +We park this until we have time to revisit the details. It is accepted that we +need to design this interfaces. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/DeveloperDocumentationStructure.txt b/doc/devel/rfc/DeveloperDocumentationStructure.txt new file mode 100644 index 000000000..1f88d130e --- /dev/null +++ b/doc/devel/rfc/DeveloperDocumentationStructure.txt @@ -0,0 +1,136 @@ +Developer Documentation Structure +================================= + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _Mon Aug 2 18:03:25 2010_ +*Proposed by* Christian Thaeter +------------------------------------- + +[abstract] +******************************************************************************** +I describe here how to bring the Lumiera Developer Documentation into an simple +hierarchical structure. Previously we accumulated a lot Documentation which +ended in quite a few different places. This should be tidied up. +******************************************************************************** + +Description +----------- +//description: add a detailed description: + +I propose to reorganize the developer documentation in the following way: + + * make a 3 (or more, see below) level documentation structure: + 1. The entry level becomes the 'Lumiera: The inner Core' document which shall + not go into details but give a hint what everything is made for. This + will be the first introductory doc for new developers. + 2. second level are the RFC's which descibe the design as planned on a + general level, not going (except for some example snippets) into + implementation details. + 3. the third level is the doxygen documentation which describes what + actually got implemented in detail. This can be further split into + an external reference and a internal part. + +We using test-driven-development, our tests are our specifications. This leads +to the idea that ideas, design and intentions for tests should be documented +there too. In a higher level abstract human written form. I propose to use my +pipadoc documentation extractor (that means, writing asciidoc within the code as +special comments) for this. + + +Tasks +~~~~~ +// List what would need to be done to implement this Proposal in a few words: +// * item ... + + * Go over the old content of the asciidoced tiddlywikis, integrate it either in + the "Lumiera: The inner Core" document or write single RFC's for them. + * The 'proc' tiddlywiki is a bit special, we need a plan how to integrate this. + Possibly making a own document-dir for this, or refactor it in plenty RFC's. + This is ichthyos decision. + * Decide how to proceed with the UML model + + + +Pros +^^^^ +// add just a fact list/enumeration which make this suitable: + +Much easier entry to the whole developer documentation. Reading the "Inner Core" +document should be sufficient to get a good idea about the Lumiera design and +layout. All details are linked from there and thus easily findable. + + +Cons +^^^^ +// fact list of the known/considered bad implications: + +There are some open ends yet, doxygen for example doesn't integrate nicely, we +possibly can't link to single doxygen entities since these have no permanent +link (to my understanding, to be investigated). Other parts like the UML model +are not yet decided and moving the other existing content over needs some (not +really much) work. + +Alternatives +------------ +//alternatives: explain alternatives and tell why they are not viable: + +Spring 2010 we discussed and decided an overall website and documentation structure. +We could just stick to that. + + +Rationale +--------- +//rationale: Describe why it should be done *this* way: + +This approach fits nicely into our overall infrastructure and the way we wanted +to do things. Using git and asciidoc mostly, making the developer documentation +part of the source tree and reasonable easy available/maintainable to +developers. + +//Conclusion +//---------- +//conclusion: When approbated (this proposal becomes a Final) +// write some conclusions about its process: + + + + +Comments +-------- +//comments: append below + +* The general idea of having three levels, with 'The Inner Core' as entry point, + looks OK for me. +* beyond that -- we had a detailed discussion about the overall website structure, + which includes the documentation. Why should we overthrow these results now and + re-start the discussion? Lets just stick to this agreed on structure! +* especially I don't like the way this proposal tries to squeeze everything into + an completely uniform structure. It is simply not true that the RFCs are just the + second level, and doxygen would cover the 3^rd^ level. Look at the existing + documentation to see why. + - RFCs are a 'kind' of document, not a 'hierarchy level.' Indeed, our existing + RFCs span all three hierarchy levels, and this is OK so and should remain this + way. (And yes, I like the RFCs much and want to retain them) + - RFCs are well suited to topics requiring discussion and agreement by the whole + core developer team. I see no point in 'pseudo-RFC-ing' the individual design + decisions only relevant for an isolated part of the application and without + any potential for discussion. + - similarily, in the TiddlyWiki, besides just working notes (``extended brain'') + you'll find finished text pages belonging to all different levels, from very + high-level conceptual down to explanation of technical details, with + cross references and tags for categorisation (and this will be retained + when asciidocing the content). +* so my conclusion is rather having one overview text, and then the split into + *conceptual* and *technical* documentation, each of which has a separate sub + structure not necessarily congruent to the structure on the other half. RFCs, + UML model and doxygen are just separate and consistent bodies of documentation + and can be referred to from the main documentation. (I agree with the observation + regarding permanent links into doxygen. But I can't imagine there isn't some + existing solution to this problem) + -- link:Ichthyostega[] 2010-10-15 + +//endof_comments: diff --git a/doc/devel/rfc/EngineInterfaceOverview.txt b/doc/devel/rfc/EngineInterfaceOverview.txt new file mode 100644 index 000000000..e9e931661 --- /dev/null +++ b/doc/devel/rfc/EngineInterfaceOverview.txt @@ -0,0 +1,271 @@ +Engine Interface Overview +========================= +:Date: 2010 + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Draft_ +*Date* _2010-04-16_ +*Proposed by* link:Ichthyostega[] +------------------------------------- + + +Overview Engine Interface(s) +---------------------------- + +******************************************************************************** +At the Engine Interfaces, Lumiera's Backend and Session get connected and work +together to produce rendered output. This design proposal intends to give an +overview of the connection points and facilities involved, to define some terms +and concepts and to provide a foundation for discussion and working out the +APIs in detail. +******************************************************************************** + + + +Participants +~~~~~~~~~~~~ + *Render Process*:: represents an ongoing calculation as a whole + *Engine Model*:: encloses the details of the current engine configuration and + wiring + *Dispatcher*:: translates a render process into the (planned) invocation of + individual nodes + *Scheduler*:: cares for calculations actually to happen, in the right order + and just in time, if at all + *Node*:: abstraction of an processing unit, supports planning by the + dispatcher, allows to pull data, thereby driving the actual calculation. + +Render Process +~~~~~~~~~~~~~~ +The render process brackets an ongoing calculation as a whole. It is not to be +confused with a operating system process or thread; rather it is a point of +reference for the relevant entities in the GUI and Proc-Layer in need to +connect to such a "rendering", and it holds the specific definitions for this +calculation series. A render process +_corresponds to a single data stream_ to be rendered. Thus, when the play + controller of some timeline in the model is +in _playing_ or _paused_ state, typically multiple corresponding render +processes exist. + +* there is an displayer- or output slot, which got allocated on creation + of the process +* the process disposes calculated data frames "into" this slot +* the process can be paused/started and stopped (aborted, halted). +* some processes allow for changing parameters dynamically (e.g. speed, + direction) +* each process has to ensure that the output/display slot gets closed or + released finally + +.Process parameters +A process is linked to a single stream data format (a -> +link:StreamTypeSystem.html[stream implementation type]). + +It is configured with _frame quantisation_ and _timings_, and a _model port_ +identifier and _channel selector_. + + quantisation:: + translates time values into frame numbers. (In the most general + case this is a function, connected to the session) + + timings:: + a definition to translate global model time units in real clock time, + including _alignment_ to an external _time grid_. + + model port:: + a point in the (high level) model where output can be produced. + + This might be a global pipe in one of the model's timelines, or + it might be a _probe point_. + + channel:: + within the session and high level model, details of the stream + implementation are abstracted. Typically, a global pipe (master bus + or subgroup) corresponds to a multichannel stream, and each of these + channels might be hooked up to an individual render process + (we have to work out if that's _always the case_ or just under + _some circumstances_) + + +[NOTE] +=================== +While certainly the port and channel definition is fixed, unfortunately the +quantisation and the timings are'nt. The timings may be changed in the middle +of an ongoing render process, due to changed playback speed, shuffling or +requirements forwarded from chase-and-lock synchronisation to an external +source. We still need to discuss if Lumiera is going to support variable +framerates (several media professionals I've talked to were rather positive we +need to support that -- personally I'm still in doubt we do). Variable +framerates force us to determine the frame numbers by an integration over time +from a start position up to the time position in question. The relevant data to +be integrated is located in the session / high-level model; probably we'll then +create an excerpt of this data, but still the less quantisation will be a +function of time. Anyway, it is the render processes job to translate all kinds +of parameter changes into relevant internal API calls to reconfigure the +calculation process to fit. +=================== + + + +Engine Model (low-level Model) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The low level model is a network of interconnected render nodes. It is created +by the build process to embody any configuration, setup and further +parametrisation derived from the high-level description within the session. But +the data structure of this node network is _opaque_ and considered an +implementation detail. It is not intended to be inspected and processed by +outward entities (contrast this to the high-level model within the session, +which provides an extensive discovery API and can be manipulated by model +mutating commands). We just provide a set of _query and information retrieval +functions_ to suit the needs of the calculation process. The engine model is +_not persisted._ + +* the engine model is partitioned by a _segmentation_ of the time axis. + Individual segments can be hot-swapped. +* the engine has _exit nodes,_ corresponding to the model ports mentioned above +* each exit node provides a stream type definition plus quantisation and + alignment constraints. + +Thus, for any pair (port, time) it is possible to figure out a segment and an +exit node to serve this position. The segmentation(s) for multiple ports might +differ. To allow for effective dispatching, the model should provide +convenience functions to translate these informations into frame number ranges. +The mentioned quantisation and alignment constraints stem from the fact that +the underlying media source(s) are typically themselves quantised and the +timings might be manipulated within the processing chain. We might or might not +be able to shift the underlying media source +(it might be a live input or it might be tied to a fixed timecode) + + + +Processing Node +~~~~~~~~~~~~~~~ +In this context, a node is a conceptual entity: it is an elementary unit of +processing. It might indeed be a single invocation of a _processor_ (plugin or +similar processing function), or it might be a chain of nodes, a complete +subtree, it might _represent_ a data source (file, external input or peer in +case of distributed rendering), or it might stand for a pipeline implemented in +hardware. The actual decision about these possibilities happened during the +build process and can be configured by rules. Information about these decisions +is retained only insofar it is required for the processing, most of the +detailed type information is discarded after the wiring and configuration step. +As mentioned above, each node serves two distinct purposes, namely to assist +with the planning and dispatching, and to pull data by performing the +calculations. + +Nodes can be considered _stateless_ -- pulling a node has no effect outside the +invocation context. While a node _might_ actually be configured to drive a +whole chain or subtree and propagate the pull request +_within_ this tree or chain internally, the node _never propagates a pull + request beyond its realm._ The pull() +call expects to be provided with all prerequisite data, intermediary and output +buffers. + + +Dispatching Step +~~~~~~~~~~~~~~~~ +The dispatcher translates a render process (actually a _calculation stream_ as +part of a render process) into sequences of node invocations, +which then can be analysed further (including planning the invocation of +prerequisites) and scheduled. This mapping is assisted by the engine model API +(to find the right exit node in the right segment), the render process (for +quantisation) and the involved node's invocation API (to find the +prerequisites) + + +Node Invocation API +~~~~~~~~~~~~~~~~~~~ +As nodes are stateless, they need to be embedded into an invocation context in +order to be of any use. The node invocation has two distinct stages and thus +the invocation API can be partitioned in two groups + +Planning +^^^^^^^^ +During the planning phase, the dispatcher retrieves various informations +necessary to _schedule_ the following pull call. These informations include + + * reproducible invocation identifier, usable to label frames for caching + * opaque source identifier (owned by the backed) in case this node + represents a source + * prerequisite nodes + * index (channel) of the prerequisite's output to be fed as input buffer(s) + * number and size of the output buffers required + * additional memory required + * control data frame(s) + + +Node pull +^^^^^^^^^ + * the pull call expects to be provided with all the resources announced during + the planning step + * moreover, the pull call needs to know (or some way to figure out) the time + coordinates + * after retrieving automation, the control flow forwards to the actual + processing function + * there is an result/error code (assuming the scheduler prefers error codes + over exceptions) + + +'''' + +Tasks +~~~~~ + * find out if we need to support variable framerate + ([green]#-> yes, implementation deferred#) + * find out about the exact handling of multichannel data streams ([green]#✔ done#) + * design and prototypical implementation of frame quantisation ([green]#✔ done#) + * design a buffer descriptor ([green]#✔ done#) + * design a buffer designation scheme [red]#TODO# + * expand on the node identification scheme [red]#TODO# + * clarify how control data frames can be addressed [red]#TODO# + + +Discussion +~~~~~~~~~~ + +Pros/Cons/Alternatives +^^^^^^^^^^^^^^^^^^^^^^ +Currently we're focussing on how to implement _this_ concept, not on +evaluating alternatives. Especially the idea of scheduling individual frame jobs +is a core concept of Lumiera. This RfC tries to bridge from the session model to +an engine based on these concepts. It's the attempt to link two concepts already +defined and decided on.... + + +Rationale +^^^^^^^^^ +* allow for optimal resource use and avoid blocking of threads +* shift away complexity from the engine into the builder, which is by far not + so performance critical +* allow to adjust the actual behaviour of the engine in a wide range, based on + actual measurements +* create a code structure able to support the foreseeable extensions (hardware + and distributed rendering) without killing maintainability + + + + +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: + + + + +Comments +-------- +//comments: append below + +.State -> Draft +Requirements and details of the design are sufficiently clear meanwhile. +Ther seems to be not much room for alternative approaches, given our +general planning for the application + + Mi 11 Mai 2011 19:27:12 CEST Ichthyostega + + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] + diff --git a/doc/devel/rfc/EngineInterfaceSpec.txt b/doc/devel/rfc/EngineInterfaceSpec.txt new file mode 100644 index 000000000..2b4be964e --- /dev/null +++ b/doc/devel/rfc/EngineInterfaceSpec.txt @@ -0,0 +1,234 @@ +Engine Interface Spec +===================== + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _Mi 11 Mai 2011 17:53:16 CEST_ +*Proposed by* Ichthyostega +------------------------------------- + +[abstract] +******************************************************************************** +The main service of the Renderengine is to deliver a stream of calculations +bound by timing constraints. The parameters of this delivery can be reconfigured +******************************************************************************** + +Try to start the integration and overall design of the Lumiera Render Engine. +Point of reference is the functionality other parts of the application are relying on. + +Description +----------- +//description: add a detailed description: +The Engine is driven by the Player subsystem and draws on the low-level Model +(Render nodes network) for all local parameters and control data. The goal is +to deliver all the typical playback and rendering operations commonly found +in NLE applications (functional requirements). Moreover, this functionality +shall be delivered in a robust and reliable fashion, while making optimal +use of the available I/O bandwidth and computation power (non-functional +requirements). + +Requirements Specification +~~~~~~~~~~~~~~~~~~~~~~~~~~ +.functional +- simple calculation stream +- with or without defined end point +- deliver to fixed output buffer(s) with high timing precision +- ability to deliver individual data chunks (e.g. single frames) +- ``free wheeling'' operation for maximum calculation throughput +- throttled operation using idle calculation or bandwidth resources +- streams with regular stepping and arbitrary block sizes +- looping, seamless chaining of calculation streams with ongoing timing +- ability to pause and to change / combine any of the above any time +- ability to abort or change, providing reliable feedback on completion +- ability to relocate (shift in time) parts of an ongoing calculation stream +- support for chase-and-lock synchronisation + +.non-functional +- protection against overload and deadlocks +- gracious degradation in case of problems +- maintain a configurable quality-of-service level +- utilise precisely the resources actually available + +Functionality description in detail +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-> see the link:EngineInterfaceOverview.html[Engine/Interface overview] for +a description of the involved entities and for definitions for common terms. + +Definitions +^^^^^^^^^^^ +Calculation Stream:: + A series of similar but parametrised calculations, + bound to deliver results in sequence and in accordance to timing constraints +Timed Delivery:: + Calculation result data has to be delivered within a fixed time interval + immediately preceding the delivery deadline, so it can be consumed by + an (possibly external) output process without the need for further buffering +Output Slot:: + An abstracted facility receiving calculated data; including a scheme + to organise the output buffers, which get handed over to an independent + thread running in parallel, or maybe even to an external process + +Operation description +^^^^^^^^^^^^^^^^^^^^^ +When *creating a calculation stream*, the _exit node_ and an already opened +_output slot_ are provided, plus the _timing parameters_ (frame duration, +stepping interval length, optional start and/or endtime) + +A *free wheeling calculation stream* is created in a similar fashion, just +without timing constraints on the output delivery; i.e. just the output slot +is parametrised differently. This invocation is used to create a ``mixdown'' +or ``final render'' to be saved into an output file. + +A *background rendering mandate* is created analogous, but _without_ providing +an output slot. Rather, it is expected that the engine will cache the generated +data internally. + +When calculation stream definitions are *chained*, the follow-up calculation +stream is expected to be delivered seamlessly after the preceding stream, +without interrupting the output timings. + +*Looping* is a special kind of chained calculations, where the same segment +is delivered continuously. But note, the loop boundaries are not necessarily +aligned with the frame spacing or the output timing requirements. + +For supporting *chase-and-lock*, the engine needs a mechanism to follow an +externally provided synchronisation goal, _without_ altering the output +delivery timings. Obviously, we need to build in a strategy for handling +this problem (because the solution is bound to be different for different +kinds of media). + +The engine can expect the output slot to support *de-clicking* or *flicker +protection* -- yet the engine needs to signal precisely when this is necessary + +Quality of service +^^^^^^^^^^^^^^^^^^ +The Engine is expected to provide different quality-of-service classes, +which are requested as part of the definition parameters for a calculation stream. + +- 'SYNC_PRIORITY' means to keep up to the delivery requirements, + even if this means failing to deliver data altogether. +- 'PERFECT_RESULT' means to deliver data perfect up to the definition, + even if this means violating the timing constraints. +- 'COMPROMISE' allows the engine to take some shortcuts in order to + deliver an roughly satisfactory behaviour. Likely there will be + multiple classes of compromise. + +The quality of service is partially implemented directly by the engine +and partially passed on as parameter to the individual node invocations. +For example, the engine might decide to switch down to proxy media, while +actually the node network will perform the actual switch and reconfiguration. + +The quality of service could be implemented as a strategy, to be consulted at +various decision points. The above cases would then be just some preconfigured +default strategies. + +Reconfiguration and abort +^^^^^^^^^^^^^^^^^^^^^^^^^ +There needs to be a separate ``control channel'' to cause various reconfigurations +during an ongoing calculation process. With the exception of the output slot, all +parameters defining an calculation stream might be changed on-the-fly -- including +the possibility to abort calculation altogether. + +The engine is _not required_ to react on such change requests immediately or +synchronously. The goal is rather to integrate such changes seamlessly. +Yet we _require_... + +- a guarantee that the change request is observed within some tolerance interval + (i.e. we may block waiting on the change to happen, without risking a deadlock) +- a reliable feedback _after_ the change has happened, by invoking a response signal + (functor/callback provided with the change request) +- a guarantee not to proceed with the original setup after this signalling + (read: after receiving this feedback, resources required only by the initial + setup may be deallocated) + +Especially note that the following things might be changed in the middle of an +ongoing calculation: + +- timing parameters of the calculation stream (frame durations, stepping interval) +- start and end time +- splitting and chaining of calculation streams (e.g introducing jumps) +- adjust the looping boundaries +- toggle _paused_ state +- change the exit node to use for pulling +- relocate the nominal time position of parts of the calculation stream; + especially we expect already calculated and cached data to be re-labeled +- invalidate parts of the (nominal) time axis, forcing recalculation +- abort individual calculation streams without interfering with others. + + +Tasks +~~~~~ +// List what needs to be done to implement this Proposal: +* analyse requirements of the player subsystem ([green]#✔ done#) +* determine further informations needed during calculation [,yellow]#WIP# +* find out about timing requirements and constraints in detaill [red]#TODO# +* define the interface functions in detail [red]#TODO# +* prepare a test fixture with mock-up calculations [red]#TODO# +* implement the invocation backbone with stubbed functionality [red]#TODO# + + +Discussion +~~~~~~~~~~ + +Pros +^^^^ +// add a fact list/enumeration which make this suitable: +// * foo +// * bar ... + + + +Cons +^^^^ +// fact list of the known/considered bad implications: +The requirements placed on life changes are quite high + + + +Alternatives +^^^^^^^^^^^^ +//alternatives: explain alternatives and tell why they are not viable: + + + +Rationale +--------- +//rationale: Give a concise summary why it should be done *this* way: +The expectations for the playback and render functionality of a NLE are +pretty much set. There isn't much room for reducing functionality. +So the goal for this RfC is to precisely define the inevitable +and break it down into tangible functionality on the implementation level. + + + +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: + + + + +Comments +-------- +//comments: append below + +Discussed in the May developers meeting. Seems to be basically acceptable. +_Cehteh_ proposed some small adjustments: + +- making the _QualityOfService_ rather a strategy to be queried +- treating the rescheduling a bit separate from the other changes, because + that is very common and needs to be performant. +- introducing a separate scheduler/queue for time scheduled tasks, like + with rater soft realtime requirements + + So 15 Mai 2011 00:55:24 CEST Ichthyostega + + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/FeatureBundle_PluggableModules.txt b/doc/devel/rfc/FeatureBundle_PluggableModules.txt new file mode 100644 index 000000000..06b9fe814 --- /dev/null +++ b/doc/devel/rfc/FeatureBundle_PluggableModules.txt @@ -0,0 +1,195 @@ +[grid="all"] +`------------`----------------------- +*State* _Parked_ +*Date* _2008-09-03_ +*Proposed by* link:Ichthyostega[] +------------------------------------- + + + +Describe pluggable modules by a "Feature Bundle" +------------------------------------------------ +This proposal builds upon Cehteh's Plugin Loader, which is the fundamental +mechanism for integrating variable parts into the application. + +It targets the special situation when several layers have to cooperate in order +to provide some pluggable functionality. The most prominent example are the +"effects plugins" visible for the user. Because, in order to provide such an +effect + + * the engine needs a processing function + * the builder needs description data + * the gui may need a custom control plugin + * and all together need a deployment descriptor detailing how they are + related. + + + + +Description +~~~~~~~~~~~ +The Application has a fixed number of *Extension Points*. Lumiera deliberately +by design does _not build upon a component architecture_ -- which means that +plugins can not themselves create new extension points and mechanisms. New +extension points are created by the developers solely, by changing the code +base. Each extension point can be addressed by a fixed textual ID, e.g. +"Effect", "Transition", .... + +Now, to provide a pluggable extension for such an Extension Point, we use a +*Feature Bundle* Such a Feature Bundle is comprised of + + * a Deployment Descriptor (provided as "structured data" -- TODO: define the + actual data format) + * the corresponding resources mentioned by this Deployment Descriptor + +The Deployment Descriptor contains + + * Metadata describing the Feature Bundle + - ID of the Extension point + - ID of the Bundle (textual ID) + - ID of origin / provider (could be a domain name) + - Category (textual, tree-like) + - Version number (major, minor) + - required Extension point version number (or Lumiera version no.?) + - Author name (utf8) + - Support email (utf8) + - textual description in a single line (utf8) + * A List of Resources, each with: + - ResourceID + - SubID + - Type of Resource, which may be + . Plugin + . Properties + . Script + . ...? + + - one of: + . the Resource provided inline in suitable quoted form (for textual + resources only) + . an URL or path or similar locator for accessing the Resource (TODO: + define) + - Additional Metadata depending on Type of Resource (e.g. the language of a + script) + + + +We do _not_ provide a meta-language for defining requirements of an Extension +Point, rather, each extension point has hard wired requirements for a Feature +Bundle targeted at this extension point. There is an API which allows code +within lumiera to access the data found in the Feature Bundle's Deployment +Descriptor. Using this API, the code operating and utilizing the Extension +Point has to check if a given feature bundle is usable. + +It is assumed that these Feature Bundles are created / maintained by a third +party, which we call a *Packager*. This packager may use other resources from +different sources and assemble them as a Feature Bundle loadable by Lumiera. Of +course, Lumiera will come with some basic Feature Bundles (e.g. for colour +correction, sound panning,....) which are maintained by the core dev team. +(please don't confuse the "packager" mentioned here with the packager creating +RPMs or DEBs or tarballs for installation in a specific distro). Additionally, +we may allow for the auto-generation of Feature Bundles for some simple cases, +if feasible (e.g. for LADSPA plugins). + + +The individual resources +^^^^^^^^^^^^^^^^^^^^^^^^ +In most cases, the resources referred by a Feature Bundle will be Lumiera +Plugins. Which means, there is an Interface (with version number), which can be +used by the code within lumiera for accessing the functionality. Besides, we +allow for a number of further plugin architectures which can be loaded by +specialized loader code found in the core application. E.g. Lumiera will +probably provide a LADSPA host and a GStreamer host. If such an adapter is +applicable depends on the specific Extension point. + +The ResourceID is the identifyer by which an Extension point tries to find +required resources. For example, the Extension Point "Effect" will try to find +an ResourceID called "ProcFunction". There may be several Entries for the same +ResourceID, but with distinct SubID. This can be used to provide several +implementations for different platforms. It is up to the individual Extension +Pont to impose additional semantic requirements to this SubID datafield. (Which +means: define it as we go). Similarly, it is up to the code driving the +individual Extension point to define when a Feature Bundle is fully usable, +partially usable or to be rejected. For example, an +"Effect" Feature Bundle may be partially usable, even if we can't load any + "ProcFunction" for +the current platform, but it will be unusable (rejected) if the proc layer +can't access the properties describing the media stream type this effect is +supposed to handle. + +Besides binary plugins, other types of resources include: + * a set of properties (key/value pairs) + * a script, which is executed by the core code using the Extension Point and + which in turn may access certain interfaces provided by the core for "doing + things" + +Probably there will be some discovery mechanism for finding (new) Feature +Bundles similar to what we are planning for the bare plugins. It would be a +good idea to store the metadata of Feature Bundles in the same manner as we +plan to store the metadata of bare plugins in a plugin registry. + + + + +Tasks +^^^^^ + + +Pros +^^^^ + + + +Cons +^^^^ + + + +Alternatives +^^^^^^^^^^^^ +Use or adapt one of the existing component systems or invent a new one. + + + +Rationale +~~~~~~~~~ +The purpose of this framework is to decouple the core application code from the +details of accessing external functionality, while providing a clean +implementation with a basic set of sanity checks. Moreover, it allows us to +create an unique internal description for each loaded module, and this +description data e.g. is what is stored as an "Asset" into the user session. + +Today it is well understood what is necessary to make a real component +architecture work. This design proposal deliberately avoids to create a +component architecture and confines itself to the bare minimum needed to avoid +the common maintenance problems. As a guideline, for each flexibility available +to the user or packager, we should provide clearly specified bounds which can +be checked and enforced automatically. Because our main goal isn't to create a +new platform, framework or programming language, it is sufficient to allow the +user to _customize_ things, while structural and systematic changes can be done +by the lumiera developers only. + + + + + + +Comments +-------- + +From a fast reading, I like this, some things might get refined. For example +I'd strongly suggest to make the Deployment Descriptor itself an Interface +which is offered by a plugin, all data will then be queried by functions on +this interface, not by some 'dataformat'. Also Resource ID's and a lot other +metadata can be boiled down to interfaces: names, versions, uuid of these +instead reiventing another system for storing metadata. My Idea is to make the +link:Plugin/Interface[] system self-describing this will also be used to +bootstrap a session on itself (by the serializer which is tightly integrated) + -- link:ct[] [[DateTime(2008-09-04T09:28:37Z)]] 2008-09-04 09:28:37 + +Parked +~~~~~~ +Needs to ne reviewed some time later. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/GitSubmoduleTransistion.txt b/doc/devel/rfc/GitSubmoduleTransistion.txt new file mode 100644 index 000000000..fdbcd69db --- /dev/null +++ b/doc/devel/rfc/GitSubmoduleTransistion.txt @@ -0,0 +1,87 @@ +[grid="all"] +`------------`----------------------- +*State* _Parked_ +*Date* _2008-04-09_ +*Proposed by* link:ct[] +------------------------------------- + + +Use Git Submodules to organize the project +------------------------------------------ +We planned this long time ago when the project started, this proposal is for to +work out the details and define a turnover point in time. + + +Description +~~~~~~~~~~~ +There is a git-filter-branch command which helps in doing the dirty work +isolating commits which touch certain dirs. This can moderately easily be used +to create a new repository with a rewritten history containing only sub parts +of the original history. + +The basic idea is that one developer who wants to works on a certain subsystem +clones the 'official' master and then updates and tracks only the development +state of a certain subsystem. + + +Tasks +^^^^^ + * what shall be in the master repository? + * boilerplate files, license, build infrastructure + * the _admin_ dir with supplemental scripts + * define which submodules shall be defined? + * _doc/devel_ + * _doc/user_ + * _wiki_ + * _uml_ + * _src/backend_ + * _src/proc_ + * _src/gui_ + * _src/lib_ + +Not yet decided: + * _tests_ move them into the _src/$subsystem_ as symlink? + * _src/tool_ + + +Pros +^^^^ + * better isolation of single subprojects + * one who is interested on one subproject can track a master and only + following certain subproject updates + * smaller/faster updates/downloads + + +Cons +^^^^ + * needs some more git-fu to be used by the developers + * we will host considerably more git repositories (bigger list in gitweb), + this is not a problem but might look more confusing + + + +Alternatives +^^^^^^^^^^^^ +Go as we do currently with one big repository per developer. The decision to +use submodules is not urgend and it can be transfered at any time. The +turnaround should just be planned and be scheduled to one day to minimize the +confusion and merging issues. + + +Rationale +~~~~~~~~~ +When all people get used to it it allows a cleaner more sane work flow and well +isolated, less conflicting commits. + + + + + +Comments +-------- + +We concluded that that submodules are not yet needed with exception for the +./doc folder. Parked for now. + -- ct 2008-07-26 09:09:57 + +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/MistakestoAvoid.txt b/doc/devel/rfc/MistakestoAvoid.txt new file mode 100644 index 000000000..4c671b8c8 --- /dev/null +++ b/doc/devel/rfc/MistakestoAvoid.txt @@ -0,0 +1,427 @@ +Design Process : Mistakes to avoid +================================== + +[grid="all"] +`------------`----------------------- +*State* _Dropped_ +*Date* _2008-04-21_ +*Proposed by* link:rick_777[] +------------------------------------- + + +Mistakes to avoid in the Lumiera design +--------------------------------------- + + +As a multimedia user and experienced programmer, I've found various flaws +present in open source Non Linear Video editors. Here I will list the problems +and their proposed (or mandatory) solutions. Please forgive me if some of the +ideas here have already been approved, I wrote this text before reaching this +wiki. + + +Description +~~~~~~~~~~~ + + +As a multimedia user and experienced programmer, I've found the following flaws +present in open source Non Linear Video editors (your mileage may vary) : + +. Frequent crashes (which most of the time make you lose your work) +. Reinventing the wheel for every new project +. Lack of a user-friendly (and extensible) UI +. Lack of support for certain video formats or codecs +. Lack of documentation +. Lack of cross-platform support +. Dependency on scripted languages like Python, which make installation a mess + +I will expand on the problems and their proposed (or mandatory) solutions. + + +1. Frequent crashes +~~~~~~~~~~~~~~~~~~~ + +[grid="all"] +`------------`------------------------------------------------------ +*Problem* Frequent Crashes and unsaved work. +*Severity* CRITICAL. +*Solution* Isolating the UI from the rendering and data handling (also + improves the extensibility) +*Required* Yes +*Workarounds* Auto-save (however it's not a real solution for the problem) +-------------------------------------------------------------------- + +Working with multimedia (video / audio) editing is a magnet for segfaults +(crashes) due to the handling of pointers and compression algorithms. A bug in +a plugin (like in Audacity's low-pass filter) will crash and you suddenly +realize you lost your work - unless you have an auto-save feature, but that +doesn't go to the root of the problem. + +My proposal is to move the low-level handling of video to a separate process, +which then will do the processing - if it crashes, the UI will only report an +error with a dialog (i.e. "the process crashed. Try again?"), but you work will +stay safe. I'm not sure of the implementation difficulties that arise from +having a shared memory buffer for rendering / processing, but one thing is +certain: Whenever you move the cursor or rewind a part of a clip in your +resources, the application isn't supposed to crash. Just moving the cursor +isn't a time-critical task, so perhaps we can use temporary files for this. +It's safer if you're not doing the final rendering. + + +Comments +^^^^^^^^ + +I am not sure yet about separating things into processes, generally it is clear +that this would be more robust but there are some performance impacts and +programming problems (massisve amounts of data in shared memory). But most +importantly, when a subprocess gets a job and crashes on it, it won't complete +the job, we don't have a choice except gracefully abort it. From a user +perspective "It doesn't work!" there is no much difference to a complete crash. +Well and yes we aim to make it crash proof rather, crashes a bugs and have to +be fixed, point. + +Lumiera will never ever loose work, we don't plan to make a project file, +autosafe way. Lumiera will keep projects in an internal database like format +which consists of a Dumpfile and a contingous written logfile. After a +crash/powerdown whatever, this log just gets replayed. The advantages are +countless, imagine persistent, selective undo and so on. Any other format +(cinelerra2 XML, MXF, ...) will be realized by importer/exporter plugins. + -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] + + + +2. Reinventing the wheel for every new project +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +[grid="all"] +`------------`------------------------------------------------------ +*Problem* Various projects compete and reinvent the wheel +*Severity* Serious (Slows down development time. A lot) +*Solution* Multi-tier design, turn the data handling into a backend and use + whatever UI you prefer +*Required* Yes. Better now that the project hasn't started +--------------------------------------------------------------------- + +Imagine the Linux kernel was tied to the window manager. You would have to +stick with KDE or GNOME and you couldn't improve it! Fortunately it's not like +that for Linux, but it is for some projects. If you want to change the user +interface from QT to wxWidgets or GTK you'll need to rewrite every module. + +If you separate the UI from the project handling engine, you can simply fork +the project and change the UI to one that supports skinning, without having to +do the complicated video-processing stuff. + +Separating the processes has an equivalent for web programming, it's called +"separation of concerns", or multi-tier design. When you suddenly change the +database engine, you don't need to change the whole program, just the database +module. Same goes for changing the UI from HTML to XML or Flash. If they're +separate modules that only communicate through a clearly defined API. + +Example case 1: The Code::Blocks IDE. The compiling engine supports various +compilers, and the engine itself is only a plugin for the main editor. If the +compiler crashes, you only get an error, but the IDE doesn't crash (unless it's +the UI part that's doing something wrong). + +Example case 2: Chessmaster. The user interface and speech synthesis stuff only +call the chess engine, called "theking.exe". Linux chess games also depend on +an engine to do the thinking. + +So I suggest to split the project into four separate tiers (not necessarily +processes): + +. User interface - communicates with the "project" tier, handles the user + events and does the calls. +. The project tier - the main part of the video editor. This one invokes the + renderer and decides which effects to apply, saving them as mere parameters + for later processing. It also tells you where the current pointer for the + track view is. Also calls the rendering engine for the current frame, or for + previews of a certain special effect. Note that if this process keeps running + even if the GUI crashes, later we can restart the GUI and keep working. +. The rendering engine - This one must be a separate process for the reasons + stated in problem #1. This also gives us the advantage that it can work on + the background while we keep working on the project (after all the project is + just a set of data stating which effects to apply to which tracks, and which + files are used for the tracks) - instead of just having a window saying + "Rendering, please wait". Even Adobe Premiere Pro suffered from this problem. + This means that if we put enough effort, we can surpass commercial software + in certain areas. Note that the rendering engine uses the same API than the + project tier, as it works on a copy of the project when doing the final + rendering. +. The video processing wrapper, which has interfaces for different video + processing toolkits (DirectX, GStreamer, etc). This also makes the project + cross-platform. Tiers 1 and 2 can go in one process, and the 3 and 4 in + another (this would make tier 2 a library which defines a C++ Class, and tier + 4 would also be a library which is used by the rendering engine). + +By separating the tiers, these can later become their own projects and overall +the community would receive great benefits. + + +Comments +^^^^^^^^ + +Please look at our design drafts, things will be separated (little different +than you describe here). We reuse things which are benefitful (gavl, ffmpeg, +..) but we are also aware that we reinvent the wheel for some things by +intention. Lumieras goal is not just to glue some existing libraries together +under a new gui, there are already a lot projects trying this way. We rather +aim for a ''Professional'' high performance Video editing solution which does +some things in a different (maybe more complex) way. We do not use existing +frameworks like MLT or gstreamer because we believe that these do not fit our +goals (gstreamer will be supported through plugins). We do not produce yet +another multimedia framework library (this only happen by coincidence) to be +used by others. + -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] + + +3. Lack of a user-friendly and extensible UI. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +[grid="all"] +`------------`------------------------------------------------------ +*Problem* Often, editors provide a very poor and buggy interface. + Examples: Jahshaka doesn't even provide tooltips for the various tools, and + the documentation is poor; In Cinelerra I've noticed some bugs when using the + open dialog, I'd rather have the KDE one, thanks. +*Severity* From Annoying to Serious. +*Solution 1* Use a library that allows you to use different widget + libraries, like wxWidgets. +*Required* Recommended, but not obligatory. +*Solution 2* Write different user interfaces, but they'd be hard to maintain. +*Required*, No. +--------------------------------------------------------------------- + +This problem is complicated, we need a good framework for handling the tracks. +Perhaps this could become a separate project. Ideas are welcome. + + +Comments +^^^^^^^^ + +Joel started working on a GUI recently and making good progress. The UI should +finally be quite flexible as it mostly provides a skeletion where plugins +render to. We have quite a lot ideas about the UI and user input is welcome. +The UI is currently the most separate tier in the design, i'd like to make it a +plugin itself which is loaded when lumiera is started in a gui mode, but it is +to early to say how exactlly it will be integrated, except that we all agree +that GUI is optional and Lumiera can also run headless, script driven. + -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] + + +4. Lack of support for certain video formats or codecs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +[grid="all"] +`------------`------------------------------------------------------ +*Problem* Lack of support for certain video formats or codecs. +*Severity* Critical. +*Workarounds* 1. Give a help page for the user to do his own conversion, but + this is very inelegant, annoying, and a waste of time. 2. Provide conversion + on the fly, and keep a separate "preprocessed" copy of the imported clip in a + separate directory. This is a nice middle ground, IMHO. +*Solution* Use a wrapper library as stated in problem # 2, having a + plugin-based design is recommended. +*Required* Yes. +--------------------------------------------------------------------- + +Some editors like Cinelerra are hardwired into using one format, or have a +phobia to certain formats / codecs (i.e. DivX AVI's). If we separate the +project editing engine from the video handling libraries, we can use unknown +formats by simply providing an input/output plugin. This would allows us to use +files encoded with lossless codecs like +http://lags.leetcode.net/codec.html[Lagarith]. This also provides forward +compatibility for future formats. + + +Comments +^^^^^^^^ + +Lumiera is a video editor we don't care (*cough*, not really true) about video +formats. Everything which comes In and goes Out is defined in plugins which +handle video formats. We currently decided to use 'gavl' because it is a nice +small library which does exactly what we want. Later on gstreamer and other +such kinds of decoder/encoder/processing-pipe libs will be realized. + -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] + + +5. Lack of documentation +~~~~~~~~~~~~~~~~~~~~~~~~ + +[grid="all"] +`------------`------------------------------------------------------ +*Problem* Some video editors have very poor documentation (and that's an + understatement *cough* Jahshaka *cough* ) +*Severity* Critical. +*Solution* Have a team for the documentation. +*Required* Yes. +--------------------------------------------------------------------- + +Nuff said. + + +Comments +^^^^^^^^ + +Quote from Ohloh.net: (http://www.ohloh.net/projects/lumiera)[] + +------------------------------------------------------------ +Extremely well-commented source code + +Lumiera is written mostly in C++. Across all C++ projects on Ohloh, 23% of all +source code lines are comments. For Lumiera, this figure is 46%. This very +impressive number of comments puts Lumiera among the best 10% of all C++ +projects on Ohloh. +------------------------------------------------------------ + + +Nuff saied... Oh well, about user docs we like to get that impressive ratings +there too, any helpers? + -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] + + +6. Lack of cross-platform support +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +[grid="all"] +`------------`------------------------------------------------------ +*Problem* Where's my Windows version? +*Severity* Blocker +*Solution* Use a cross-platform toolkit for the UI. +*Required* Depends, do you plan to make it Cross-Platform? +-------------------------------------------------------------------- + +A good example for this is the Code::Blocks IDE, which was thought of being +cross-platform from the beginning. Curiously, at first the project was +Windows-only, and its only F/OSS alternative was Dev-C++ from Bloodshed (eew). +Otherwise you'd have to stick with proprietary applications like Visual C++. + +In Linux there were various IDE's, but they were Linux-only. Since Code::Blocks +uses a cross-platform toolkit (wxWidgets), it can be compiled either in Windows +and Linux. There are RPM's for various distros now that the first public +version (8.02) got out. I've heard that QT is also cross-platform, but I +haven't tried it yet. + +Of course - if you separate the UI from the project engine, someone could make +his own Windows UI for the project. Now what needs to be taken care of, is that +the rendering libraries are cross-platform too. + + +Comments +^^^^^^^^ + +We refuse to make it cross platform intentionally. Most things are written +portable, POSIX compatible, some might need platform specific fixes. But our +target is primary Linux (because thats what we use) secondary any other Free OS +(hopefully we find some testers/maintainers for that). Lumiera ''might'' run on +OSX and patches will be accepted, but it is not a free platform so we don't +care by ourself. Windows due its diffrent system interfaces will be hard to +port, if someone wants to do that, have fun, we will accept patches to, but we +do not support it in *any* way by ourself. + -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] + + +7. Dependency on scripted languages like Python, which make installation a mess +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +[grid="all"] +`------------`------------------------------------------------------ +*Problem* Installation can be a mess if we depend on scripted languages. +*Severity* Annoying, the end user might just conform with another project + that "just works". +*Solution* Make it in C++ or other easily-compilable language. +*Required* VERY recommended. +--------------------------------------------------------------------- + +I've had to install several packages for my distro (whose repository is not as +large as others like Ubuntu's) from source. Some of them depend on very +esoteric scripted languages which I also need to install. And then the +libraries, etc. My suggestion is to free the end user from this burden, and +work on a common language, like C++. + + +Comments +^^^^^^^^ + +At some point a scripting language ''will'' be required, yet to drive the +testsuite, make headless rendering work and so on. We need to provide +installation instructions and/or even bundle this language with Lumiera. This +will likely become a small embedded language like Lua or some kind of forth (or +maybe some scheme?) it should not depend on strange modules which are not part +of the core scripting language distribution (or we shall provide them too), +needs to be worked out. + -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] + + + +Author's comments +^^^^^^^^^^^^^^^^^ + +Some of the measures stated in this document are optional, but separating the +processes for the rendering engine, editor and User Interface are the optimal +solution and required to avoid common problems. + + +Discussion +---------- + +Mostly we agree with the general statements in this Design Entry. But there are +some points which don't stand the test of a detailed technical discussion. For +example, you simply can't state it's a 'mistake' not to write code which +similarly runs on windows and *nix. Well. You could try to write it in Java. +See my point? While today it's quite feasible to write office stuff or banking +applications in a cross-platform manner, a video editor still is a different +kind of a beast. + +A similar argumentation holds true for the question, wether or not to use +separate processes and IPC. While it certainly is a good idea to have the X +server or a database running in a separate process, the situation is really +quite different for editing video. Hopefully it's clear why. + +Could you please rework this Design Entry in a way that we can finalize +(accept) it? + +* Please remove the section about windows +* Please separate out things needing technical discussion and are not just + "mistakes", thus retaining only the big picture statements (on which we all + agree) +* How to secure the application against crashes +* If it is viable/desirable to run the gui in a separate process really needs + in-depth technical discussion (create a new Design Entry for this) +* How to deal with the dependencies problem in combination with + plugins/extensions and script languages + -- link:Ichthyostega[] [[DateTime(2008-10-05T01:51:50Z)]] + + +Conclusion +---------- +The October.2008 dev meeting decided to 'drop' this design proposal as is. + +Basically, this text just tells us "to make Lumiera good", and especially it +contains a mixture of topics + +* We fully agree to 80% of the statements made there, but we think those + statements are so very basic and self-evident as to be considered off-topic + here. We are aware of the recurring problems with open source video editing. + That's why we are here. +* The proposal draws conclusions on two technically substantial points, at + which we don't agree. And it fails to provide sufficient (technically sound) + arguments to prove these statements. + +While it is certainly 'desirable' to be cross-platform as much as possible and +especially '''target Microsoft Windows''', we don't see much possibilities with +today's mainstream technology to build an application which is as +technologically demanding as a video editor is. We would end up developing two +or even three sister applications, or we are forced to sacrifice performance +for portability. When put up to face such options, we have a clear preference +to concentrate on a really free and open platform. + +While it is certainly 'desirable' to make the application as robust as +possible, we don't see how '''using multiple separate processes''' could help +us with this goal ''without creating major scalability or performance +problems'' due to the use of shared memory. And, yet more important: we don't +share the basic assumption made in the proposal, namely that video processing +is inherently dangerous. We think the basic algorithms involved are +sufficiently well-known and understandable to implement them in a sound manner. + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/NormalizedDeviceCoordinates.txt b/doc/devel/rfc/NormalizedDeviceCoordinates.txt new file mode 100644 index 000000000..b594cc3b2 --- /dev/null +++ b/doc/devel/rfc/NormalizedDeviceCoordinates.txt @@ -0,0 +1,112 @@ +[grid="all"] +`------------`----------------------- +*State* _Parked_ +*Date* _2009-01-14_ +*Proposed by* link:ct[] +------------------------------------- + + +Normalized Device Coordinates +----------------------------- + +AkhIL pointed me out to some blender problem and how renderman fixes that. We +should use this too. + + + + +Description +~~~~~~~~~~~ + +Just snippet from IRC log: + +------------------------------------------------------------ +[15:09] and I hope lumiera will use some resolution independend + measuring for all parameters +[15:09] one can rotate where the node actually sits +[15:09] like NDC +[15:09] or pass transistions through the renderpipe, make all effects + transisition aware and apply them at the end +[15:10] the later is better but needs more efforts and some rethinking +[15:10] we will prolly support both in lumiera :) +[15:11] in renderman's NDC for horizontal image with 4:3 aspect ration + (-1.33,-1) is lower-left corner and (1.33,1) upper-right +[15:11] ah +[15:11] so moving to different resolutions and different aspect ratios + in renderman makes no problems +[15:11] well good point, we will measure in pixel but need to convert + between them . using a float would be good to address pixels +[15:12] yes +[15:12] what stands NDC for? +[15:13] Normalized Device Coordinates +[15:14] ok +[15:14] so from -1 to 1 is a range by smallest image size +[15:15] yes sounds reasonable +[15:15] * cehteh adds a note to the lumiera design docs +[15:15] so far we dont do anything where it matters .. but that will + come +[15:16] when you move some logo to (0.8,-0.8) it will stay on screen + even when you chenge resolution and image aspect ratio +[15:17] all input images should be scaled to this range (-1,1) by + smalles side + +------------------------------------------------------------ + + + +Tasks +^^^^^ + + + + +Discussion +~~~~~~~~~~ + +Pros +^^^^ + + + +Cons +^^^^ + + + +Alternatives +^^^^^^^^^^^^ + + + +Rationale +~~~~~~~~~ +TBD + + + + + +Comments +-------- + +One issue where I always assumed we'd need to define something of this sort is +for proxy editing. Especially this is a problem in conjunction with masks. +Basically, this means a bit more of "vector graphics". With film/video editing, +this was rather unusual, but with the advent of more and new digital video/film +formats it gets more and more important. Also, our considerations regarding +time handling and quantisation to single frames somewhat fit into this line of +thought. Up to now, rather the standard way of thinkin was to use a "project +framerate" and a fixed resolution in pixels. But we certainly can do better. + + -- Ichthyostega 18:09:50 + + +Parked +~~~~~~ +deferred for later, generally accepted. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + + + +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/OfficialAssemblyLanguage.txt b/doc/devel/rfc/OfficialAssemblyLanguage.txt new file mode 100644 index 000000000..a60444951 --- /dev/null +++ b/doc/devel/rfc/OfficialAssemblyLanguage.txt @@ -0,0 +1,161 @@ +Design Process : Official Assembly Language +=========================================== + +[grid="all"] +`------------`----------------------- +*State* _Dropped_ +*Date* _2008-08-01_ +*Proposed by* link:PercivalTiglao[] +------------------------------------- + + +Official Assembly Language +-------------------------- + +I describe here an optimization that might have to be be taken into account at +the design level. At very least, we should design our code with +auto-vectorization in mind. At the most, we can choose to manually write parts +of our code in assembly language and manually vectorize it using x86 SSE +Instructions or !PowerPC !AltiVec instructions. By keeping these instructions +in mind, we can easily achieve a large increase in speed. + + +Description +~~~~~~~~~~~ + +While the C / C++ core should be designed efficiently and as portable as +possible, nominating an official assembly language or an official platform can +create new routes for optimization. For example, the x86 SSE instruction set +can add / subtract 16 bytes in parallel (interpreted as 8-bit, 16-bit, 32-bit, +or 64-bit integers, or 32-bit/64-bit floats), with some instructions supporting +masks, blending, dot products, and other various instructions specifically +designed for media processing. While the specific assembly level optimizations +should be ignored for now, structuring our code in such a way to encourage a +style of programming suitable for SSE Optimization would make Lumiera +significantly faster in the long run. At very least, we should structure our +innermost loop in such a way that it is suitable for gcc's auto-vectorization. + +The problem is that we will be splitting up our code. Bugs may appear on some +platforms where assembly-specific commands are, or perhaps the C/C++ code would +have bugs that the assembly code does not. We will be maintaining one more +codebase for the same set of code. Remember though, we don't have to do +assembly language now, we just leave enough room in the design to add +assembly-level libraries somewhere in our code. + + +Tasks +~~~~~ + +* Choose an "Official" assembly language / platform. +* Review the SIMD instructions avaliable for that assembly language. +* For example, the Pentium 2 supports MMX instructions. Pentium 3 supports MMX + and SSE Instructions. Early Pentium4s support MMX, SSE, and SSE2 + instructions. Core Duo supports upto SSE4 instructions. AMD announced SSE5 + instructions to come in 2009. +* Consider SIMD instructions while designing the Render Nodes and Effects + architecture. +* Write the whole application in C/C++ / Lua while leaving sections to optimize + in assembly later. (Probably simple tasks or a library written in C) +* Rewrite these sections in Assembly using only instructions we agreed upon. + + +Pros +~~~~ + +Assuming we go all the way with an official assembly language / platform... + +* Significantly faster render and previews. (Even when using a high-level + library like http://www.pixelglow.com/macstl/valarray/[macstl valarray], we + can get 3.6x -- 16.2x the speed in our inner loop. We can probably expect + greater if we hand-optimize the assembly) + + +Cons +~~~~ + +* Earlier architectures of that family will be significantly slower or + unsupported +* Other architectures will rely on C / C++ port instead of optimized assembly +* Redundant Code + + +Alternatives +^^^^^^^^^^^^ + +* We only consider auto-vectorization -- GCC is attempting to convert trivial + loops into common SSE patterns. Newer or Higher level instructions may not be + supported by GCC. This is turned on + http://gcc.gnu.org/projects/tree-ssa/vectorization.html[in GCC4.3 with + specific compiler flags] +* We can consider assembly but we don't officially support it -- We leave the + holes there for people to patch up later. Unofficial ports may come up, and + maybe a few years down the line we can reconsider assembly and start to + reimplement it down the road. +* Find a SIMD library for C/C++ -- Intel's ICC and + http://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Vector-Extensions.html[GCC] both + have non-standard extensions to C that roughly translate to these + instructions. There is also the + http://www.pixelglow.com/macstl/valarray/[macstl valarray library] mentioned + earlier. Depending on the library, the extensions can be platform specific. +* Write in a language suitable for auto-vectorization -- Maybe there exists + some vector-based languages? Fortran might be one, but I don't really know. + + +Rationale +~~~~~~~~~ + +I think this is one of those few cases where the design can evolve in a way +that makes this kind of optimization impossible. As long as we try to keep this +optimization avaliable in the future, then we should be good. + + +Comments +-------- + +* I have to admit that I don't know too much about SSE instructions aside from + the fact that they can operate on 128-bits at once in parallel and there are + some cache tricks involved when using them. (you can move data in from memory + without bringing in the whole cache line). Nonetheless, keeping these + assembly level instructions in mind will ease optimization of this Video + Editor. Some of the instructions are high-level enough that they may effect + design decisions. Considering them now while we are still in early stages of + development might prove to be advantagous. Optimize early? Definitely not. + However, if we don't consider this means of optimization, we may design + ourselves into a situation where this kind of optimization becomes + impossible. + +* I don't think we should change any major design decisions to allow for + vectorization. At most, we design a utility library that can be easily + optimized using SIMD instructions. Render Nodes and Effects can use this + library. When this library is optimized, then all Render Nodes and Effects + can be optimized as well. -- link:PercivalTiglao[] + [[DateTime(2008-08-01T16:12:11Z)]] + +* Uhm, the Lumiera core (backend, proc, gui) doesn't do any numbercrunching. + This is all delegated to plugins (libgavl, effects, encoders). I think we + don't need any highly assembler/vector optimized code in the core (well, lets + see). This plugins and libraries are somewhat out of our scope and thats good + so, the people working on it know better than we how to optimize this stuff. + It might be even worthwile to try if when we leave all vectorization out, if + then the plugins can use the vector registers better and we gain overall + performance! + -- link:ct[] [[DateTime(2008-08-03T02:27:14Z)]] + +* Another idea about a probably worthwhile optimization: gcc can instumentate + code for profileing and then do arc profileing and build it a second time + with feedback what it learnd from the profile runs, this mostly affects + branch prediction and can give a reasonable performance boost. If somone + likes challenges, prepare the build system to do this: +. build it with -fprofile-arcs +. profile it by running ''carefully'' selected benchmarks and tests. +. rebuild it again this time with -fbranch-probabilities +. PROFIT + -- link:ct[] [[DateTime(2008-08-03T02:27:14Z)]] + +* I've discussed general ideas around, and I agree now that "core Lumiera" is + not the place to think of these kinds of optimizations. So I'll just move + this over to dropped. -- link:PercivalTiglao[] + [[DateTime(2008-08-04T18:33:58Z)]] + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/RefactorLiblumieraOut.txt b/doc/devel/rfc/RefactorLiblumieraOut.txt new file mode 100644 index 000000000..c31fb06a2 --- /dev/null +++ b/doc/devel/rfc/RefactorLiblumieraOut.txt @@ -0,0 +1,240 @@ +Refactor Liblumiera Out +======================= + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _Fr 22 Apr 2011 10:46:50 CEST_ +*Proposed by* Christian Thaeter +------------------------------------- + +[abstract] +******************************************************************************** +liblumiera contains alot useful and reuseable code which is already in use by +other projects +******************************************************************************** + +Description +----------- +//description: add a detailed description: +Over the time we've put some efforts into the liblumiera. I've added +some from my code which predates the lumiera project which I am using +on many other projects. This now caused that I maintain this sources in +different unrelated projects and have to cross merge and update stuff +when I do updates and fixes somewhere. I think its time to factor the +reuseable parts out into a independent library (like glib does for +gtk), in fact I had this plan long ago. + + +.What parts are eligible for a standalone library + +Anything which is something tool alike and useful for other projects and not +tied to Lumiera only. This are the algorithms/datastructures, allocators, tool +macros. Additionally some of the src/common things should be moved into the +library. I give some lists below. + +.How to name it + +Long time ago my plan was to name it 'ctlib' or 'cehlib' but meanwhile there is +enough code done by others. So I'd propose a more neutral name, still +'lumieralib' or 'lulib' would be approbiate. The only thing we have to account +for is that some parts which are too specific for Lumiera and should not be +integrated into this spinoff need either to stay in a lumiera-internal lib +(src/lib/) as currently or being moved to the respective subsystems using them +(src/backend, src/proc, src/common, ...), so the names should not clash. + +.C, C++ ... + +For myself I need the C parts, while there is C++ code which interfaces to the +C implementations and also a lot code which does nice C++ things on its own. +This possibly means that we should in fact make 2 packages out of this, one C +and one C++ library (where the C++ part is more than just the wrappers, but +also the tools and tricks which are currently in src/lib/ and reuseable). + +.Who maintains it + +Despite a spin of I think we don't want to change anything from our current +practice and maintain it by the Lumiera developers. For many parts I feel +responsible for it, but its really a part of the Lumiera codebase, despite +independently useable. + +.How to maintain it + +We need to decide about build system and documentation system. As build system +we may right start using scons. For documentation the situation is a but +different since some of my code uses pipadoc/asciidoc and other uses doxygen. + +.What not to do + +Some of the code is currently quite specific to Lumiera while it could be made +more generic. This is *NOT* subject of this RFC we may or may not do such a +refactoring but this RFC and any work resulting from this should only restrict +to simple things like necessary namespace and variable renaming and integration +in the build system. + + +C Parts +------- + +Library +~~~~~~~ +What belongs to the library + +Containers +^^^^^^^^^^ + * cuckoo hashing (cuckoo.c|h) + * linked lists (llist.h slist.h) + * cache lists (mrucache.c|h) + * splay trees (psplay.c|h) + * priority queues (not done yet) + +Runtime tools +^^^^^^^^^^^^^ + * error handling (error.h error.c) used by the other facilities too + * clib convinience wrapers (safeclib.c|h) needs better name, maybe refactor + into new facilities + +Multithreading +^^^^^^^^^^^^^^ + * locking, condition variables etc. (condition.c|h (rec)mutex.c|h, rwlock ...) + +Memory management +^^^^^^^^^^^^^^^^^ + * Memory pools (mpool.c|h) + * Temporary buffers (tmpbuf.c|h) + +Metaprogramming +^^^^^^^^^^^^^^^ + * preprecessor tools (ppmpl.h) move common preprocessor macros here + * polymorphic call helper for C (vcall.h) + +Interface system and module loader +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +except for some hardcoded references to 'lumiera_org' and '.lum' plugin names +this is quite generic, possibly moving this over could be postponed, but might +eventually be done. + +From 'src/common' +------ +interface.c interfacedescriptor.h interface.h interfaceproxy.cpp +interfaceregistry.c interfaceregistry.h plugin.c plugin_dynlib.c plugin.h +------ + + +The 'config' system could become a candidate too if it ever gets finished and +proved useful, but for the time being its better kept in Lumiera. + + +Not Library +~~~~~~~~~~~ +Too specific to Lumiera: +----- +luid.c luid.h time.h +----- + + +C++ Parts +--------- + +For most of the C++ parts I am not sure, ichthyo should decided upon these +(please edit this here) + +Library +~~~~~~~ +These look 'generic' or wrap the C parts: +------ +singleton-factory.hpp singleton.hpp singleton-policies.hpp +singleton-preconfigure.hpp singleton-ref.hpp singleton-subclass.hpp +sync-classlock.hpp sync.cpp sync.hpp thread-local.hpp +typed-allocation-manager.hpp typed-counter.hpp util.cpp util-foreach.hpp +util.hpp variant.hpp +------ + +Not Sure +~~~~~~~~ +------ +access-casted.hpp advice advice.hpp allocation-cluster.cpp +allocation-cluster.hpp bool-checkable.hpp cmdline.cpp cmdline.hpp del-stash.hpp +diagnostic-context.hpp element-tracker.hpp error.hpp (currently too +lumiera specific) exception.cpp (as before) factory.hpp format.hpp +frameid.hpp functor-util.hpp handle.hpp hash-indexed.hpp iter-adapter.hpp +iter-adapter-stl.hpp iter-source.hpp itertools.hpp iter-type-binding.hpp +lifecycle.cpp lifecycleregistry.hpp lumitime-fmt.hpp lumitime.hpp +multifact-arg.hpp multifact.hpp meta/* null-value.hpp observable-list.hpp +opaque-holder.hpp optional-ref.hpp p.hpp query.cpp query.hpp ref-array.hpp +ref-array-impl.hpp result.hpp scoped-holder.hpp scoped-holder-transfer.hpp +scoped-ptrvect.hpp searchpath.cpp searchpath.hpp sub-id.hpp symbol.hpp +symbol-impl.cpp visitor-dispatcher.hpp visitor.hpp visitor-policies.hpp +wrapper.hpp wrapperptr.hpp appstate.cpp appstate.hpp basic-setup.cpp +basic-setup.hpp DIR_INFO external guifacade.cpp instancehandle.hpp option.cpp +option.hpp query subsys.cpp subsys.hpp subsystem-runner.hpp +---- + + +Not Library +~~~~~~~~~~~ +------ +logging.cpp nobug-init.cpp nobug-init.hpp streamtype.cpp streamtype.hpp test/* +time/* time.cpp tree.hpp +----- + +Tasks +~~~~~ +// List what needs to be done to implement this Proposal: +// * first step ([green]#✔ done#) + + * decide on name, namespaces [,yellow]#WIP# + * create git repository, setup boilerplace (build system, legalese) + [,yellow]#WIP# + * move all code over into the git repos, refactor (namespace renames) () + [,yellow]#WIP# + * make Lumiera use the new lib [,yellow]#WIP# + +Discussion +~~~~~~~~~~ + +Pros +^^^^ +// add a fact list/enumeration which make this suitable: + + * I am already reuse much of its code, making it independent makes maintaining + it less burden + + +Cons +^^^^ +// fact list of the known/considered bad implications: + + * new packages, new dependencies for Lumiera instead 'batteries included' + +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 + + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/RenderOptimizer.txt b/doc/devel/rfc/RenderOptimizer.txt new file mode 100644 index 000000000..74604e77e --- /dev/null +++ b/doc/devel/rfc/RenderOptimizer.txt @@ -0,0 +1,100 @@ +[grid="all"] +`------------`----------------------- +*State* _Parked_ +*Date* _2007-06-07_ +*Proposed by* link:ct[] +------------------------------------- + +Render Optimizer +---------------- +Render only parts of a frame which are necessary for the Output; Optimize +render pipeline for efficiency + + +Description +~~~~~~~~~~~ +This Idea is just stored here for later reference/implementation. + +Effects give some information on which data their output depends (like +transitions, temporal dependencies, color/alpha etc) and what the operation +costs. Based on this information we optimize the render pipeline, for example +if the output is a zoom, then we only need to calculate the parts of a frame +which will be viewable in the output (plus some more dependencies, like blur +has radius and so on). Further in some cases it might be favorable to reorder +some effects for the actual render process, as long it would produce the same +output as the original sequence of effects. + + + + + +Tasks +^^^^^ + + + + +Discussion +~~~~~~~~~~ + +Pros +^^^^ + + + +Cons +^^^^ + + + +Alternatives +^^^^^^^^^^^^ + + + +Rationale +~~~~~~~~~ + + + + + + +Comments +-------- + +Possible classification for video filters: + 1. The filter only changes the color of each pixel in the same way + 2. The filter deforms the image but leaves the color + 3. The filter makes complex things. The only additional hint it can export is + the + number of referenced past frames, if such a limit exists (sometimes it + doesn't). + +Filters of type 1 and type 2 never use any previous frames, and are strictly +one frame in - one frame out. Filters of type 1 can always be swapped with +filters of type 2, the output is the same. All other filters cannot be swapped +in general. + +The good news is, that: + + 1. All commonly used filters are either type 1 or type 2 + (type 3 are more the fun effects) + 2. Filters of type 2 are colormodel agnostic + 3. If a filter of type 1 makes only linear transformations of the color + vectors (new_color = matrix * old_color), + the matrix can be transformed from e.g. RGB to YUV, so these filters can + always work in both colorspaces directly + + +Parked +~~~~~~ +Generally this is accepted but needs some more polishing when we go over it. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + + + + + +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/ResourceManagementBudgeting.txt b/doc/devel/rfc/ResourceManagementBudgeting.txt new file mode 100644 index 000000000..ffa488e5c --- /dev/null +++ b/doc/devel/rfc/ResourceManagementBudgeting.txt @@ -0,0 +1,121 @@ +Resource Management: Budgeting +============================== + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _Fri Jul 23 20:33:32 2010_ +*Proposed by* Christian Thaeter +------------------------------------- + +[abstract] +****************************************************************************** +The Profiler will give some Idea about how much Resources can me used to +optimally utilize the system. Knowing this number leads to the next challenge, +distributing the resources to different subsystems, jobs and objects. I here +introduce a budgeting system which takes care for this. +****************************************************************************** + + +Description +----------- +//description: add a detailed description: + +The idea is quite simple, for each kind of resource we have a global budget +manager which accounts for the available and used amounts of this resource. + +Each user of a resource has its own account managing his share on the resource. + +The system is completely voluntary giving only hints how much of a resource is +available for anyone. + + + +[source,C] +------------------------------------------------------------------------------ +typedef ssize_t budget_count; + +struct budgetmanager +{ + rwlock lock; + + void (*callback)(); // called on resource shortage + (resource collector) + + int sum_priorities; // sum of all client budgets .. each + client is granted available/(sum_priorities/own_priority) of the resource + + budget_count available_prefs; // configuration from preferences + budget_count available_profile; // tuned by profiler + int available_factor; // how much % from prefs vs profile + + budget_count available; // caclulated from above + budget_count allocated; // actively in use +}; + +struct budget +{ + BudgetManager manager; + int priority; + + budget_count allocated; +}; +------------------------------------------------------------------------------ + + + + +Tasks +~~~~~ +// List what would need to be done to implement this Proposal in a few words: +// * item ... + + + + +Discussion +~~~~~~~~~~ + +Pros +^^^^ +// add just a fact list/enumeration which make this suitable: +// * foo +// * bar ... + + + +Cons +^^^^ +// fact list of the known/considered bad implications: + + + +Alternatives +^^^^^^^^^^^^ +//alternatives: if possible explain/link alternatives and tell why they are not + viable: + + + +Rationale +--------- +//rationale: Describe 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 + + +//endof_comments: diff --git a/doc/devel/rfc/ResourceManagementProfiling.txt b/doc/devel/rfc/ResourceManagementProfiling.txt new file mode 100644 index 000000000..bfd1b2731 --- /dev/null +++ b/doc/devel/rfc/ResourceManagementProfiling.txt @@ -0,0 +1,182 @@ +Resource Management: Profiling +============================== + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _Fri Jul 23 19:34:29 2010_ +*Proposed by* Christian Thaeter +------------------------------------- + +[abstract] +****************************************************************************** +From the beginning on we planned some kind of 'profiling' to adapt dynamically +to workload and machine capabilities. I describe here how statistic data can be +gathered in a generic way. This will later work together with other components +tuning the system automatically. +****************************************************************************** + + +Description +----------- +//description: add a detailed description: + +I just introduce some ideas about the planned profiling framework here, nothing +is defined/matured yet this is certainly subject for futher discussion and +refinement. + +.Requirements/Evaluation generic:: + Profiling should be sufficiently abstracted to have a single set of + datastructures and algorithms to work on a broad range of subjects + being profiled. Moreover the profiling core just offers unitless + counters, semantic will be added on top of that on a higher level. + + least possible overhead:: + Profiling itself must not cost much, it must not block and should avoid + expensive operations. Simple integer arithmetic without divisions is + suggested. + + accurate:: + We may sample data on in stochastic way to reduce the overhead, + nevertheless data which gets sampled must be accurately stored and + processed without rounding losses and drifts. + + transient values:: + It's quite common that some values can be far off either in maximum or + in minimum direction, the system should adapt to this and recover from + such false alarms. Workload also changes over time we need to find some + way to measure the current/recent workload an grand total over the + whole application runtime is rather uninteresting. While it is also + important that we adapt slow enough not to get into some osccilating + cycle. + + active or passive system:: + Profiling can be only passive collecting data and let it be analyzed by + some other component or active triggering some action when some limits + are reached. I am yet a bit undecided and keep it open for both. + + + + + + + +.Brainstorming in Code +[source,C] +------------------------------------------------------------------------------ + +typedef int64_t profile_value; + +struct profile +{ + ProfileVTable vtable; + + /* + Using trylock for sampling makes it never contend on the lock but some + samples are lost. Should be ok. + */ + mutex_t lock; /* with trylock? */ + + + /* statistics / running averages */ + + /* n being a small number 2-5 or so */ + profile_value max[n]; /* n maximum values seen so far, + decreased by recovery */ + profile_value min[n]; /* n minimum values seen so far, + increased by recovery */ + + /* store sum & count, but average calculation implies a division and will be + only done on demand */ + profile_value count; /* count profile calls */ + profile_value sum; /* sum up all calls, average = + sum/count */ + + /* current is the sampled value to be integrated */ + + /* trend is caclulated before theb new run_average */ + profile_value trend; /* trend = (trend + + (run_average-current))>>1 */ + + /* we may need some slower diverging formula for running average */ + profile_value run_average; /* run_average = (run_average + + current)>>1) */ + + + /* active limits, define whats good and whats bad, calls back to vtable when + limit is hit */ + profile_value max_limit; + profile_value min_limit; + /* do we want limits for trends too? */ + + /* we count how often we hit limits, a hit/miss ratio will give a good value + for optimization */ + profile_value hit_cnt; + profile_value high_miss_cnt; + profile_value low_miss_cnt; + + /* recovery state */ + int rec_init; + int rec_current; + int rec_percent; + + + void* extra; +}; +------------------------------------------------------------------------------ + + +Tasks +~~~~~ +// List what would need to be done to implement this Proposal in a few words: +// * item ... + + + + +Discussion +~~~~~~~~~~ + +Pros +^^^^ +// add just a fact list/enumeration which make this suitable: +// * foo +// * bar ... + + + +Cons +^^^^ +// fact list of the known/considered bad implications: + + + +Alternatives +^^^^^^^^^^^^ +//alternatives: if possible explain/link alternatives and tell why they are not + viable: + + + +Rationale +--------- +//rationale: Describe 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 + + +//endof_comments: diff --git a/doc/devel/rfc/SkillsCollection.txt b/doc/devel/rfc/SkillsCollection.txt new file mode 100644 index 000000000..c2c4f3d68 --- /dev/null +++ b/doc/devel/rfc/SkillsCollection.txt @@ -0,0 +1,80 @@ +[grid="all"] +`------------`----------------------- +*State* _Parked_ +*Date* _2007-06-13_ +*Proposed by* link:ct[] +--------------------------------- + + +Skills Collection +----------------- +Make a page where people can tell in which areas they are willing to support +others. + + +Description +~~~~~~~~~~~ +Some Page should list different things needed for working on the project and +users should attach themself when they offer support for it. This is meant that +people who run into problems know who to ask. In contrast this is not meant +like these Skill pages on Sourceforge or such. I don't like this rating and +posing system. We let people assing themself to skill and not skills to people +and there is no rating. + +Skills shall be anything which is needed like the tools we use, the code we +create etc. + + + +Example +^^^^^^^ + +.Git +* ct +* j6t + +.autotools +* ct + +.lumiera/renderpipe +* ichthyo + +... shall this contain emails? + + +Tasks +^^^^^ + * just set this page up .. either on this wiki or in a tiddlywiki which + becomes checked into the repo + + +Pros +^^^^ + * inter developer support and help network + + +Cons +^^^^ + * privacy concerns, people might not publish what they know or better what + they ''not'' know + + +Alternatives +^^^^^^^^^^^^ +...urgs + + +Rationale +~~~~~~~~~ +This only announces where people offer support within the lumiera developer +community and is absolutely voluntary. + + + + + +Comments +-------- + + +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/StreamTypeSystem.txt b/doc/devel/rfc/StreamTypeSystem.txt new file mode 100644 index 000000000..12323606f --- /dev/null +++ b/doc/devel/rfc/StreamTypeSystem.txt @@ -0,0 +1,268 @@ +Stream Type System +================== + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Draft_ +*Date* _2008-10-05_ +*Proposed by* link:Ichthyostega[] +------------------------------------- + + + +******************************************************************************** +.Abstract +Especially in the Proc-Layer, within the Builder and at the interface to the +Engine we need sort of a framework to deal with different »kinds« of +media streams. + +This is the foundation to be able to define what can be connected and to +separate out generic parts and isolate specific parts. +******************************************************************************** + + +Description +----------- +//description: add a detailed description: +The general idea is that we need meta information, and -- more precisely -- +that _we_ need to control the structure of this metadata. Because it has +immediate consequences on the way the code can test and select the appropriate +path to deal with some data or a given case. This brings us in a difficult +situation: + + * almost everything regarding media data and media handling is notoriously + convoluted + * because we can't hope ever to find a general umbrella, we need an extensible + solution + * we want to build on existing libraries rather then re-inventing media + processing. + * a library well suited for some processing task not necessarily has a type + classification system which fits our needs. + +The proposed solution is to create an internal Stream Type System which acts as +a bridge to the detailed (implementation type) classification provided by the +library(s). Moreover, the approach was chosen especially in a way as to play +well with the rule based configuration, which is envisioned to play a central +role for some of the more advanced things possible within the session. + + +Terminology +~~~~~~~~~~~ + * *Media* is comprised of a set of streams or channels + * *Stream* denotes a homogeneous flow of media data of a single kind + * *Channel* denotes an elementary stream, which -- _in the given context_ -- + can't be decomposed any further + * all of these are delivered and processed in a smallest unit called *Frame*. + Each frame corresponds to a time interval. + * a *Buffer* is a data structure capable of holding one or multiple Frames of media data. + * the *Stream Type* describes the kind of media data contained in the stream + + +Concept of a Stream Type +~~~~~~~~~~~~~~~~~~~~~~~~ + +The Goal of our Stream Type system is to provide a framework for precisely +describing the ``kind'' of a media stream at hand. The central idea is to +structure the description/classification of streams into several levels. +A complete stream type (implemented by a stream type descriptor) contains +a tag or selection regarding each of these levels. + +Levels of classification +^^^^^^^^^^^^^^^^^^^^^^^^ + + * Each media belongs to a fundamental *kind of media*, examples being _Video, + Image, Audio, MIDI, Text,..._ This is a simple Enum. + * Below the level of distinct kinds of media streams, within every kind we + have an open ended collection of *Prototypes*, which, within the high-level + model and for the purpose of wiring, act like the "overall type" of the + media stream. Everything belonging to a given Prototype is considered to be + roughly equivalent and can be linked together by automatic, lossless + conversions. Examples for Prototypes are: stereoscopic (3D) video versus the + common flat video lacking depth information, spatial audio systems + (Ambisonics, Wave Field Synthesis), panorama simulating sound systems (5.1, + 7.1,...), binaural, stereophonic and monaural audio. + * Besides the distinction by prototypes, there are the various *media + implementation types*. This classification is not necessarily hierarchically + related to the prototype classification, while in practice commonly there + will be some sort of dependency. For example, both stereophonic and monaural + audio may be implemented as 96kHz 24bit PCM with just a different number of + channel streams, but we may as well have a dedicated stereo audio stream + with two channels multiplexed into a single stream. + + +Working with media stream implementations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +For dealing with media streams of various implementation type, we need +_library_ routines, which also yield a _type classification system_ suitable +for their intended use. Most notably, for raw sound and video data we use the +http://gmerlin.sourceforge.net/[GAVL] library, which defines a fairly complete +classification system for buffers and streams. For the relevant operations in +the Proc-Layer, we access each such library by means of a Façade; it may sound +surprising, but actually we just need to access a very limited set of +operations, like allocating a buffer. _Within_ the Proc-Layer, the actual +implementation type is mostly opaque; all we need to know is if we can connect +two streams and get an conversion plugin. + +Thus, to integrate an external library into Lumiera, we need explicitly to +implement such a Lib Façade for this specific case, but the intention is to be +able to add this Lib Façade implementation as a plugin (more precisely as a +"Feature Bundle", because it probably includes several plugins and some +additional rules) + + +Link between implementation type and prototype +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +At this point the rules based configuration comes into play. Mostly, to start +with, determining a suitable prototype for a given implementation type is sort +of a tagging operation. But it can be supported by heuristic rules and an +flexible configuration of defaults. For example, if confronted with a media +with 6 sound channels, we simply can't tell if it's a 5.1 sound source, or if +it's a pre mixed orchestra music arrangement to be routed to the final balance +mixing or if it's a prepared set of spot pick-ups and overdubbed dialogue. But a +heuristic rule defaulting to 5.1 would be a good starting point, while +individual projects should be able to set up very specific additional rules +(probably based on some internal tags, conventions on the source folder or the +like) to get a smooth workflow. + +Moreover, the set of prototypes is deliberately kept open ended. Because some +projects need much more fine grained control than others. For example, it may +be sufficient to subsume any video under a single prototype and just rely on +automatic conversions, while other projects may want to distinguish between +digitized film and video NTSC and PAL. Meaning they would be kept in separate +pipes an couldn't be mixed automatically without manual intervention. + + +connections and conversions +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * It is _impossible to connect_ media streams of different kind. Under some + circumstances there may be the possibility of a _transformation_ though. For + example, sound may be visualized, MIDI may control a sound synthesizer, + subtitle text may be rendered to a video overlay. Anyway, this includes some + degree of manual intervention. + * Streams subsumed by the same prototype may be _converted_ lossless and + automatically. Streams tagged with differing prototypes may be _rendered_ + into each other. + * Conversions and judging the possibility of making connections at the level + of implementation types is coupled tightly to the used library; indeed, most + of the work to provide a Lib Façade consists of coming up with a generic + scheme to decide this question for media streams implemented by this + library. + + +Tasks +~~~~~ +// List what needs to be done to implement this Proposal: + * draft the interfaces ([green]#✔ done#) + * define a fall-back and some basic behaviour for the relation between + implementation type and prototypes [,yellow]#WIP# + * find out if it is necessary to refer to types in a symbolic manner, or if it + is sufficient to have a ref to a descriptor record or Façade object. + * provide a Lib Façade for GAVL [,yellow]#WIP# + * evaluate if it's a good idea to handle (still) images as a separate distinct + kind of media + + + +Discussion +~~~~~~~~~~ + +Alternatives +^^^^^^^^^^^^ +//alternatives: explain alternatives and tell why they are not viable: +Instead of representing types by metadata, leave the distinction implicit and +instead implement the different behaviour directly in code. Have video tracks +and audio tracks. Make video clip objects and audio clip objects, each +utilising some specific flags, like sound is mono or stereo. Then either +switch, switch-on-type or scatter out the code into a bunch of virtual +functions. See the Cinelerra source code for details. + +In short, following this route, Lumiera would be plagued by the same notorious +problems as most existing video/sound editing software. Which is, implicitly +assuming ``everyone'' just does ``normal'' things. Of course, users always were +and always will be clever enough to work around this assumption, but the problem +is, all those efforts will mostly stay isolated and can't crystallise into a +reusable extension. Users will do manual tricks, use some scripting or rely on +project organisation and conventions, which in turn creates more and more +coercion for the ``average'' user to just do ``normal'' things. + +To make it clear: both approaches discussed here do work in practice, and it's +more a cultural issue, not a question guided by technical necessities to select +the one or the other. + + +Rationale +--------- +//rationale: Give a concise summary why it should be done *this* way: + + * use type metadata to factor out generic behaviour and make variations in + behaviour configurable. + * don't use a single classification scheme, because we deal with distinctions + and decisions on different levels of abstraction + * don't try to create an universal classification of media implementation type + properties, rather rely on the implementation libraries to provide already a + classification scheme well suited for _their_ needs. + * decouple the part of the classification guiding the decisions on the level + of the high level model from the raw implementation types, reduce the former + to a tagging operation. + * provide the possibility to incorporate very project specific knowledge as + rules. + +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: + + + + +Comments +-------- +//comments: append below +As usual, see the +http://www.lumiera.org/wiki/renderengine.html#StreamType[Proc-Layer impl doku] +for more information and implementation details. + +Practical implementation related note: I found I was blocked by this one in +further working out the details of the processing nodes wiring, and thus make +any advance on the builder and thus to know more precisely how to organize the +objects in the link:EDL/Session[]. Because I need a way to define a viable +abstraction for getting a buffer and working on frames. The reason is not +immediately obvious (because initially you could just use an opaque type). The +problem is related to the question what kind of structures I can assume for the +builder to work on for deciding on connections. Because at this point, the +high-level view (pipes) and the low level view (processing functions with a +number of inputs and outputs) need in some way to be connected. + +The fact that we don't have a rule based system for deciding queries currently +is not much of a problem. A table with some pre configured default answers for +a small number of common query cases is enough to get the first clip rendered. +(Such a solution is already in place and working.) + + -- link:Ichthyostega[] 2008-10-05 + +Woops fast note, I didn't read this proposal completely yet. Stream types could +or maybe should be coopertatively handled together with the backend. Basically +the backend offers one to access regions of a file in a continous block, this +regions are addressed as "frames" (this are not necessary video frames). The +backend will keep indices which associate this memory management with the frame +number, plus adding the capabilitiy of per frame metadata. This indices get +abstracted by "indexing engines" it will be possible to have different kinds of +indices over one file (for example, one enumerating single frames, one +enumerating keyframes or gops). Such a indexing engine would be also the place +to attach per media metadata. From the proc layer it can then look like `struct +frameinfo* get_frame(unsigned num)` where `struct frameinfo` (not yet defined) +is something like `{ void* data; size_t size; struct metadata* meta; ...}` + + -- link:ct[] 2008-10-06 + +Needs Work +~~~~~~~~~~ +There are a lot details to be worked out for an actual implementation but we +agreed that we want this concept as proposed here. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/TagCloudsOnResources.txt b/doc/devel/rfc/TagCloudsOnResources.txt new file mode 100644 index 000000000..2c36d76dc --- /dev/null +++ b/doc/devel/rfc/TagCloudsOnResources.txt @@ -0,0 +1,81 @@ +Design Process : Tag Clouds for Resources +========================================= + +[grid="all"] +`------------`----------------------- +*State* _Dropped_ +*Date* _2008-07-15_ +*Proposed by* link:PercivalTiglao[] +------------------------------------- + + +Tag Clouds for Resources +------------------------ + +Perhaps a Cloud of tags is unnecessary, but tagging resources similar to +Youtube or like Tag Clouds allows for efficient searching and filtering. Anyone +who uses the web would know how to use them. If a "Cloud of Tags" approach is +used, then organizing the tags by some sort of frequency would be useful. IE: +the more a specific tag is used, the larger it gets, or perhaps the more often +that tag is searched on. + + +Description +~~~~~~~~~~~ + + + +Tasks +~~~~~ + + +Pros +~~~~ + +* Simple GUI Concept +* Eases management of resources with Search +* Orthogonal to other resource management schemes like Folders + + +Cons +~~~~ + + +Alternatives +~~~~~~~~~~~~ + + + +Rationale +~~~~~~~~~ + + + + + +Comments +-------- + +* Note: I was inspired with this idea during an email conversation with + Rick777. -- link:PercivalTiglao[] [[DateTime(2008-07-17T14:29:57Z)]] + +* Agreed, this is usefull. Also, more advanced config rules can make use of + such tags and wiring can depend on them, for example to route your dialogue + audio to another global bus than the music or ambiance. + -- link:Ichthyostega[] [[DateTime(2008-07-27T22:23:38Z)]] + + + +Conclusion +---------- + +This Design Proposal is 'superseded' by a much more advanced proposal: +link:DelectusShotEvaluator[Delectus] + +(Dropping it doesn't mean disapproval) + + +'''' + + +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/TodoLists.txt b/doc/devel/rfc/TodoLists.txt new file mode 100644 index 000000000..1d3b987ac --- /dev/null +++ b/doc/devel/rfc/TodoLists.txt @@ -0,0 +1,49 @@ +[grid="all"] +`------------`----------------------- +*State* _Parked_ +*Date* _2008-03-05_ +*Proposed by* link:ct[] +------------------------------------- + +Todo Lists +---------- +We need some way to organize tasks to be done (tiddlywiki, testsuite, ...?) + + +Description +~~~~~~~~~~~ + + +Tasks +^^^^^ + + +Pros +^^^^ + + + +Cons +^^^^ + + + +Alternatives +^^^^^^^^^^^^ + + + +Rationale +^^^^^^^^^ + + + + + + +Comments +-------- +We decided to use a Tiddlywiki for now until this is further worked out + -- link:ct[] [[DateTime(2008-03-08T03:38:50Z)]] + +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/UnitTests_Python.txt b/doc/devel/rfc/UnitTests_Python.txt new file mode 100644 index 000000000..9d71205e6 --- /dev/null +++ b/doc/devel/rfc/UnitTests_Python.txt @@ -0,0 +1,82 @@ +Design Process : Unit Tests Python +================================== + + +[grid="all"] +`------------`----------------------- +*State* _Dropped_ +*Date* _2007-06-17_ +*Proposed by* link:Ichthyostega[] +------------------------------------- + + +UnitTests in Python +------------------- + +Use the Python scripting language for the actual Unit Tests and access the +Cinelerra Code via SWIG + + +Description +~~~~~~~~~~~ + +Define Test classes in Python, using e.g. the link:PyUnit[] framework of the +Python Standard lib. The SWIG compiler can generate wrapper code automatically, +so we can access the C++ Classes and Facilities of Cinelerra as Python Modules +and Classes. The Classes to be tested in Cinelerra need to provide some +Interface for carrying out these tests (and this is one of the main benefits of +the whole Test driven aproach). + + +Tasks +~~~~~ + + * Find out how the SWIG generated wrappers play together with Python's List + and Map types. Without the ability to use the latter in the tests, this + whole proposal is rather pointless. + * Think on how we can test video data processing (at least in its basics, e.g. + does additive overlay work) + + +Pros +~~~~ + +Programming Unit and Self tests in a Scripting language facillates this task. +The X-Language bindings are quite usable today. As a side effect, it helps to +get a clean program structure, because the tests need some Interface and/or +some object factories to create the test candidates. Python is proposed, +because it is fairly mainstream, has a flat learning curve and but is +moderately modern and functional-style at the same time. + +Cons +~~~~ + + * Adds to the complexity + * Some old-style hackers have a quite distinct aversion against Python + + +Alternatives +~~~~~~~~~~~~ + +Rationale +~~~~~~~~~ + +Why am I proposing this? Out of lazyness. Python is there, many devs (on linux) +have some Python skills, SWIG is not overly complicated to use. + +And last but not least: just to get the discussion going... ;-) + + +Comments +-------- + +* I'd rather consider to use some embedded language in cinelerra which we can + use to drive tests, should be something smaller and more sane than python. + Needs certainly more discussion. For simple unit tests some C/C++ harness and + bit shell scripting would suffice, I really want to integrate this with + link:NoBug[]. + -- link:ct[] [[DateTime(2007-06-17T17:32:27Z)]] + + +'''' +Back to link:../DesignProcess[] diff --git a/doc/devel/rfc/UseCases.txt b/doc/devel/rfc/UseCases.txt new file mode 100644 index 000000000..7f1b96d5c --- /dev/null +++ b/doc/devel/rfc/UseCases.txt @@ -0,0 +1,310 @@ +[grid="all"] +`------------`----------------------- +*State* _Parked_ +*Date* _2008-10-31_ +*Proposed by* link:Ichthyostega[] +------------------------------------- + + +Use Case analysis +----------------- + +The only way to defeat "featuritis" is to build upon a coherent design -- + + +which in turn relies upon a more or less explicit understanding what the +application should be like, and the way the prospective user is thought to work +with the program. Today, a generally accepted 'method' for building up such +an understanding is to do a *use case analysis*. Such a formal analysis would +require to identify all usage scenarios with the involved actors and parts of +the system, and then to refine them in detail and break them down into distinct +use cases. Here, I'll try a rather informal variant of such an analysis. I'll +restrain myself to describing the most important usage situations. + +'please participate in the discussion. It well may be that everything detailed + here is self-evident, but I doubt so. At least the grouping and the omissions + kind-of reflect a certain focus of the project' + + +Describing basic Lumiera usage situations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The fundamental assumption is that the user works on a project, which is +reflected in the fact that the user is working on a single session over an +extended period of time (several hours to several years). External media will +be imported and incorporated into this session, additional media will be +created within this session, and finally there is at least one render or export +procedure to harvest the results of this work. + + +Scenario (1) : Exploring Media +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Various external media files are opened. You play, cue and examine the media. +Tagging, labeling and adding notes. Marking of interesting points and ranges. +Possibly breaking down into clips, or at least extract some ranges as clips. +Draft arranging the clips, applying some effects to check the result and thus +to find out about the viability of the footage. Playback of several media at +the same time (several videos, but also video and music). Grouping of assets +(media, clips, effects, markers) into folders. + + +Scenario (2) : Simple assembly +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You build up a simple linear cut sequence. Either by + + - using a single source media, trimming it and cutting away (a small number + of) unwanted parts + - playing source media and spilling over (insert, overwrite) some parts into + the final assembly + - dragging over the pre-organised clips from clip folders to build up the + assembly. + +Sound is either used immediately as-is (the soundtrack attached to the media), +or there is a similarly simple, linear music bed. Some people prefer to switch +sound off entirely for this kind of work. In any case, the link is either +automatic, or rather vague and soft (as music being vaguely correlated) + + +Scenario (3) : Augmenting an assembly +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Without the intention to rework it from scratch, an already existing simple +assembly is augmented, beautified and polished, maybe to conform with +professional standards. This includes the ``rescue'' of a somewhat questionable +assembly by repairing localized technical problems, but also shortening and +re-arranging, and in extreme cases even changing the narrative structure. A +distinctive property of this usage scenario is that work happens rather in the +context of 'tasks' (passes) -- not so much isolated operations: + + - the task may be to get the rhythm or overall tempo right, and thus you go + over the sequence and do trim, roll, shuffle or slide edits. + - you may want to ``fold-out'' parts of the sound, thus interweaving o-sound + and music + - there may be a sound overdubbing and replacing pass + - you may want to walk certain automation curves and adjust levels (sound + volume or tone, fade, brightness/contrast/colour) + - general polishing may include adding title overlays, fading in and out, + adding (typically a single type of) transition(s) in a coherent manner + + +Scenario (4) : Compositional work +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Here I define *compositional work* as a situation where you deal with +multiple more or less independent sequences going on in parallel, similar to a +musical score. Frequently, we encounter compositional parts embedded in a +otherwise linear work, and often those parts evolve when Scenario (3) is driven +to the extreme. + + - the most common situation is that o-sound, sound design and music work + together with the temporal structure created in the image edits. + - a movie with a complex narrative structure may induce compositional work on + a very large scale (and existing applications frequently fall short on + supporting such) + - _compositing_ often leads to compositional work. Special FX, masked objects + being arranged, artificial elements to be integrated. + - similarly any collage-like or heavily layered arrangements lead themselves + to requiring compositional work. + +The common distinctive property of all those situations is: objects are +embedded into a primary context and have to obey the rules of this context, and +at the same time have a close correlation to other objects which are embedded +in a completely different (``orthogonal'') context. (To give a catchy example: +assume, a CG monster has to be integrated. Besides the masked monster object, +you have several colouring and blurring layers at completely different levels +in the layering order, and at the same time you have correlated sound objects, +which need to be integrated into the general sound-scape. And now your primary +job is to get the movement and timings of the monster right in relation to the +primary timing grid established by the existing edit) + +The working style and thus the tool support necessary for compositional work is +completely different to Scenario (3). After an initial build-up (which often is +very systematic), the working profile can be characterized by tweaks to various +parameters to be done in-sync at widely separated sites within the session, +together with repeated cycles of ``do it'', ``assess the result'', ``undo all and +do some small detail differently''. Typically there is the need for much navigation +(contrast this to Scenario (3) where you work in _tasks_ or _passes_) + + +Scenario (5) : Working with Sound +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The degree of integrating sound work is worth discussing. Often, due to +limitations in existing tools, sound work is done in separate applications to a +large extent. Which in turn forces the whole production into a sequential +organisation scheme. First the edit has to be roughly final, and then the sound +people can step in. (Of course this is an simplification). To list the common +operations: + + - cleaning and preparing original sound + - fitting sound library elements or separately produced sound + - overdubbing + - playing or building music to match the rhythm of the edit or the original + footage + - montage of dialogue and/or noise correlated to the primary content of the + sequence + - sound design, shaping the pace and the feel of a sequence + - final balance mix + +While clearly some of those tasks are always better done within a dedicated +application, the ability to carry out this work partially within the main +session and even while the basic edit is still in flux -- may open new artistic +possibilities. + + +Scenario (6) : Large Projects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +At first sight, the operations and the work to be done in large projects is the +same as in small ones. But large projects tend to create sort of an additional +``layer'' on top of the usage scenarios described thus far, which will ``kick in'' +at various places. + + - work may be divided upon several editors, working on separate parts + (sequences) which then need to be re-integrated + - there may be a global asset organisation (naming scheme), which will be + extended locally, resulting in nested naming scopes. + - some quite basic stuff needs to be done in a coherent fashion, e.g. titles, + a certain transition (template), the way fade-outs are done, a certain + colour profile. Possibly, this stuff needs to be adjusted all over the + project. + - there will be a general (large scale) timing grid with distinct ``check points'' + and probably there is the need to navigate to the different parts of the + whole project. + - there may be the necessity to build several versions of the same project in + parallel (e.g. a short version and a extended director's cut) + - you may have to care for such nasty and tedious things as keeping sub-titles + in-sync while the edit is still in flux + - you may want to do integration builds, where you add placeholders just for + the purpose to get an impression of the work as a whole. + + +Scenario (7) : Teamwork +^^^^^^^^^^^^^^^^^^^^^^^ + +Several people work on a project. + + - A longer sequence might be split up into parts, each one edited by another + person. The parts will be collected and assembled by the chief editor. Edits + to the parts will still be possible, but a system of permissions allows to + lock down access to parts of the edit, so to prevent unexpected interferences. + - Arrangements based on the same resources can be branched, tagged and merged. + - Edits are logged with usernames + - Markers can be shown/hidden on a per creator base. + - Team members need ways to share and store notes and suggestion for each + other work. Annotations can be added to clips, markers or arrangements + - A pen tool could allow to scribble on top of frames or arrangements. An + expressive and fast way to leave suggestions about deletions, movements and + all other kinds of edits. + + +Scenario (8) : Script driven +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The application is started ``headless'' (without GUI) and controlled via an +API. Either an existing session is loaded, or a new session is created and +populated. Then, some operations have to be done in a systematic manner, +requiring a way to address parts of the session both unambiguously and in a way +easy to access and control from a programming environment (you can't just +``see'' the right clip, it needs to be tagged). Finally, there might be an +export or render step. A variation of this scenario is the automatic extraction +of some informations from an existing project. + + +Discussion +~~~~~~~~~~ + +.Pros +* describing such scenarios, even if hypothetical, create an anchor or point of + referral for feature/GUI design work to be done in detail +* relating features to working situations helps to see what is really important + and what is rather of technical merit +* compiling and discussing this list helps shaping the character of the + application as a whole +* the above compilation relates individual features to a general production + process. +* the goal of this compilation is to be _fairly complete_ + + +.Cons +* any of those descriptions is artificial +* sometimes it is better to develop an application technology driven, + especially when it is technologically challenging to get it to work properly. +* having such a large-scale vision may freak away people which otherwise + might jump in and implement some crazy but valuable new feature +* the listed usage scenarios intend to be _fairly complete_, which can be a + limitation or even self-deception. Better have an open ended list. +* the above compilation seems quite conventional and explicitly leaves out some + scenarios + - networked, distributed scenarios, compound applications + - television, life video, VeeJay-ing + - cartoons, animations, game design + + + +.Alternatives +* avoiding a general plan, just sharing a vague general vision +* just start out with one scenario directly at hand (e.g. the simple assembly) + and not worrying about the rest +* rather then defining those scenarios (which are necessarily hypothetical), + rather stick to the operation level. E.g. a use case would be rather + on the level of ``triming a clip'' +* doing a complete state-of-the art UML use case analysis. +* after having created the foundation, rather stick to an XP approach, i.e. + implement, integrate and release small ``usage stories'' + + + +Rationale +^^^^^^^^^ + +Well, after having considered, compiled and written such an concept, altogether +avoiding a big picture view of the application is not longer an option. To the +other extreme, we neither have the resources, nor the circumstances for doing a +rigid and formal analysis. Finally, the XP approach really sounds promising, +and it should be clear that it is in no way ruled out. Nothing hinders us to +have a detailed vision, but then to implement small usage stories which fit +into this vision. + +Besides, another consideration. The above compilation builds upon the notion, +that there is a common denominator of film making craft, a core editing art, +which has been shaped in the first 100 years of cinema, and which won't go away +within the next generation, even if the technological and practical +circumstances of production change quite dramatically. + + + + + + + + +Comments +-------- +//comments: append below + +.Template e.g. for regular TV series +Constraints to fit all contents within fixed timeline, cover topic, select +collage of iconic scenes from archived and collected footage. Update intro and +credit roll for each episode. Add in stopmotion, and 3D model animations with +vocal commentaries. Gather together separate items from "outworkers". + +Tree:: '2008-12-27 08:36:36' + + +//endof_comments: + + + +Parked +~~~~~~ +We have to revisit this, possibly someone (or a group) who wants to work on +the workflow. For now its parked until revisited. + + Do 14 Apr 2011 03:06:42 CEST Christian Thaeter + + + + +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/WebsiteNavigation.txt b/doc/devel/rfc/WebsiteNavigation.txt new file mode 100644 index 000000000..172b6543f --- /dev/null +++ b/doc/devel/rfc/WebsiteNavigation.txt @@ -0,0 +1,160 @@ +WebsiteNavigation +================= + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _Mi 08 Dez 2010 11:32:32 CET_ +*Proposed by* Ichthyostega +------------------------------------- + +[abstract] +******************************************************************************** +The Lumiera website is assumed to accumulate a lot of content. Thus we need +to care about making that content accessible, to help finding the relevant +topics and to keep the overall structure intact. This RfC is to collect, +discuss and agree upon the guidelines and requirements. +******************************************************************************** + +Description +----------- +//description: add a detailed description: + +Issues to care +~~~~~~~~~~~~~~ + +Navigation:: + The page hierarchy becomes at least 5 levels deep, likely even deeper. + When reading a page, the current subtree leading down to this page should + be right at hand; especially access to the siblings and the parent's siblings + is important. For re-accessing content, it is necessary to be able to drill + down to an known location (``within the design docs, detailing the application, + I need the configuration section'') + + -> we need an *auto generated navigation* and an embedded *menu tree widget* in the web pages. + +Tagging:: + There should be an easy way to categorise individual pages *by keyword(s)* + and an automatically generated indexing by tags, possibly with an per tag + overview page. + +Search:: + The usual *site search*. It should include the contents of the issue tracker. + Even today such a scoped search is valuable and even necessary for working + with the informations collected within the Lumiera project + +Sanity:: + Each relevant page needs to be reachable. There are some additional pages and + especially subdirectories which should not be linked into the website navigation. + Moreover, all (internal) links on the pages should be valid. + + -> this could be addressed by a **sanity checker script** + +Usage situations +~~~~~~~~~~~~~~~~ + +(a) working on content +^^^^^^^^^^^^^^^^^^^^^^ +Working on content should be readily accessible for _everyone_. One time contributions +are especially encouraged. This leads to the following usage scenario: + +A contributor has some informations to share or wants to do some additions or modifications. +(S)he locates somehow the place where relevant informations are stored, adds some text, +possibly adds a new page or splits another page in two. + +_Note_: no awareness of the issues of navigation can be assumed. The occasional contributor +won't notice any concern which isn't right at hand. + +(b) maintaining a subsystem +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Some person(s) will be responsible for a subsystem or some segment of the informations +on the website. This responsibility is content centric. It might include frequent rearranging, +regrouping and reordering of pages to accommodate the increasing scope of informations. + +_Note_: while here some awareness of website organisational issues can be assumed, +any requirement to care for external organisational issues is a burden and distracts +from the actual work to be done -- thus it is likely to be short circuited or postponed +``for later''. Note especially, reorganising content in a subsection *must not* incur +the burden of re-doing the same reorganisation steps mirrored in some central navigation +configuration or table of contents. (this is a knock out criterion) + +(c) maintaining the website +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The website maintainer is responsible for the overall sanity of the website, without +being familiar with all details of ongoing work in some part or section of the information. +Another concern here is the outward perception of the website, which might incur changes +on the top level navigation or some rewording of overview pages. + +_Note_: this kind of work is rather unrewarding. There is the danger of collisions with the +work of the subsystem maintainer + + +Conclusion: Requirements for any navigation solution +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * ability to to pick up a nested page structure + * ability to cope with any additions and changes in the lower levels automatically, without help by the user + * ability to override: + + - not including some subdirectories + - including links-to-external at arbitrary positions + +optional/additional features +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The following features would be handy, but can be considered optional + + * ability to change the displayed title of a page in the navigation + * ability to control the ordering of pages in the navigation + * complete manual override of the visible content of a specific subdirectory + + + +Tasks +~~~~~ +// List what would need to be done to implement this Proposal in a few words: +// * item ... + + + +Discussion +~~~~~~~~~~ + +Pros +^^^^ +// add just 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: Describe 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 + + +//endof_comments: diff --git a/doc/devel/rfc_dropped/ApplicationStructure.txt b/doc/devel/rfc_dropped/ApplicationStructure.txt deleted file mode 100644 index 3b18ab92b..000000000 --- a/doc/devel/rfc_dropped/ApplicationStructure.txt +++ /dev/null @@ -1,200 +0,0 @@ -Design Process : Application Structure -====================================== - -[grid="all"]] -`------------`---------------------- -*State* _Dropped_ -*Date* _2008-11-05_ -*Proposed by* link:ct[] ------------------------------------- - -Application Structure ---------------------- -Here I am going to propose some more refined structure of the application and -its components. - - -Description -~~~~~~~~~~~ -So far we came up with a simplified BACKEND/PROC/GUI structure where each of -this entities defines its own sub subcomponents. We agreed to glue that all -together with some portable versioned interfaces system, but details where not -laid out yet. At the time of this writing the interface system and plugin -loader are reasonable finished to be usable (some small refinements to do). We -recently discussed some details on IRC on how to engage this without a -definitive decision. The topic of this proposal is to make a detailed -description towards how the application components being glued together. - -In the discussion mentioned above we concluded that we want a 'lumiera' binary -which in turn loads the optional parts as plugins. There was no consent what -this parts are actually be, except that the GUI should be optional for headless -operation. I suggested to make as much as possible pluginable to make it easier -to validate our interfaces and try different things out. - -Now I introduce 'lumiera' here, this will become a new component in -./src/lumiera being the driver application for bootstraping all the rest: - -Then our application structure looks somewhat like (please refine): - - * the 'lumiera' loader - - commandline handling - - interface & plugin system - - session manager core - - configuration system - - lua scripting - * backend - - file and io handling - - caches - - streams - - threads - - scheduler - * proc - - asset management - - config rules system - - builder - - render graph management - * gui - - timelines - - viewers - - resources - - preferences - - ... - -Furthermore the interface&plugin system is flexible enough to provide things -independently of their origin (if it is build in or a plugin/dynamic library). -So deployment (where to link these things) is secondary. - -'lumiera' will then be the executable the user starts up, what exactly gets - initialized and booted up is then matter -of configuration and commmandline options (and maybe lua scripting?). - - - -Tasks -^^^^^ - - * create the 'lumiera' directory - - setup the build system - - move config, plugin and interfaces therein - - lua support can be done later - * write the main() part of the application - - start config system - - parse commandline opts - * librificate all other components (backend, proc gui) - - define their lumiera interfaces - - decide if they shall be statically linked, becoming shared libs or plugins - -This are rather distributed tasks, after the 'lumiera' being set up, all other -components have to be adapted to be loadable from it. - - -Pros -^^^^ - - * flexible plugin based architecture - - later: loads only things which are necessary for a given task - * very fast startup - * things which cant be used on a given environment can be left out (no gui on - a headless system, no $DISPLAY set) - * inter dependencies between interfaces and plugins are automatically tracked. - - -Cons -^^^^ - -Ichthyo raised concerns that this kind of flexibility might attract other -people to write things which are not in our intention and break future design -and compatibility. We need to carefully document and define interfaces that -people don't abuse those! - - - -Alternatives -^^^^^^^^^^^^ - -We discussed the startup/main() through the GUI as it is currently done, it -would be also possible to produce some more executables (lumigui, luminode, -lumiserver, ....). But I think we agreed that a common loader is the best way -to go. - - -Rationale -~~~~~~~~~ - -I just think this is the best way to ensure a enduring design even for future -changes we can not forsee yet. - - - - - -Comments --------- -We discussed this issue lately on IRC and I got the feeling we pretty much -agreed on it. - - * we don't want to build a bunch of specialized executables, rather we build - one core app which pulls up optional parts after parsing the config - * we change the GUI to be loaded via the module/interfaces system - -From reading the above text, this proposal seems to capture that. But I am -somewhat unsure if the purpose of this proposal isn't rather to load just a -micro kernel and the pull up components according to configuration. Because I -wouldn't accept such an architecture, and I clearly stated so right at the -beginning of our project. I accepted a very flexible and language neutral -plugin system on the condition the core remains in control, stays -''reasonable'' monolithic and componentization doesn't handicap us in creating -an architecture based on abstractions and exploiting the proven design -patterns. - -It has that flexibility, yes. But that means not that we have to abuse it in -any way. The main() there and thus the bootstrap of the application is under -our tight control, if we want to reject scriptable/highly configurable -bootstrapping there then we can just do so. Thats more a social than a -technical decision. I personally don't like if a design is 'nannying' and puts -too much constraints into unforeseen areas. If the computer can do some task -better than we, it shall do it. This still means that I want to stay very much -in control, it should only do some tedious, error-prone managing tasks for me. -For example the interfaces system already tracks inter-dependencies between -plugins and interfaces automatically, without the programmer needs to care or -define anything. The interface system gets it right and we wont need to care -for the order initialization. I added that because I consider such as -absolutely important for plugins which might be supplied by third parties where -we have no control over. But I now realized that we can nicely use that for our -own internal things too. Imo thats some very valuable service. - -- link:ct[] [[DateTime(2008-11-08T06:26:18Z)]] - -Some further minor details: We didn't finish the discussion about namespaces on -the last meeting. (I know I still have to write up a proposal showing the two -or three alternatives I see regarding namespace organisation). But probably, -"lumiera::" will be our top level interface namespace and then probably the -lumiera directory will be taken by that. I see no problem also putting some -startup facilities in there, but generally, it shouldn't contain implementation -code, only headers and abstract classes. If that's going to become a problem, -we should consider to use a separate package for the startup, e.g. "src/boot". - -Another point is, you need not write a main, because there is already one. -Please have a look at it, especially with regards to the -[wiki:self:../GlobalInitialization global initialisation]. Further, last year -I've investigated boost::program_options and think it's fine. I use it for my -test class runner since then. I don't think there is any reason why we should -bother with parsing options (most config is pulled up from the session). I -don't think we get much program options, maybe something to set a GUI skin. -Moreover, I've written last year a thin wrapper around the commandline and -integrated it with the boost options parser such that user code can receive the -remaining options as a vector of std::strings. Please have a look at -link:http://git.lumiera.org/gitweb?p=LUMIERA;a=blob;f=tests/common/mainsuite.cpp;h=455bfd98effd0b7dbe6597f712a1bdfa35232308;hb=80e1e382f42512ebf2e10a802f77e50327b8fb73[the test class runner main] -for an usage example. I really want our Lumiera main to be clean and expressive -in the way showed there. Probably the most important part of the startup is -pulling up the session core; because of that I think most of the startup -process falls into the realm of the Proc-Layer. Within Proc, I don't want any -significant string manipulations done with C-strings and I don't want raw -arrays when we can use std::vector. - -- link:Ichthyostega[] [[DateTime(2008-11-06T19:28:13Z)]] - -I 'dropped' this now because we do it somewhat differently now and I dont want -to document this here :P - -- link:ct[] [[DateTime(2009-02-03T17:28:28Z)]] - -'''' -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_dropped/ApplicationStructure.txt b/doc/devel/rfc_dropped/ApplicationStructure.txt new file mode 120000 index 000000000..0bba2a8db --- /dev/null +++ b/doc/devel/rfc_dropped/ApplicationStructure.txt @@ -0,0 +1 @@ +../rfc/ApplicationStructure.txt \ No newline at end of file diff --git a/doc/devel/rfc_dropped/MistakestoAvoid.txt b/doc/devel/rfc_dropped/MistakestoAvoid.txt deleted file mode 100644 index 4c671b8c8..000000000 --- a/doc/devel/rfc_dropped/MistakestoAvoid.txt +++ /dev/null @@ -1,427 +0,0 @@ -Design Process : Mistakes to avoid -================================== - -[grid="all"] -`------------`----------------------- -*State* _Dropped_ -*Date* _2008-04-21_ -*Proposed by* link:rick_777[] -------------------------------------- - - -Mistakes to avoid in the Lumiera design ---------------------------------------- - - -As a multimedia user and experienced programmer, I've found various flaws -present in open source Non Linear Video editors. Here I will list the problems -and their proposed (or mandatory) solutions. Please forgive me if some of the -ideas here have already been approved, I wrote this text before reaching this -wiki. - - -Description -~~~~~~~~~~~ - - -As a multimedia user and experienced programmer, I've found the following flaws -present in open source Non Linear Video editors (your mileage may vary) : - -. Frequent crashes (which most of the time make you lose your work) -. Reinventing the wheel for every new project -. Lack of a user-friendly (and extensible) UI -. Lack of support for certain video formats or codecs -. Lack of documentation -. Lack of cross-platform support -. Dependency on scripted languages like Python, which make installation a mess - -I will expand on the problems and their proposed (or mandatory) solutions. - - -1. Frequent crashes -~~~~~~~~~~~~~~~~~~~ - -[grid="all"] -`------------`------------------------------------------------------ -*Problem* Frequent Crashes and unsaved work. -*Severity* CRITICAL. -*Solution* Isolating the UI from the rendering and data handling (also - improves the extensibility) -*Required* Yes -*Workarounds* Auto-save (however it's not a real solution for the problem) --------------------------------------------------------------------- - -Working with multimedia (video / audio) editing is a magnet for segfaults -(crashes) due to the handling of pointers and compression algorithms. A bug in -a plugin (like in Audacity's low-pass filter) will crash and you suddenly -realize you lost your work - unless you have an auto-save feature, but that -doesn't go to the root of the problem. - -My proposal is to move the low-level handling of video to a separate process, -which then will do the processing - if it crashes, the UI will only report an -error with a dialog (i.e. "the process crashed. Try again?"), but you work will -stay safe. I'm not sure of the implementation difficulties that arise from -having a shared memory buffer for rendering / processing, but one thing is -certain: Whenever you move the cursor or rewind a part of a clip in your -resources, the application isn't supposed to crash. Just moving the cursor -isn't a time-critical task, so perhaps we can use temporary files for this. -It's safer if you're not doing the final rendering. - - -Comments -^^^^^^^^ - -I am not sure yet about separating things into processes, generally it is clear -that this would be more robust but there are some performance impacts and -programming problems (massisve amounts of data in shared memory). But most -importantly, when a subprocess gets a job and crashes on it, it won't complete -the job, we don't have a choice except gracefully abort it. From a user -perspective "It doesn't work!" there is no much difference to a complete crash. -Well and yes we aim to make it crash proof rather, crashes a bugs and have to -be fixed, point. - -Lumiera will never ever loose work, we don't plan to make a project file, -autosafe way. Lumiera will keep projects in an internal database like format -which consists of a Dumpfile and a contingous written logfile. After a -crash/powerdown whatever, this log just gets replayed. The advantages are -countless, imagine persistent, selective undo and so on. Any other format -(cinelerra2 XML, MXF, ...) will be realized by importer/exporter plugins. - -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] - - - -2. Reinventing the wheel for every new project -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -[grid="all"] -`------------`------------------------------------------------------ -*Problem* Various projects compete and reinvent the wheel -*Severity* Serious (Slows down development time. A lot) -*Solution* Multi-tier design, turn the data handling into a backend and use - whatever UI you prefer -*Required* Yes. Better now that the project hasn't started ---------------------------------------------------------------------- - -Imagine the Linux kernel was tied to the window manager. You would have to -stick with KDE or GNOME and you couldn't improve it! Fortunately it's not like -that for Linux, but it is for some projects. If you want to change the user -interface from QT to wxWidgets or GTK you'll need to rewrite every module. - -If you separate the UI from the project handling engine, you can simply fork -the project and change the UI to one that supports skinning, without having to -do the complicated video-processing stuff. - -Separating the processes has an equivalent for web programming, it's called -"separation of concerns", or multi-tier design. When you suddenly change the -database engine, you don't need to change the whole program, just the database -module. Same goes for changing the UI from HTML to XML or Flash. If they're -separate modules that only communicate through a clearly defined API. - -Example case 1: The Code::Blocks IDE. The compiling engine supports various -compilers, and the engine itself is only a plugin for the main editor. If the -compiler crashes, you only get an error, but the IDE doesn't crash (unless it's -the UI part that's doing something wrong). - -Example case 2: Chessmaster. The user interface and speech synthesis stuff only -call the chess engine, called "theking.exe". Linux chess games also depend on -an engine to do the thinking. - -So I suggest to split the project into four separate tiers (not necessarily -processes): - -. User interface - communicates with the "project" tier, handles the user - events and does the calls. -. The project tier - the main part of the video editor. This one invokes the - renderer and decides which effects to apply, saving them as mere parameters - for later processing. It also tells you where the current pointer for the - track view is. Also calls the rendering engine for the current frame, or for - previews of a certain special effect. Note that if this process keeps running - even if the GUI crashes, later we can restart the GUI and keep working. -. The rendering engine - This one must be a separate process for the reasons - stated in problem #1. This also gives us the advantage that it can work on - the background while we keep working on the project (after all the project is - just a set of data stating which effects to apply to which tracks, and which - files are used for the tracks) - instead of just having a window saying - "Rendering, please wait". Even Adobe Premiere Pro suffered from this problem. - This means that if we put enough effort, we can surpass commercial software - in certain areas. Note that the rendering engine uses the same API than the - project tier, as it works on a copy of the project when doing the final - rendering. -. The video processing wrapper, which has interfaces for different video - processing toolkits (DirectX, GStreamer, etc). This also makes the project - cross-platform. Tiers 1 and 2 can go in one process, and the 3 and 4 in - another (this would make tier 2 a library which defines a C++ Class, and tier - 4 would also be a library which is used by the rendering engine). - -By separating the tiers, these can later become their own projects and overall -the community would receive great benefits. - - -Comments -^^^^^^^^ - -Please look at our design drafts, things will be separated (little different -than you describe here). We reuse things which are benefitful (gavl, ffmpeg, -..) but we are also aware that we reinvent the wheel for some things by -intention. Lumieras goal is not just to glue some existing libraries together -under a new gui, there are already a lot projects trying this way. We rather -aim for a ''Professional'' high performance Video editing solution which does -some things in a different (maybe more complex) way. We do not use existing -frameworks like MLT or gstreamer because we believe that these do not fit our -goals (gstreamer will be supported through plugins). We do not produce yet -another multimedia framework library (this only happen by coincidence) to be -used by others. - -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] - - -3. Lack of a user-friendly and extensible UI. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -[grid="all"] -`------------`------------------------------------------------------ -*Problem* Often, editors provide a very poor and buggy interface. - Examples: Jahshaka doesn't even provide tooltips for the various tools, and - the documentation is poor; In Cinelerra I've noticed some bugs when using the - open dialog, I'd rather have the KDE one, thanks. -*Severity* From Annoying to Serious. -*Solution 1* Use a library that allows you to use different widget - libraries, like wxWidgets. -*Required* Recommended, but not obligatory. -*Solution 2* Write different user interfaces, but they'd be hard to maintain. -*Required*, No. ---------------------------------------------------------------------- - -This problem is complicated, we need a good framework for handling the tracks. -Perhaps this could become a separate project. Ideas are welcome. - - -Comments -^^^^^^^^ - -Joel started working on a GUI recently and making good progress. The UI should -finally be quite flexible as it mostly provides a skeletion where plugins -render to. We have quite a lot ideas about the UI and user input is welcome. -The UI is currently the most separate tier in the design, i'd like to make it a -plugin itself which is loaded when lumiera is started in a gui mode, but it is -to early to say how exactlly it will be integrated, except that we all agree -that GUI is optional and Lumiera can also run headless, script driven. - -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] - - -4. Lack of support for certain video formats or codecs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -[grid="all"] -`------------`------------------------------------------------------ -*Problem* Lack of support for certain video formats or codecs. -*Severity* Critical. -*Workarounds* 1. Give a help page for the user to do his own conversion, but - this is very inelegant, annoying, and a waste of time. 2. Provide conversion - on the fly, and keep a separate "preprocessed" copy of the imported clip in a - separate directory. This is a nice middle ground, IMHO. -*Solution* Use a wrapper library as stated in problem # 2, having a - plugin-based design is recommended. -*Required* Yes. ---------------------------------------------------------------------- - -Some editors like Cinelerra are hardwired into using one format, or have a -phobia to certain formats / codecs (i.e. DivX AVI's). If we separate the -project editing engine from the video handling libraries, we can use unknown -formats by simply providing an input/output plugin. This would allows us to use -files encoded with lossless codecs like -http://lags.leetcode.net/codec.html[Lagarith]. This also provides forward -compatibility for future formats. - - -Comments -^^^^^^^^ - -Lumiera is a video editor we don't care (*cough*, not really true) about video -formats. Everything which comes In and goes Out is defined in plugins which -handle video formats. We currently decided to use 'gavl' because it is a nice -small library which does exactly what we want. Later on gstreamer and other -such kinds of decoder/encoder/processing-pipe libs will be realized. - -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] - - -5. Lack of documentation -~~~~~~~~~~~~~~~~~~~~~~~~ - -[grid="all"] -`------------`------------------------------------------------------ -*Problem* Some video editors have very poor documentation (and that's an - understatement *cough* Jahshaka *cough* ) -*Severity* Critical. -*Solution* Have a team for the documentation. -*Required* Yes. ---------------------------------------------------------------------- - -Nuff said. - - -Comments -^^^^^^^^ - -Quote from Ohloh.net: (http://www.ohloh.net/projects/lumiera)[] - ------------------------------------------------------------- -Extremely well-commented source code - -Lumiera is written mostly in C++. Across all C++ projects on Ohloh, 23% of all -source code lines are comments. For Lumiera, this figure is 46%. This very -impressive number of comments puts Lumiera among the best 10% of all C++ -projects on Ohloh. ------------------------------------------------------------- - - -Nuff saied... Oh well, about user docs we like to get that impressive ratings -there too, any helpers? - -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] - - -6. Lack of cross-platform support -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -[grid="all"] -`------------`------------------------------------------------------ -*Problem* Where's my Windows version? -*Severity* Blocker -*Solution* Use a cross-platform toolkit for the UI. -*Required* Depends, do you plan to make it Cross-Platform? --------------------------------------------------------------------- - -A good example for this is the Code::Blocks IDE, which was thought of being -cross-platform from the beginning. Curiously, at first the project was -Windows-only, and its only F/OSS alternative was Dev-C++ from Bloodshed (eew). -Otherwise you'd have to stick with proprietary applications like Visual C++. - -In Linux there were various IDE's, but they were Linux-only. Since Code::Blocks -uses a cross-platform toolkit (wxWidgets), it can be compiled either in Windows -and Linux. There are RPM's for various distros now that the first public -version (8.02) got out. I've heard that QT is also cross-platform, but I -haven't tried it yet. - -Of course - if you separate the UI from the project engine, someone could make -his own Windows UI for the project. Now what needs to be taken care of, is that -the rendering libraries are cross-platform too. - - -Comments -^^^^^^^^ - -We refuse to make it cross platform intentionally. Most things are written -portable, POSIX compatible, some might need platform specific fixes. But our -target is primary Linux (because thats what we use) secondary any other Free OS -(hopefully we find some testers/maintainers for that). Lumiera ''might'' run on -OSX and patches will be accepted, but it is not a free platform so we don't -care by ourself. Windows due its diffrent system interfaces will be hard to -port, if someone wants to do that, have fun, we will accept patches to, but we -do not support it in *any* way by ourself. - -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] - - -7. Dependency on scripted languages like Python, which make installation a mess -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -[grid="all"] -`------------`------------------------------------------------------ -*Problem* Installation can be a mess if we depend on scripted languages. -*Severity* Annoying, the end user might just conform with another project - that "just works". -*Solution* Make it in C++ or other easily-compilable language. -*Required* VERY recommended. ---------------------------------------------------------------------- - -I've had to install several packages for my distro (whose repository is not as -large as others like Ubuntu's) from source. Some of them depend on very -esoteric scripted languages which I also need to install. And then the -libraries, etc. My suggestion is to free the end user from this burden, and -work on a common language, like C++. - - -Comments -^^^^^^^^ - -At some point a scripting language ''will'' be required, yet to drive the -testsuite, make headless rendering work and so on. We need to provide -installation instructions and/or even bundle this language with Lumiera. This -will likely become a small embedded language like Lua or some kind of forth (or -maybe some scheme?) it should not depend on strange modules which are not part -of the core scripting language distribution (or we shall provide them too), -needs to be worked out. - -- link:ct[] [[DateTime(2008-04-21T11:27:23Z)]] - - - -Author's comments -^^^^^^^^^^^^^^^^^ - -Some of the measures stated in this document are optional, but separating the -processes for the rendering engine, editor and User Interface are the optimal -solution and required to avoid common problems. - - -Discussion ----------- - -Mostly we agree with the general statements in this Design Entry. But there are -some points which don't stand the test of a detailed technical discussion. For -example, you simply can't state it's a 'mistake' not to write code which -similarly runs on windows and *nix. Well. You could try to write it in Java. -See my point? While today it's quite feasible to write office stuff or banking -applications in a cross-platform manner, a video editor still is a different -kind of a beast. - -A similar argumentation holds true for the question, wether or not to use -separate processes and IPC. While it certainly is a good idea to have the X -server or a database running in a separate process, the situation is really -quite different for editing video. Hopefully it's clear why. - -Could you please rework this Design Entry in a way that we can finalize -(accept) it? - -* Please remove the section about windows -* Please separate out things needing technical discussion and are not just - "mistakes", thus retaining only the big picture statements (on which we all - agree) -* How to secure the application against crashes -* If it is viable/desirable to run the gui in a separate process really needs - in-depth technical discussion (create a new Design Entry for this) -* How to deal with the dependencies problem in combination with - plugins/extensions and script languages - -- link:Ichthyostega[] [[DateTime(2008-10-05T01:51:50Z)]] - - -Conclusion ----------- -The October.2008 dev meeting decided to 'drop' this design proposal as is. - -Basically, this text just tells us "to make Lumiera good", and especially it -contains a mixture of topics - -* We fully agree to 80% of the statements made there, but we think those - statements are so very basic and self-evident as to be considered off-topic - here. We are aware of the recurring problems with open source video editing. - That's why we are here. -* The proposal draws conclusions on two technically substantial points, at - which we don't agree. And it fails to provide sufficient (technically sound) - arguments to prove these statements. - -While it is certainly 'desirable' to be cross-platform as much as possible and -especially '''target Microsoft Windows''', we don't see much possibilities with -today's mainstream technology to build an application which is as -technologically demanding as a video editor is. We would end up developing two -or even three sister applications, or we are forced to sacrifice performance -for portability. When put up to face such options, we have a clear preference -to concentrate on a really free and open platform. - -While it is certainly 'desirable' to make the application as robust as -possible, we don't see how '''using multiple separate processes''' could help -us with this goal ''without creating major scalability or performance -problems'' due to the use of shared memory. And, yet more important: we don't -share the basic assumption made in the proposal, namely that video processing -is inherently dangerous. We think the basic algorithms involved are -sufficiently well-known and understandable to implement them in a sound manner. - -'''' -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_dropped/MistakestoAvoid.txt b/doc/devel/rfc_dropped/MistakestoAvoid.txt new file mode 120000 index 000000000..5931c6693 --- /dev/null +++ b/doc/devel/rfc_dropped/MistakestoAvoid.txt @@ -0,0 +1 @@ +../rfc/MistakestoAvoid.txt \ No newline at end of file diff --git a/doc/devel/rfc_dropped/OfficialAssemblyLanguage.txt b/doc/devel/rfc_dropped/OfficialAssemblyLanguage.txt deleted file mode 100644 index a60444951..000000000 --- a/doc/devel/rfc_dropped/OfficialAssemblyLanguage.txt +++ /dev/null @@ -1,161 +0,0 @@ -Design Process : Official Assembly Language -=========================================== - -[grid="all"] -`------------`----------------------- -*State* _Dropped_ -*Date* _2008-08-01_ -*Proposed by* link:PercivalTiglao[] -------------------------------------- - - -Official Assembly Language --------------------------- - -I describe here an optimization that might have to be be taken into account at -the design level. At very least, we should design our code with -auto-vectorization in mind. At the most, we can choose to manually write parts -of our code in assembly language and manually vectorize it using x86 SSE -Instructions or !PowerPC !AltiVec instructions. By keeping these instructions -in mind, we can easily achieve a large increase in speed. - - -Description -~~~~~~~~~~~ - -While the C / C++ core should be designed efficiently and as portable as -possible, nominating an official assembly language or an official platform can -create new routes for optimization. For example, the x86 SSE instruction set -can add / subtract 16 bytes in parallel (interpreted as 8-bit, 16-bit, 32-bit, -or 64-bit integers, or 32-bit/64-bit floats), with some instructions supporting -masks, blending, dot products, and other various instructions specifically -designed for media processing. While the specific assembly level optimizations -should be ignored for now, structuring our code in such a way to encourage a -style of programming suitable for SSE Optimization would make Lumiera -significantly faster in the long run. At very least, we should structure our -innermost loop in such a way that it is suitable for gcc's auto-vectorization. - -The problem is that we will be splitting up our code. Bugs may appear on some -platforms where assembly-specific commands are, or perhaps the C/C++ code would -have bugs that the assembly code does not. We will be maintaining one more -codebase for the same set of code. Remember though, we don't have to do -assembly language now, we just leave enough room in the design to add -assembly-level libraries somewhere in our code. - - -Tasks -~~~~~ - -* Choose an "Official" assembly language / platform. -* Review the SIMD instructions avaliable for that assembly language. -* For example, the Pentium 2 supports MMX instructions. Pentium 3 supports MMX - and SSE Instructions. Early Pentium4s support MMX, SSE, and SSE2 - instructions. Core Duo supports upto SSE4 instructions. AMD announced SSE5 - instructions to come in 2009. -* Consider SIMD instructions while designing the Render Nodes and Effects - architecture. -* Write the whole application in C/C++ / Lua while leaving sections to optimize - in assembly later. (Probably simple tasks or a library written in C) -* Rewrite these sections in Assembly using only instructions we agreed upon. - - -Pros -~~~~ - -Assuming we go all the way with an official assembly language / platform... - -* Significantly faster render and previews. (Even when using a high-level - library like http://www.pixelglow.com/macstl/valarray/[macstl valarray], we - can get 3.6x -- 16.2x the speed in our inner loop. We can probably expect - greater if we hand-optimize the assembly) - - -Cons -~~~~ - -* Earlier architectures of that family will be significantly slower or - unsupported -* Other architectures will rely on C / C++ port instead of optimized assembly -* Redundant Code - - -Alternatives -^^^^^^^^^^^^ - -* We only consider auto-vectorization -- GCC is attempting to convert trivial - loops into common SSE patterns. Newer or Higher level instructions may not be - supported by GCC. This is turned on - http://gcc.gnu.org/projects/tree-ssa/vectorization.html[in GCC4.3 with - specific compiler flags] -* We can consider assembly but we don't officially support it -- We leave the - holes there for people to patch up later. Unofficial ports may come up, and - maybe a few years down the line we can reconsider assembly and start to - reimplement it down the road. -* Find a SIMD library for C/C++ -- Intel's ICC and - http://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Vector-Extensions.html[GCC] both - have non-standard extensions to C that roughly translate to these - instructions. There is also the - http://www.pixelglow.com/macstl/valarray/[macstl valarray library] mentioned - earlier. Depending on the library, the extensions can be platform specific. -* Write in a language suitable for auto-vectorization -- Maybe there exists - some vector-based languages? Fortran might be one, but I don't really know. - - -Rationale -~~~~~~~~~ - -I think this is one of those few cases where the design can evolve in a way -that makes this kind of optimization impossible. As long as we try to keep this -optimization avaliable in the future, then we should be good. - - -Comments --------- - -* I have to admit that I don't know too much about SSE instructions aside from - the fact that they can operate on 128-bits at once in parallel and there are - some cache tricks involved when using them. (you can move data in from memory - without bringing in the whole cache line). Nonetheless, keeping these - assembly level instructions in mind will ease optimization of this Video - Editor. Some of the instructions are high-level enough that they may effect - design decisions. Considering them now while we are still in early stages of - development might prove to be advantagous. Optimize early? Definitely not. - However, if we don't consider this means of optimization, we may design - ourselves into a situation where this kind of optimization becomes - impossible. - -* I don't think we should change any major design decisions to allow for - vectorization. At most, we design a utility library that can be easily - optimized using SIMD instructions. Render Nodes and Effects can use this - library. When this library is optimized, then all Render Nodes and Effects - can be optimized as well. -- link:PercivalTiglao[] - [[DateTime(2008-08-01T16:12:11Z)]] - -* Uhm, the Lumiera core (backend, proc, gui) doesn't do any numbercrunching. - This is all delegated to plugins (libgavl, effects, encoders). I think we - don't need any highly assembler/vector optimized code in the core (well, lets - see). This plugins and libraries are somewhat out of our scope and thats good - so, the people working on it know better than we how to optimize this stuff. - It might be even worthwile to try if when we leave all vectorization out, if - then the plugins can use the vector registers better and we gain overall - performance! - -- link:ct[] [[DateTime(2008-08-03T02:27:14Z)]] - -* Another idea about a probably worthwhile optimization: gcc can instumentate - code for profileing and then do arc profileing and build it a second time - with feedback what it learnd from the profile runs, this mostly affects - branch prediction and can give a reasonable performance boost. If somone - likes challenges, prepare the build system to do this: -. build it with -fprofile-arcs -. profile it by running ''carefully'' selected benchmarks and tests. -. rebuild it again this time with -fbranch-probabilities -. PROFIT - -- link:ct[] [[DateTime(2008-08-03T02:27:14Z)]] - -* I've discussed general ideas around, and I agree now that "core Lumiera" is - not the place to think of these kinds of optimizations. So I'll just move - this over to dropped. -- link:PercivalTiglao[] - [[DateTime(2008-08-04T18:33:58Z)]] - -'''' -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_dropped/OfficialAssemblyLanguage.txt b/doc/devel/rfc_dropped/OfficialAssemblyLanguage.txt new file mode 120000 index 000000000..75b5afb1d --- /dev/null +++ b/doc/devel/rfc_dropped/OfficialAssemblyLanguage.txt @@ -0,0 +1 @@ +../rfc/OfficialAssemblyLanguage.txt \ No newline at end of file diff --git a/doc/devel/rfc_dropped/TagCloudsOnResources.txt b/doc/devel/rfc_dropped/TagCloudsOnResources.txt deleted file mode 100644 index 2c36d76dc..000000000 --- a/doc/devel/rfc_dropped/TagCloudsOnResources.txt +++ /dev/null @@ -1,81 +0,0 @@ -Design Process : Tag Clouds for Resources -========================================= - -[grid="all"] -`------------`----------------------- -*State* _Dropped_ -*Date* _2008-07-15_ -*Proposed by* link:PercivalTiglao[] -------------------------------------- - - -Tag Clouds for Resources ------------------------- - -Perhaps a Cloud of tags is unnecessary, but tagging resources similar to -Youtube or like Tag Clouds allows for efficient searching and filtering. Anyone -who uses the web would know how to use them. If a "Cloud of Tags" approach is -used, then organizing the tags by some sort of frequency would be useful. IE: -the more a specific tag is used, the larger it gets, or perhaps the more often -that tag is searched on. - - -Description -~~~~~~~~~~~ - - - -Tasks -~~~~~ - - -Pros -~~~~ - -* Simple GUI Concept -* Eases management of resources with Search -* Orthogonal to other resource management schemes like Folders - - -Cons -~~~~ - - -Alternatives -~~~~~~~~~~~~ - - - -Rationale -~~~~~~~~~ - - - - - -Comments --------- - -* Note: I was inspired with this idea during an email conversation with - Rick777. -- link:PercivalTiglao[] [[DateTime(2008-07-17T14:29:57Z)]] - -* Agreed, this is usefull. Also, more advanced config rules can make use of - such tags and wiring can depend on them, for example to route your dialogue - audio to another global bus than the music or ambiance. - -- link:Ichthyostega[] [[DateTime(2008-07-27T22:23:38Z)]] - - - -Conclusion ----------- - -This Design Proposal is 'superseded' by a much more advanced proposal: -link:DelectusShotEvaluator[Delectus] - -(Dropping it doesn't mean disapproval) - - -'''' - - -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_dropped/TagCloudsOnResources.txt b/doc/devel/rfc_dropped/TagCloudsOnResources.txt new file mode 120000 index 000000000..c3d9d0035 --- /dev/null +++ b/doc/devel/rfc_dropped/TagCloudsOnResources.txt @@ -0,0 +1 @@ +../rfc/TagCloudsOnResources.txt \ No newline at end of file diff --git a/doc/devel/rfc_dropped/UnitTests_Python.txt b/doc/devel/rfc_dropped/UnitTests_Python.txt deleted file mode 100644 index 9d71205e6..000000000 --- a/doc/devel/rfc_dropped/UnitTests_Python.txt +++ /dev/null @@ -1,82 +0,0 @@ -Design Process : Unit Tests Python -================================== - - -[grid="all"] -`------------`----------------------- -*State* _Dropped_ -*Date* _2007-06-17_ -*Proposed by* link:Ichthyostega[] -------------------------------------- - - -UnitTests in Python -------------------- - -Use the Python scripting language for the actual Unit Tests and access the -Cinelerra Code via SWIG - - -Description -~~~~~~~~~~~ - -Define Test classes in Python, using e.g. the link:PyUnit[] framework of the -Python Standard lib. The SWIG compiler can generate wrapper code automatically, -so we can access the C++ Classes and Facilities of Cinelerra as Python Modules -and Classes. The Classes to be tested in Cinelerra need to provide some -Interface for carrying out these tests (and this is one of the main benefits of -the whole Test driven aproach). - - -Tasks -~~~~~ - - * Find out how the SWIG generated wrappers play together with Python's List - and Map types. Without the ability to use the latter in the tests, this - whole proposal is rather pointless. - * Think on how we can test video data processing (at least in its basics, e.g. - does additive overlay work) - - -Pros -~~~~ - -Programming Unit and Self tests in a Scripting language facillates this task. -The X-Language bindings are quite usable today. As a side effect, it helps to -get a clean program structure, because the tests need some Interface and/or -some object factories to create the test candidates. Python is proposed, -because it is fairly mainstream, has a flat learning curve and but is -moderately modern and functional-style at the same time. - -Cons -~~~~ - - * Adds to the complexity - * Some old-style hackers have a quite distinct aversion against Python - - -Alternatives -~~~~~~~~~~~~ - -Rationale -~~~~~~~~~ - -Why am I proposing this? Out of lazyness. Python is there, many devs (on linux) -have some Python skills, SWIG is not overly complicated to use. - -And last but not least: just to get the discussion going... ;-) - - -Comments --------- - -* I'd rather consider to use some embedded language in cinelerra which we can - use to drive tests, should be something smaller and more sane than python. - Needs certainly more discussion. For simple unit tests some C/C++ harness and - bit shell scripting would suffice, I really want to integrate this with - link:NoBug[]. - -- link:ct[] [[DateTime(2007-06-17T17:32:27Z)]] - - -'''' -Back to link:../DesignProcess[] diff --git a/doc/devel/rfc_dropped/UnitTests_Python.txt b/doc/devel/rfc_dropped/UnitTests_Python.txt new file mode 120000 index 000000000..8e20f915a --- /dev/null +++ b/doc/devel/rfc_dropped/UnitTests_Python.txt @@ -0,0 +1 @@ +../rfc/UnitTests_Python.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/AllPluginInterfacesAreC.txt b/doc/devel/rfc_final/AllPluginInterfacesAreC.txt new file mode 120000 index 000000000..e7c654b8f --- /dev/null +++ b/doc/devel/rfc_final/AllPluginInterfacesAreC.txt @@ -0,0 +1 @@ +../rfc/AllPluginInterfacesAreC.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/ArchitectureOverview.txt b/doc/devel/rfc_final/ArchitectureOverview.txt new file mode 120000 index 000000000..381b9c79f --- /dev/null +++ b/doc/devel/rfc_final/ArchitectureOverview.txt @@ -0,0 +1 @@ +../rfc/ArchitectureOverview.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/CCodingStyleGuide.txt b/doc/devel/rfc_final/CCodingStyleGuide.txt new file mode 120000 index 000000000..17dc2f359 --- /dev/null +++ b/doc/devel/rfc_final/CCodingStyleGuide.txt @@ -0,0 +1 @@ +../rfc/CCodingStyleGuide.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/CodingStyle.txt b/doc/devel/rfc_final/CodingStyle.txt new file mode 120000 index 000000000..3542b0cd7 --- /dev/null +++ b/doc/devel/rfc_final/CodingStyle.txt @@ -0,0 +1 @@ +../rfc/CodingStyle.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/DataBackend.txt b/doc/devel/rfc_final/DataBackend.txt new file mode 120000 index 000000000..72d42844e --- /dev/null +++ b/doc/devel/rfc_final/DataBackend.txt @@ -0,0 +1 @@ +../rfc/DataBackend.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/DevelopmentFramework.txt b/doc/devel/rfc_final/DevelopmentFramework.txt new file mode 120000 index 000000000..0f6ff716e --- /dev/null +++ b/doc/devel/rfc_final/DevelopmentFramework.txt @@ -0,0 +1 @@ +../rfc/DevelopmentFramework.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/DistributedDevelopmentFramework.txt b/doc/devel/rfc_final/DistributedDevelopmentFramework.txt new file mode 120000 index 000000000..ea5c1dcf9 --- /dev/null +++ b/doc/devel/rfc_final/DistributedDevelopmentFramework.txt @@ -0,0 +1 @@ +../rfc/DistributedDevelopmentFramework.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/EDLsAreMetaClips.txt b/doc/devel/rfc_final/EDLsAreMetaClips.txt new file mode 120000 index 000000000..3a2ad62ed --- /dev/null +++ b/doc/devel/rfc_final/EDLsAreMetaClips.txt @@ -0,0 +1 @@ +../rfc/EDLsAreMetaClips.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/GloballInitialization.txt b/doc/devel/rfc_final/GloballInitialization.txt new file mode 120000 index 000000000..cca5f8b26 --- /dev/null +++ b/doc/devel/rfc_final/GloballInitialization.txt @@ -0,0 +1 @@ +../rfc/GloballInitialization.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/HowToProceed.txt b/doc/devel/rfc_final/HowToProceed.txt new file mode 120000 index 000000000..d282ce97b --- /dev/null +++ b/doc/devel/rfc_final/HowToProceed.txt @@ -0,0 +1 @@ +../rfc/HowToProceed.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/InterfaceNamespaces.txt b/doc/devel/rfc_final/InterfaceNamespaces.txt new file mode 120000 index 000000000..fe26b9f57 --- /dev/null +++ b/doc/devel/rfc_final/InterfaceNamespaces.txt @@ -0,0 +1 @@ +../rfc/InterfaceNamespaces.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/LumieraDesignProcess.txt b/doc/devel/rfc_final/LumieraDesignProcess.txt new file mode 120000 index 000000000..d17096282 --- /dev/null +++ b/doc/devel/rfc_final/LumieraDesignProcess.txt @@ -0,0 +1 @@ +../rfc/LumieraDesignProcess.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/LumieraForwardIterator.txt b/doc/devel/rfc_final/LumieraForwardIterator.txt new file mode 120000 index 000000000..ea1de15fe --- /dev/null +++ b/doc/devel/rfc_final/LumieraForwardIterator.txt @@ -0,0 +1 @@ +../rfc/LumieraForwardIterator.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/MakeSconsTheOfficialBuildSystem.txt b/doc/devel/rfc_final/MakeSconsTheOfficialBuildSystem.txt new file mode 120000 index 000000000..0f912dab7 --- /dev/null +++ b/doc/devel/rfc_final/MakeSconsTheOfficialBuildSystem.txt @@ -0,0 +1 @@ +../rfc/MakeSconsTheOfficialBuildSystem.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/Manifest.txt b/doc/devel/rfc_final/Manifest.txt new file mode 120000 index 000000000..79f57e208 --- /dev/null +++ b/doc/devel/rfc_final/Manifest.txt @@ -0,0 +1 @@ +../rfc/Manifest.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/MarbleMode.txt b/doc/devel/rfc_final/MarbleMode.txt new file mode 120000 index 000000000..e0ae6aa60 --- /dev/null +++ b/doc/devel/rfc_final/MarbleMode.txt @@ -0,0 +1 @@ +../rfc/MarbleMode.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/MasterRepositorySetup.txt b/doc/devel/rfc_final/MasterRepositorySetup.txt new file mode 120000 index 000000000..62979ff09 --- /dev/null +++ b/doc/devel/rfc_final/MasterRepositorySetup.txt @@ -0,0 +1 @@ +../rfc/MasterRepositorySetup.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/MonthlyDeveloperMeetingOnIRC.txt b/doc/devel/rfc_final/MonthlyDeveloperMeetingOnIRC.txt new file mode 120000 index 000000000..40a5b74c4 --- /dev/null +++ b/doc/devel/rfc_final/MonthlyDeveloperMeetingOnIRC.txt @@ -0,0 +1 @@ +../rfc/MonthlyDeveloperMeetingOnIRC.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/NoBugFlags.txt b/doc/devel/rfc_final/NoBugFlags.txt new file mode 120000 index 000000000..e1c41a2b5 --- /dev/null +++ b/doc/devel/rfc_final/NoBugFlags.txt @@ -0,0 +1 @@ +../rfc/NoBugFlags.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/ProcBuilder.txt b/doc/devel/rfc_final/ProcBuilder.txt new file mode 120000 index 000000000..05c489045 --- /dev/null +++ b/doc/devel/rfc_final/ProcBuilder.txt @@ -0,0 +1 @@ +../rfc/ProcBuilder.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/ProcHighLevelModel.txt b/doc/devel/rfc_final/ProcHighLevelModel.txt new file mode 120000 index 000000000..5b8ffe111 --- /dev/null +++ b/doc/devel/rfc_final/ProcHighLevelModel.txt @@ -0,0 +1 @@ +../rfc/ProcHighLevelModel.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/ProcPlacementMetaphor.txt b/doc/devel/rfc_final/ProcPlacementMetaphor.txt new file mode 120000 index 000000000..0c27e6afc --- /dev/null +++ b/doc/devel/rfc_final/ProcPlacementMetaphor.txt @@ -0,0 +1 @@ +../rfc/ProcPlacementMetaphor.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/RepositorySetup.txt b/doc/devel/rfc_final/RepositorySetup.txt new file mode 120000 index 000000000..beb22c4bc --- /dev/null +++ b/doc/devel/rfc_final/RepositorySetup.txt @@ -0,0 +1 @@ +../rfc/RepositorySetup.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/Roadmap-first.txt b/doc/devel/rfc_final/Roadmap-first.txt new file mode 120000 index 000000000..eb67764c2 --- /dev/null +++ b/doc/devel/rfc_final/Roadmap-first.txt @@ -0,0 +1 @@ +../rfc/Roadmap-first.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/ScriptingLanguage.txt b/doc/devel/rfc_final/ScriptingLanguage.txt new file mode 120000 index 000000000..085085137 --- /dev/null +++ b/doc/devel/rfc_final/ScriptingLanguage.txt @@ -0,0 +1 @@ +../rfc/ScriptingLanguage.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/ThreadsSignalsAndImportantManagementTasks.txt b/doc/devel/rfc_final/ThreadsSignalsAndImportantManagementTasks.txt new file mode 120000 index 000000000..239615881 --- /dev/null +++ b/doc/devel/rfc_final/ThreadsSignalsAndImportantManagementTasks.txt @@ -0,0 +1 @@ +../rfc/ThreadsSignalsAndImportantManagementTasks.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/TimeHandling.txt b/doc/devel/rfc_final/TimeHandling.txt new file mode 120000 index 000000000..b0bbe82a4 --- /dev/null +++ b/doc/devel/rfc_final/TimeHandling.txt @@ -0,0 +1 @@ +../rfc/TimeHandling.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/TimelineSequenceOutput.txt b/doc/devel/rfc_final/TimelineSequenceOutput.txt new file mode 120000 index 000000000..c16a20c51 --- /dev/null +++ b/doc/devel/rfc_final/TimelineSequenceOutput.txt @@ -0,0 +1 @@ +../rfc/TimelineSequenceOutput.txt \ No newline at end of file diff --git a/doc/devel/rfc_final/VersionNumberScheme.txt b/doc/devel/rfc_final/VersionNumberScheme.txt new file mode 120000 index 000000000..40cf75236 --- /dev/null +++ b/doc/devel/rfc_final/VersionNumberScheme.txt @@ -0,0 +1 @@ +../rfc/VersionNumberScheme.txt \ No newline at end of file diff --git a/doc/devel/rfc_parked/ClipCatalogingSystem.txt b/doc/devel/rfc_parked/ClipCatalogingSystem.txt deleted file mode 100644 index 8149c23ac..000000000 --- a/doc/devel/rfc_parked/ClipCatalogingSystem.txt +++ /dev/null @@ -1,128 +0,0 @@ -Design Process : Clip Cataloging System -======================================= - -[grid="all"] -`------------`----------------------- -*State* _Parked_ -*Date* _2008-07-26_ -*Proposed by* link:JordanN[] -------------------------------------- - -Clip Cataloging System ------------------------ - -A system for storing, organizing, and retrieving assets, such as images and -videos. - - -Description -~~~~~~~~~~~ - -Organizations that work with video, and even home users, tend to have massive -collections of stock videos and images that they will need to find and use in -their projects. A Linux-based system is needed to help them to organize, tag, -and retrieve assets from those collections. Being able to find the clips the -user needs and bring them into his timeline, will mean that the user will be -able to more rapidly complete his project. - -This could be implemented as a separate application, but integrated for use in -a Linux-based video workflow suite, including apps like Lumiera and Blender. - - -Tasks -~~~~~ - -* Identify ways in which existing groups organize their collections. -* Determine pros / cons of each method -* Implement a solution that will be modular enough for other content creation - projects to also use - - -Pros -~~~~ - -* Faster, more efficient workflow - - -Cons -~~~~ -Not directly a part of Lumiera. If not implemented separately, could cause -undue bloat. - - -Alternatives -~~~~~~~~~~~~ -Storage-based organization. User must remember where files are, and must store -them correctly. Not clip-based, so the entire video must be imported and the -desired portion selected. - - -Rationale -~~~~~~~~~ - - -Comments --------- - -* Such is planned, but as you pointed out, this would be a rather standalone - application which needs a lot of efforts to be implemented. We don't have the - development power to do that now. If someone wants to work on that, please - contact me. General idea is to put all kinds of resources (Footage, Clips, - Effects, Subprojects, Sounds ....) into a database with then gets - tagged/attributed in different ways (implicit things like 'filename', 'type', - 'length'; automatic deduceable things like 'Exposure', 'Timecode', ...; And - manual tags like: who was on set, location, ....). Then present this all in a - *good* GUI (by default just showing filesysten like) but one can define - queries on this database and the generated views will then be storeable. -Back to Lumiera, for now we will likely just use 'normal' file open dialogs -until the above system becomes available. - -- link:ct[] [[DateTime(2008-07-26T08:31:42Z)]] -* Yes, it's indeed an important feature we should care for. But cehteh is - right, we have more important things to do first. But feel free to target it. -* Also, we'd need integration with production support systems, for example - http://celtx.com/[CELTX]. -* The interface to the Lumiera App would be to populate the asset manager with - the required assets - -- link:Ichthyostega[] [[DateTime(2008-07-27T22:19:38Z)]] - - -Videos, Audio, Clips and Resources Manager by using plugins for FOSS GPL -"Library & Collections Management" programs. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The video and audio raw material, clips, etc could be managed using code that -is already available in project that carry out the same tasks. For example as -library managers, or media (video, audio or CD) collections, Integrated -Library Systems (ILS). - -Examples of a library management program ; - -. Kete - http://kete.net.nz/[] -. Koha - http://www.koha.org/[] -. link:GreenStone[] - http://www.greenstone.org/[] -. Evergreen - http://open-ils.org/faq.php[] - -An additional benefit to using "library" managers, is that it can handle -interloans, referencing of "other" (people's/organization's) libraries, -numbering systems, descriptions, and classifications, thousands to millions of -items, search systems, review and comment systems, plus the benefits of open -source that allow the expansion of features easily. The use of task oriented -programs in this way, makes use of established code, that has been developed by -experts in their field. Any database system would be useful for managing all -these media. But one that has been developed by the people that have been -working with cataloging systems for a long time is likely to do well. Plus it -can be readily improved, by people who do not have to know the first thing -about how to design video editing programs. The program also gets improved -because of it own community, which adds features or performance to Lumiera, -without even having to "drive" the development.. ---link:Tree[][[DateTime(2008-08-27T20:38:00NZ)]]. - -'''' - -Parked until someone cares -~~~~~~~~~~~~~~~~~~~~~~~~~~ -Decided on Developer meeting - - Do 14 Apr 2011 02:52:30 CEST Christian Thaeter - - -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_parked/ClipCatalogingSystem.txt b/doc/devel/rfc_parked/ClipCatalogingSystem.txt new file mode 120000 index 000000000..1f0872d0b --- /dev/null +++ b/doc/devel/rfc_parked/ClipCatalogingSystem.txt @@ -0,0 +1 @@ +../rfc/ClipCatalogingSystem.txt \ No newline at end of file diff --git a/doc/devel/rfc_parked/DelectusShotEvaluator.txt b/doc/devel/rfc_parked/DelectusShotEvaluator.txt deleted file mode 100644 index 0d5b55a88..000000000 --- a/doc/devel/rfc_parked/DelectusShotEvaluator.txt +++ /dev/null @@ -1,397 +0,0 @@ -[grid="all"] -`------------`----------------------- -*State* _Parked_ -*Date* _2008-09-21_ -*Proposed by* link:nasa[] -------------------------------------- - - -Delectus Shot Evaluator ------------------------ -This is a brain dump about the shot evaluator subproject. - - -Description -~~~~~~~~~~~ - - -Brainstorm on Delectus -~~~~~~~~~~~~~~~~~~~~~~ -Some (many) of the ideas presented herein come from the various parties -involved in the Lumiera discussion list and IRC channel #lumiera. -http://lists.lumiera.org/pipermail/lumiera/2008-September/000053.html[] -- the -main discussion thread - -Additionally, a lot of great concepts for how to streamline the interface are -derived in part from link:KPhotoAlbum[]. - -I use tags, keywords, and metadata almost interchangeably, with the exception -that metadata includes computer generated metadata as well. These are not tags -in the conventional sense -- they don't have to be text. In fact the planned -support (please add more!) is: - - * Text -- both simple strings (tags) and blocks - * Audio -- on the fly (recorded from the application) or pregenerated - * Video -- same as audio - * Link -- back to a Celtx or other document resource, forward to a final cut, - URL, etc - * Still image -- inspiration image, on set details, etc - * ID -- such as the serial number of a camera used, the ISBN of a book to be - cited, etc - -As such, the tags themselves can have metadata. You can see where this is -going... - -Also, the tags are applied to "clips" -- which I use interchangeably between -source material imported into the application and slice of that material that -tags are applied to. Any section of a video or audio source can have tags -applied to it. - - -Two key functions: assign metadata and filter by metadata. - -clips are one thing; but in reality most clips are much longer than their -interesting parts. Especially for raw footage, the interesting sections of a -clip can be very slim compared to the total footage. Here is a typical workflow -for selecting footage: - -. Import footage. -. Remove all footage that is technically too flawed to be useful. -. Mark interesting sections of existing clips, possibly grouped into different - sections. -. Mark all other footage as uninteresting. -. Repeat 3-4 as many times as desired. - -Some key points: - - * Import and export should be as painless and fast as possible. - * Technically flawed footage can be both manual and computer classified. - * In some cases (e.g. documentaries, dialog) audio and video clips/footage can - follow different section processes. - It is possible to use video from footage with useless audio or use audio - from footage with useless video. - * "Interesting" is designed to be broad and is explained below. - * steps 2-5 can be performed in parallel by numerous people and can span many - different individual clips. - -In simple editors like Kino or iMovie, the fundamental unit used to edit video -is the clip. This is great for a large number of uses, such as home videos or -quick Youtube postings, but it quickly limits the expressive power of more -experienced engineers in large scale productions (which are defined for the -purposes of this document to include more than 2 post-production crew members). -The clip in those editors is trimmed down to include only the desired footage, -and these segments are coalesced together into some sort of coherent mess. - -The key to adequate expressive power is as follows: - - * Well designed, fast metadata entry. Any data that can be included should by - default, and ideally the metadata entry process should run no less than - about 75% as fast as simple raw footage viewing. Powerful group commands - that act on sections of clips and also grouping commands that recognize the - differences between takes and angles (or individual mics) enhance and speed - up the process. - * Good tools to classify the metadata into categories that are actually - useful. Much of the metadata associated with a clip is not actively used in - any part of the footage generation. - * Merging and splicing capabilities. The application should be smart enough to - fill in audio if the existing source is missing. For example, in a recent - project I was working on a camera op accidently set the shotgun mike to test - mode, ruining about 10% of the audio for the gig. I was running sound, and - luckily I had a backup copy of the main audio being recorded. This - application should, when told that these two are of the same event at the - same time, seamlessly overlay the backup audio over the section of the old - audio that has been marked bad and not even play the bad audio. This is just - background noise, and streamlining the immense task of sorting through - footage needs to be simplified as much as possible. - * Connection to on site documentation and pre-production documentation. When - making decisions about what material to use and how to classify it, it is - essential to use any tools and resources available. The two most useful are - onsite documentation (what worked/didn't work, how the weather was, pictures - of the setup, etc all at the shoot) and pre-production (what the ideal scene - would be, what is intended, etc). Anything else that would be useful should - be supported as well. - * Be easily accessible when making the final cut. Lumiera is, if the - application gets up to speed, going to serve primarily to render effects, - finalize the cut, and fine tune what material best fits together. Any - metadata, and certainly any clipping decisions, should be very visible in - Lumiera. - * Notes, notes, notes! The application should support full multimedia notes. - These differ from (4) in that they are generated during the CLASSIFICATION - process, not before. This fits in with (5) as well -- Lumiera should display - these notes prominently on clip previews. The main way for multiple parties - to communicate and even for a single person to stay organized is to add in - notes about tough decisions made and rationale, questionable sections, etc. - These notes can be video, audio, text, etc from one of the clips, from the - machine used to edit (such as using a webcam or microphone), or over the - network (other people's input). - - -Too technically flawed -^^^^^^^^^^^^^^^^^^^^^^ -A clip is said to be too technically flawed if it has no chance of making it to -the final product whatsoever. This does not, however, preclude its use -throughout the post-production process; for example, part of a clip in which -the director describes his vision of the talent's facial expression in a -particular scene is never going to make it into the final product, but is -invaluable in classifying the scene. In this case, the most reasonable place to -put the clip would be as a multimedia note referenced by all takes/angles of -the scene it refers to. - -As mentioned above, flawed video doesn't necessarily mean flawed audio or -vice-versa. - - -Interesting -^^^^^^^^^^^ -An "interesting" clip is one that has potential -- either as a metadata piece -(multimedia note, talent briefing, etc) or footage (for the final product OR -intermediary step). The main goal of the application is to find and classify -interesting clips of various types as quickly as possible. - - -Parallel Processing -^^^^^^^^^^^^^^^^^^^ -Many people, accustomed to different interfaces and work styles, should be able -to work on the same project and add interactive metadata at the same time. - - -Classification interface -++++++++++++++++++++++++ -The classification interface is divided into two categories: technical and -effective. Technical classification is simply facts about a clip or part of a -clip: what weather there is, who is on set, how many frames are present, the -average audio level, etc. Effective classification allows the artist to express -their feelings of the subjective merits (or failures) of a clip. - - -DCMS -^^^^ -The project is organized around a distributed content management system which -allows access to all existing materials at all times. Content narrowing allows -for a more digestible amount of information to process, but everything is -non-destructive; every change to the clip structure and layout is recorded, -preferably with a reason as to why it was necessary or desired. - - -Content narrowing -^^^^^^^^^^^^^^^^^ -With all of the information of an entire production available from a single -application, information overload is easy. Content narrowing is designed to fix -that by having parts of individual clips, metadata, or other files be specific -to one aspect of the overall design. This allows for much more successful use -of the related information and a cleaner, streamlined layout. As an example, -metadata involving file size has no effect whatsoever on the vast majority of -most major decisions -- the answer is almost always "whatever it takes." Thus, -it would not appear most of the time. Content narrowing means that it is easy -to add back footage -- "widen the view" one step, add it back, and "narrow the -view" again. - - -Multiple cuts -^^^^^^^^^^^^^ -There is no need to export a final cut from this application; it merely is the -first step in the post-production chain. It is the missing link between -receiving raw footage from the camera and adding the well executed scenes to -the timeline. What should come out of the application is a classification of - - -Situational, take, and instance tagging -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -This is VERY powerful. The first step to using the application is to mark which -scenes are the same in all source clips -- where same means that they contain -sections which would both not run. This can include multiple takes, different -microphones or camera angles, etc. The key to fast editing is that the -application can edit metadata for the situation (what is actually going on IN -THE SCENE), take (what is actually going on IN THIS SPECIFIC RUN), and instance -(what is actually going on IN THIS CLIP). If editing a situation, the other -referenced clips AUTOMATICALLY add metadata and relevant sections. This can be -as precise and nested as desired, though rough cuts for level one editing -(first watchthrough after technically well executed clips have been selected) -and more accurate ones for higher levels is the recommended method. - - -Subtitling -^^^^^^^^^^ -This came up on the discussion list for Lumiera, and it will be supported, -probably as a special tag. - - -nasa's Laws of Tagging -^^^^^^^^^^^^^^^^^^^^^^ -. There is always more variety in data than tags. There are always more - situations present in the data than can be adequately expressed with any - (reasonable) number of tags. This is OK. All that is needed is the minimum - set of unique tags to progress to the next cycle without losing editing - intent or the ability to rapidly evaluate many situations. -. Many tags are used many times. "Outdoors" will be a very, very common tag; so - will "redub." If conventional names are decided upon and stuck to, it is - significantly easier to map the complex interactions between different - content situations. -. Avoid compound tags. Do not have "conversation_jill_joe" as a tag; use - "conversation," "jill," and "joe" instead. It is very easy to search for - multiple tags and very hard to link data that doesn't use overlapping tags. - - - - - - - - - - -The interface -- random idea -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This is not meant to be a final interface design, just something I wrote up to -get ideas out there. - -key commands - mutt/vim-style -- much faster than using a mouse, though GUI supported. - Easy to map to joystick, midi control surface, etc. -Space stop/start and tag enter Tab (auto pause) adds metadata special Tracks -have letters within scenes -- Audio[a-z], Video[a-z], Other[a-z] (these are not -limits) -- or names. Caps lock adds notes. This is really, really fast. It -works anywhere. This means that up to 26 different overlapping metadata -sections are allowed. - -Prompting Prompting for metadata is a laborious, time-consuming process. There -is no truly efficient way to do it. This application uses a method similar to -link:KPhotoAlbum[]. When the space key is held and a letter is pressed, the tag -that corresponds to that letter is assigned to the track for the duration of -the press. (If the space is pressed and no other key is pressed at the same -time, it stops the track.) For example, suppose that the following mapping is -present: -o = outside -x = extra -p = protagonist -c = closeup - -Then holding SPACE over a section and pressing one of these keys would assign -the tag to the audio AND video of the section over which the space was held. If -instead just the key is pressed (without space being held), that tag is -assigned to the section over which it is held. This is very fast and maps well -to e.g. PS3 controller or MIDI control. - -If LALT is held down instead of SPACE, the audio is effected instead. If RALT -is held, just the video is effected. - -In order to support scenario/take/clip tagging: - The default is situation. If the keybinding to x is: - x = t:extra ; effect only take - x = ts:extra ; effect take and scenario - x = c:extra ; extra only visible in this clip! - x = tc:extra ; this take and clip show the extra - etc - -Other keyargs (the part in front of the colon) can be added to account for -other uses (e.g. l = all taken on the same location). - -Tab is pressed to add metadata mappings. Tab is pressed to enter metadata edit -mode; this pauses video. Then press any key to map; and type the tag to -associate (with space, multiple tags can be added.). The following specials are -defined: - [:keyarg:]:TAG is special tagging for scenario/take/clip. - !TAG removes TAG if it is present. This is useful because it allows huge - sections of the clip to be defined as a certain tag, then have parts - removed later. - a:TAG applies TAG only to the audio. - v:TAG applies TAG only to the video. - p:PATH adds a link to PATH as a special tag. - -(This will have a nice GUI as well, I just will always use the keyboard method - so I am describing it first. Mapping configurations can be stored in a - separate file, as a user config, or in the specific project.) - -If ESC is pressed, all currently ranged tags are ended. - -Finally, if single_quote is pressed without SPACE or {L,R}ALT down, it marks an -"interesting location." Pressing SHIFT+single_quote goes to the next -"interesting location" and pressing CNTRL+' goes to the previous "interesting -location." This allows for very quick review of footage. - - - - - - - - - - - - - - - -Comments --------- - - -Rating - Quantitative Rating as well as Qualitative Tagging -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The importance/value of the video for various factors uses, can vary through -the video. It would be helpful to have the ability to create continuous ratings -over the entire track. Ratings would be numerical. Automatic clip -selection/suggestion could be generated by using algorithms to compute the -usefulness of video based on these ratings (aswell as "boolean -operations"/"binary decisions" done with tags). The ratings could be viewed -just like levels are - color coded and ovelayed on track thumbnails. - -- Tree 2008-10-25 - - -link:MultiView[] - useful for concurrent ratings input -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -It would be convenient to have an ability to view the different tracks (of the -same scene/time sequence) at once, so the viewer can input their ratings of the -video "on the fly", including a priority parameter that helps decide which -video is better than what other video.See the GUI brainstorming for a viewer -widget, and key combinations that allow both right and left hand input, that -could be used for raising/lowing ratings for up to six tracks at once. - -- Tree 2008-10-25 - - -I like the idea of rating clips (or rather, takes) a lot. It would be cool to -include both "hard," "relative," and "fuzzy" rating. Hard is an exactly defined -value (scaled 0-1) that puts the clip in an exact location in the queue. -Relative means that one is higher or lower rated than another. Fuzzy is a -slider which is approximate value, and there is some randomness. The best part -is that these can be assigned to hardware sliders/faders. Pressure sensitive -buttons + fuzzy ratings = really easy entry interface. Just hit as hard as -needed! Multiple tracks at once also an astounding idea. I could image some -sort of heap (think binary heap, at least for the data structure) which -determines the priorities and decides which clips are played. Then the highest -rated clips are played first, down to the worst. - -- link:NicholasSA[] 2009-01-04 - - -Possible Collaboration with the people from Ardour? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -I guess if the thing can do all the things we talked about here, it would be -perfectly suitable for sound classification too, and maybe could fill another -gap in FOSS: Audio Archival Software, like this: -http://www.soundminer.com/SM_Site/Home.html[] (which is very expensive)... -maybe the Ardour people would be interested in a collaboration on this? - -I like the suggestion of sound classification with a similar (or, even better, -identical) evaluator. link:SoundMiner[] looks interesting, but like you say -very expensive. I'm a sound guy, so I feel your pain... - -- link:NicholasSA[] 2009-01-04 - - -Parked -~~~~~~ - -Decided on Developer meeting, until someone wants to investigate this further. - - Do 14 Apr 2011 02:52:30 CEST Christian Thaeter - - -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_parked/DelectusShotEvaluator.txt b/doc/devel/rfc_parked/DelectusShotEvaluator.txt new file mode 120000 index 000000000..d6de4100e --- /dev/null +++ b/doc/devel/rfc_parked/DelectusShotEvaluator.txt @@ -0,0 +1 @@ +../rfc/DelectusShotEvaluator.txt \ No newline at end of file diff --git a/doc/devel/rfc_parked/DesignRenderNodesInterface.txt b/doc/devel/rfc_parked/DesignRenderNodesInterface.txt deleted file mode 100644 index 201c34a72..000000000 --- a/doc/devel/rfc_parked/DesignRenderNodesInterface.txt +++ /dev/null @@ -1,128 +0,0 @@ -[grid="all"] -`------------`----------------------- -*State* _Parked_ -*Date* _2008-03-06_ -*Proposed by* link:Ichthyostega[] -------------------------------------- - - -Design the Render Nodes interface ---------------------------------- -In the current design, the low-level model is comprised of "Render Nodes"; -Proc-Layer and Backend carry out some colaboration based on this node network. - + -Three different interfaces can be identified - * the node wiring interface - * the node invocation interface - * the processing function interface - - -Description -~~~~~~~~~~~ -Render Nodes are created and wired by the Builder in the Proc-Layer. On the -other hand, the rendering process is controlled by the backend, which also -provides the implementation for the individual data processing tasks. To create -a result, output nodes are ''pulled'' via the invocation interface, resulting -in the affected nodes to recursively pull their predecessor(s). In the course -of this call sequence, the nodes activate their processing function to work on -a given set of buffers. Moreover, we plan to use the render network also for -gathering statistics. - -'''Note''': Render Node is an internal interface used by Proc-Layer and - activated by the Backend. Plugins are planned to be added via Adapter nodes. - Thus the Render Node interface needs ''not'' to be exported. - - -the wiring interface -^^^^^^^^^^^^^^^^^^^^ -This part of the design defines how nodes can be combined and wired up by the -builder to form a network usable for rendering. For this purpose, the -link:ProcNode[] is used as a shell / container, which is then configured by a -const WiringDescriptor. Thus, the node gets to know its predecessor(s) and is -preselected to use a combination of specific working modes: - - * participate in caching - * calculate in-place - * source reading - * (planned) use hardware acceleration - * (planned) remote dispatched calculation - -Most nodes will just have a single predecessor, but we can't limit nodes to a -single input, because there are some calculation algorithms which natively need -to work on several data streams simultaneously. This means, a single node can -be involved into the calculations for multiple streams (several pull calls on -the same frame number but for different channel, and in each case maybe a -different output node). I decided to rely solely on the cache for avoiding -duplicate calculations caused by this complication, because I deem it to be an -corner case. - - -the invocation interface -^^^^^^^^^^^^^^^^^^^^^^^^ -this is intended to be a rather simple "call-style" interface, without much -possibilites to influence the way things are happening. You pull a node and -will find the results in a provided buffer or the cache, but you can't even -change the frame data type type of the result. Besides the node invocation, -functions for collecting statistics will be accessible here too (Probably these -functions will be ''implemented'' in a classic-OO fashion by virtual functions, -but that's another story) - - -the processing interface -^^^^^^^^^^^^^^^^^^^^^^^^ -the individual nodes are configured to call a plain-C {{{process()}}} function -and provide an array of buffer pointers to be used within this function. For -the purpose of invoking actual data processing, it is irrelevant if this -function is implemented somewhere in the backend or provided by a plugin. At -this point, no type- and other meta-information is passed, rather the -processing function is supposed to do The Right Thing ^TM^ - - - -Tasks -^^^^^ - * What services do we expect from Render Nodes. What do we plan to do with a - render node? - * What different kinds (if any) of Render Nodes can be foreseen? - * order the required functionality by Proc / Backend. Find out specific - implementation constraints. - * work out a design based on this informations - - - - - - - - - -Rationale -~~~~~~~~~ -The purpose of this Design Entry is to give a summary; the questions and the -details of carrying out the operations are much more involved. - + -Please see the -http://www.lumiera.org/wiki/renderengine.html#Rendering[Proc-Layer impl -documentation (TiddlyWiki)] and the -http://www.lumiera.org/gitweb?p=lumiera/ichthyo;a=blob;f=src/proc/engine/procnod -.hpp;h=9cf3a2ea8c33091d0ee992ec0fc8f37bb5874d34;hb=refs/heads/proc[Source Code] -for details -(and/or contact Ichthyo for in-depth discussion of those technical details) - - - - - - -Comments --------- - - -Parked -~~~~~~ -We park this until we have time to revisit the details. It is accepted that we -need to design this interfaces. - - Do 14 Apr 2011 03:06:42 CEST Christian Thaeter - -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_parked/DesignRenderNodesInterface.txt b/doc/devel/rfc_parked/DesignRenderNodesInterface.txt new file mode 120000 index 000000000..bb91c30fe --- /dev/null +++ b/doc/devel/rfc_parked/DesignRenderNodesInterface.txt @@ -0,0 +1 @@ +../rfc/DesignRenderNodesInterface.txt \ No newline at end of file diff --git a/doc/devel/rfc_parked/FeatureBundle_PluggableModules.txt b/doc/devel/rfc_parked/FeatureBundle_PluggableModules.txt deleted file mode 100644 index 06b9fe814..000000000 --- a/doc/devel/rfc_parked/FeatureBundle_PluggableModules.txt +++ /dev/null @@ -1,195 +0,0 @@ -[grid="all"] -`------------`----------------------- -*State* _Parked_ -*Date* _2008-09-03_ -*Proposed by* link:Ichthyostega[] -------------------------------------- - - - -Describe pluggable modules by a "Feature Bundle" ------------------------------------------------- -This proposal builds upon Cehteh's Plugin Loader, which is the fundamental -mechanism for integrating variable parts into the application. - -It targets the special situation when several layers have to cooperate in order -to provide some pluggable functionality. The most prominent example are the -"effects plugins" visible for the user. Because, in order to provide such an -effect - - * the engine needs a processing function - * the builder needs description data - * the gui may need a custom control plugin - * and all together need a deployment descriptor detailing how they are - related. - - - - -Description -~~~~~~~~~~~ -The Application has a fixed number of *Extension Points*. Lumiera deliberately -by design does _not build upon a component architecture_ -- which means that -plugins can not themselves create new extension points and mechanisms. New -extension points are created by the developers solely, by changing the code -base. Each extension point can be addressed by a fixed textual ID, e.g. -"Effect", "Transition", .... - -Now, to provide a pluggable extension for such an Extension Point, we use a -*Feature Bundle* Such a Feature Bundle is comprised of - - * a Deployment Descriptor (provided as "structured data" -- TODO: define the - actual data format) - * the corresponding resources mentioned by this Deployment Descriptor - -The Deployment Descriptor contains - - * Metadata describing the Feature Bundle - - ID of the Extension point - - ID of the Bundle (textual ID) - - ID of origin / provider (could be a domain name) - - Category (textual, tree-like) - - Version number (major, minor) - - required Extension point version number (or Lumiera version no.?) - - Author name (utf8) - - Support email (utf8) - - textual description in a single line (utf8) - * A List of Resources, each with: - - ResourceID - - SubID - - Type of Resource, which may be - . Plugin - . Properties - . Script - . ...? - - - one of: - . the Resource provided inline in suitable quoted form (for textual - resources only) - . an URL or path or similar locator for accessing the Resource (TODO: - define) - - Additional Metadata depending on Type of Resource (e.g. the language of a - script) - - - -We do _not_ provide a meta-language for defining requirements of an Extension -Point, rather, each extension point has hard wired requirements for a Feature -Bundle targeted at this extension point. There is an API which allows code -within lumiera to access the data found in the Feature Bundle's Deployment -Descriptor. Using this API, the code operating and utilizing the Extension -Point has to check if a given feature bundle is usable. - -It is assumed that these Feature Bundles are created / maintained by a third -party, which we call a *Packager*. This packager may use other resources from -different sources and assemble them as a Feature Bundle loadable by Lumiera. Of -course, Lumiera will come with some basic Feature Bundles (e.g. for colour -correction, sound panning,....) which are maintained by the core dev team. -(please don't confuse the "packager" mentioned here with the packager creating -RPMs or DEBs or tarballs for installation in a specific distro). Additionally, -we may allow for the auto-generation of Feature Bundles for some simple cases, -if feasible (e.g. for LADSPA plugins). - - -The individual resources -^^^^^^^^^^^^^^^^^^^^^^^^ -In most cases, the resources referred by a Feature Bundle will be Lumiera -Plugins. Which means, there is an Interface (with version number), which can be -used by the code within lumiera for accessing the functionality. Besides, we -allow for a number of further plugin architectures which can be loaded by -specialized loader code found in the core application. E.g. Lumiera will -probably provide a LADSPA host and a GStreamer host. If such an adapter is -applicable depends on the specific Extension point. - -The ResourceID is the identifyer by which an Extension point tries to find -required resources. For example, the Extension Point "Effect" will try to find -an ResourceID called "ProcFunction". There may be several Entries for the same -ResourceID, but with distinct SubID. This can be used to provide several -implementations for different platforms. It is up to the individual Extension -Pont to impose additional semantic requirements to this SubID datafield. (Which -means: define it as we go). Similarly, it is up to the code driving the -individual Extension point to define when a Feature Bundle is fully usable, -partially usable or to be rejected. For example, an -"Effect" Feature Bundle may be partially usable, even if we can't load any - "ProcFunction" for -the current platform, but it will be unusable (rejected) if the proc layer -can't access the properties describing the media stream type this effect is -supposed to handle. - -Besides binary plugins, other types of resources include: - * a set of properties (key/value pairs) - * a script, which is executed by the core code using the Extension Point and - which in turn may access certain interfaces provided by the core for "doing - things" - -Probably there will be some discovery mechanism for finding (new) Feature -Bundles similar to what we are planning for the bare plugins. It would be a -good idea to store the metadata of Feature Bundles in the same manner as we -plan to store the metadata of bare plugins in a plugin registry. - - - - -Tasks -^^^^^ - - -Pros -^^^^ - - - -Cons -^^^^ - - - -Alternatives -^^^^^^^^^^^^ -Use or adapt one of the existing component systems or invent a new one. - - - -Rationale -~~~~~~~~~ -The purpose of this framework is to decouple the core application code from the -details of accessing external functionality, while providing a clean -implementation with a basic set of sanity checks. Moreover, it allows us to -create an unique internal description for each loaded module, and this -description data e.g. is what is stored as an "Asset" into the user session. - -Today it is well understood what is necessary to make a real component -architecture work. This design proposal deliberately avoids to create a -component architecture and confines itself to the bare minimum needed to avoid -the common maintenance problems. As a guideline, for each flexibility available -to the user or packager, we should provide clearly specified bounds which can -be checked and enforced automatically. Because our main goal isn't to create a -new platform, framework or programming language, it is sufficient to allow the -user to _customize_ things, while structural and systematic changes can be done -by the lumiera developers only. - - - - - - -Comments --------- - -From a fast reading, I like this, some things might get refined. For example -I'd strongly suggest to make the Deployment Descriptor itself an Interface -which is offered by a plugin, all data will then be queried by functions on -this interface, not by some 'dataformat'. Also Resource ID's and a lot other -metadata can be boiled down to interfaces: names, versions, uuid of these -instead reiventing another system for storing metadata. My Idea is to make the -link:Plugin/Interface[] system self-describing this will also be used to -bootstrap a session on itself (by the serializer which is tightly integrated) - -- link:ct[] [[DateTime(2008-09-04T09:28:37Z)]] 2008-09-04 09:28:37 - -Parked -~~~~~~ -Needs to ne reviewed some time later. - - Do 14 Apr 2011 03:06:42 CEST Christian Thaeter - -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_parked/FeatureBundle_PluggableModules.txt b/doc/devel/rfc_parked/FeatureBundle_PluggableModules.txt new file mode 120000 index 000000000..fb7a2d2c2 --- /dev/null +++ b/doc/devel/rfc_parked/FeatureBundle_PluggableModules.txt @@ -0,0 +1 @@ +../rfc/FeatureBundle_PluggableModules.txt \ No newline at end of file diff --git a/doc/devel/rfc_parked/GitSubmoduleTransistion.txt b/doc/devel/rfc_parked/GitSubmoduleTransistion.txt deleted file mode 100644 index fdbcd69db..000000000 --- a/doc/devel/rfc_parked/GitSubmoduleTransistion.txt +++ /dev/null @@ -1,87 +0,0 @@ -[grid="all"] -`------------`----------------------- -*State* _Parked_ -*Date* _2008-04-09_ -*Proposed by* link:ct[] -------------------------------------- - - -Use Git Submodules to organize the project ------------------------------------------- -We planned this long time ago when the project started, this proposal is for to -work out the details and define a turnover point in time. - - -Description -~~~~~~~~~~~ -There is a git-filter-branch command which helps in doing the dirty work -isolating commits which touch certain dirs. This can moderately easily be used -to create a new repository with a rewritten history containing only sub parts -of the original history. - -The basic idea is that one developer who wants to works on a certain subsystem -clones the 'official' master and then updates and tracks only the development -state of a certain subsystem. - - -Tasks -^^^^^ - * what shall be in the master repository? - * boilerplate files, license, build infrastructure - * the _admin_ dir with supplemental scripts - * define which submodules shall be defined? - * _doc/devel_ - * _doc/user_ - * _wiki_ - * _uml_ - * _src/backend_ - * _src/proc_ - * _src/gui_ - * _src/lib_ - -Not yet decided: - * _tests_ move them into the _src/$subsystem_ as symlink? - * _src/tool_ - - -Pros -^^^^ - * better isolation of single subprojects - * one who is interested on one subproject can track a master and only - following certain subproject updates - * smaller/faster updates/downloads - - -Cons -^^^^ - * needs some more git-fu to be used by the developers - * we will host considerably more git repositories (bigger list in gitweb), - this is not a problem but might look more confusing - - - -Alternatives -^^^^^^^^^^^^ -Go as we do currently with one big repository per developer. The decision to -use submodules is not urgend and it can be transfered at any time. The -turnaround should just be planned and be scheduled to one day to minimize the -confusion and merging issues. - - -Rationale -~~~~~~~~~ -When all people get used to it it allows a cleaner more sane work flow and well -isolated, less conflicting commits. - - - - - -Comments --------- - -We concluded that that submodules are not yet needed with exception for the -./doc folder. Parked for now. - -- ct 2008-07-26 09:09:57 - -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_parked/GitSubmoduleTransistion.txt b/doc/devel/rfc_parked/GitSubmoduleTransistion.txt new file mode 120000 index 000000000..1205cf6ba --- /dev/null +++ b/doc/devel/rfc_parked/GitSubmoduleTransistion.txt @@ -0,0 +1 @@ +../rfc/GitSubmoduleTransistion.txt \ No newline at end of file diff --git a/doc/devel/rfc_parked/NormalizedDeviceCoordinates.txt b/doc/devel/rfc_parked/NormalizedDeviceCoordinates.txt deleted file mode 100644 index b594cc3b2..000000000 --- a/doc/devel/rfc_parked/NormalizedDeviceCoordinates.txt +++ /dev/null @@ -1,112 +0,0 @@ -[grid="all"] -`------------`----------------------- -*State* _Parked_ -*Date* _2009-01-14_ -*Proposed by* link:ct[] -------------------------------------- - - -Normalized Device Coordinates ------------------------------ - -AkhIL pointed me out to some blender problem and how renderman fixes that. We -should use this too. - - - - -Description -~~~~~~~~~~~ - -Just snippet from IRC log: - ------------------------------------------------------------- -[15:09] and I hope lumiera will use some resolution independend - measuring for all parameters -[15:09] one can rotate where the node actually sits -[15:09] like NDC -[15:09] or pass transistions through the renderpipe, make all effects - transisition aware and apply them at the end -[15:10] the later is better but needs more efforts and some rethinking -[15:10] we will prolly support both in lumiera :) -[15:11] in renderman's NDC for horizontal image with 4:3 aspect ration - (-1.33,-1) is lower-left corner and (1.33,1) upper-right -[15:11] ah -[15:11] so moving to different resolutions and different aspect ratios - in renderman makes no problems -[15:11] well good point, we will measure in pixel but need to convert - between them . using a float would be good to address pixels -[15:12] yes -[15:12] what stands NDC for? -[15:13] Normalized Device Coordinates -[15:14] ok -[15:14] so from -1 to 1 is a range by smallest image size -[15:15] yes sounds reasonable -[15:15] * cehteh adds a note to the lumiera design docs -[15:15] so far we dont do anything where it matters .. but that will - come -[15:16] when you move some logo to (0.8,-0.8) it will stay on screen - even when you chenge resolution and image aspect ratio -[15:17] all input images should be scaled to this range (-1,1) by - smalles side - ------------------------------------------------------------- - - - -Tasks -^^^^^ - - - - -Discussion -~~~~~~~~~~ - -Pros -^^^^ - - - -Cons -^^^^ - - - -Alternatives -^^^^^^^^^^^^ - - - -Rationale -~~~~~~~~~ -TBD - - - - - -Comments --------- - -One issue where I always assumed we'd need to define something of this sort is -for proxy editing. Especially this is a problem in conjunction with masks. -Basically, this means a bit more of "vector graphics". With film/video editing, -this was rather unusual, but with the advent of more and new digital video/film -formats it gets more and more important. Also, our considerations regarding -time handling and quantisation to single frames somewhat fit into this line of -thought. Up to now, rather the standard way of thinkin was to use a "project -framerate" and a fixed resolution in pixels. But we certainly can do better. - - -- Ichthyostega 18:09:50 - - -Parked -~~~~~~ -deferred for later, generally accepted. - - Do 14 Apr 2011 03:06:42 CEST Christian Thaeter - - - -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_parked/NormalizedDeviceCoordinates.txt b/doc/devel/rfc_parked/NormalizedDeviceCoordinates.txt new file mode 120000 index 000000000..97174abca --- /dev/null +++ b/doc/devel/rfc_parked/NormalizedDeviceCoordinates.txt @@ -0,0 +1 @@ +../rfc/NormalizedDeviceCoordinates.txt \ No newline at end of file diff --git a/doc/devel/rfc_parked/RenderOptimizer.txt b/doc/devel/rfc_parked/RenderOptimizer.txt deleted file mode 100644 index 74604e77e..000000000 --- a/doc/devel/rfc_parked/RenderOptimizer.txt +++ /dev/null @@ -1,100 +0,0 @@ -[grid="all"] -`------------`----------------------- -*State* _Parked_ -*Date* _2007-06-07_ -*Proposed by* link:ct[] -------------------------------------- - -Render Optimizer ----------------- -Render only parts of a frame which are necessary for the Output; Optimize -render pipeline for efficiency - - -Description -~~~~~~~~~~~ -This Idea is just stored here for later reference/implementation. - -Effects give some information on which data their output depends (like -transitions, temporal dependencies, color/alpha etc) and what the operation -costs. Based on this information we optimize the render pipeline, for example -if the output is a zoom, then we only need to calculate the parts of a frame -which will be viewable in the output (plus some more dependencies, like blur -has radius and so on). Further in some cases it might be favorable to reorder -some effects for the actual render process, as long it would produce the same -output as the original sequence of effects. - - - - - -Tasks -^^^^^ - - - - -Discussion -~~~~~~~~~~ - -Pros -^^^^ - - - -Cons -^^^^ - - - -Alternatives -^^^^^^^^^^^^ - - - -Rationale -~~~~~~~~~ - - - - - - -Comments --------- - -Possible classification for video filters: - 1. The filter only changes the color of each pixel in the same way - 2. The filter deforms the image but leaves the color - 3. The filter makes complex things. The only additional hint it can export is - the - number of referenced past frames, if such a limit exists (sometimes it - doesn't). - -Filters of type 1 and type 2 never use any previous frames, and are strictly -one frame in - one frame out. Filters of type 1 can always be swapped with -filters of type 2, the output is the same. All other filters cannot be swapped -in general. - -The good news is, that: - - 1. All commonly used filters are either type 1 or type 2 - (type 3 are more the fun effects) - 2. Filters of type 2 are colormodel agnostic - 3. If a filter of type 1 makes only linear transformations of the color - vectors (new_color = matrix * old_color), - the matrix can be transformed from e.g. RGB to YUV, so these filters can - always work in both colorspaces directly - - -Parked -~~~~~~ -Generally this is accepted but needs some more polishing when we go over it. - - Do 14 Apr 2011 03:06:42 CEST Christian Thaeter - - - - - -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_parked/RenderOptimizer.txt b/doc/devel/rfc_parked/RenderOptimizer.txt new file mode 120000 index 000000000..6c80c340f --- /dev/null +++ b/doc/devel/rfc_parked/RenderOptimizer.txt @@ -0,0 +1 @@ +../rfc/RenderOptimizer.txt \ No newline at end of file diff --git a/doc/devel/rfc_parked/SkillsCollection.txt b/doc/devel/rfc_parked/SkillsCollection.txt deleted file mode 100644 index c2c4f3d68..000000000 --- a/doc/devel/rfc_parked/SkillsCollection.txt +++ /dev/null @@ -1,80 +0,0 @@ -[grid="all"] -`------------`----------------------- -*State* _Parked_ -*Date* _2007-06-13_ -*Proposed by* link:ct[] ---------------------------------- - - -Skills Collection ------------------ -Make a page where people can tell in which areas they are willing to support -others. - - -Description -~~~~~~~~~~~ -Some Page should list different things needed for working on the project and -users should attach themself when they offer support for it. This is meant that -people who run into problems know who to ask. In contrast this is not meant -like these Skill pages on Sourceforge or such. I don't like this rating and -posing system. We let people assing themself to skill and not skills to people -and there is no rating. - -Skills shall be anything which is needed like the tools we use, the code we -create etc. - - - -Example -^^^^^^^ - -.Git -* ct -* j6t - -.autotools -* ct - -.lumiera/renderpipe -* ichthyo - -... shall this contain emails? - - -Tasks -^^^^^ - * just set this page up .. either on this wiki or in a tiddlywiki which - becomes checked into the repo - - -Pros -^^^^ - * inter developer support and help network - - -Cons -^^^^ - * privacy concerns, people might not publish what they know or better what - they ''not'' know - - -Alternatives -^^^^^^^^^^^^ -...urgs - - -Rationale -~~~~~~~~~ -This only announces where people offer support within the lumiera developer -community and is absolutely voluntary. - - - - - -Comments --------- - - -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_parked/SkillsCollection.txt b/doc/devel/rfc_parked/SkillsCollection.txt new file mode 120000 index 000000000..b0d00d674 --- /dev/null +++ b/doc/devel/rfc_parked/SkillsCollection.txt @@ -0,0 +1 @@ +../rfc/SkillsCollection.txt \ No newline at end of file diff --git a/doc/devel/rfc_parked/TodoLists.txt b/doc/devel/rfc_parked/TodoLists.txt deleted file mode 100644 index 1d3b987ac..000000000 --- a/doc/devel/rfc_parked/TodoLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -[grid="all"] -`------------`----------------------- -*State* _Parked_ -*Date* _2008-03-05_ -*Proposed by* link:ct[] -------------------------------------- - -Todo Lists ----------- -We need some way to organize tasks to be done (tiddlywiki, testsuite, ...?) - - -Description -~~~~~~~~~~~ - - -Tasks -^^^^^ - - -Pros -^^^^ - - - -Cons -^^^^ - - - -Alternatives -^^^^^^^^^^^^ - - - -Rationale -^^^^^^^^^ - - - - - - -Comments --------- -We decided to use a Tiddlywiki for now until this is further worked out - -- link:ct[] [[DateTime(2008-03-08T03:38:50Z)]] - -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_parked/TodoLists.txt b/doc/devel/rfc_parked/TodoLists.txt new file mode 120000 index 000000000..89793b79c --- /dev/null +++ b/doc/devel/rfc_parked/TodoLists.txt @@ -0,0 +1 @@ +../rfc/TodoLists.txt \ No newline at end of file diff --git a/doc/devel/rfc_parked/UseCases.txt b/doc/devel/rfc_parked/UseCases.txt deleted file mode 100644 index 7f1b96d5c..000000000 --- a/doc/devel/rfc_parked/UseCases.txt +++ /dev/null @@ -1,310 +0,0 @@ -[grid="all"] -`------------`----------------------- -*State* _Parked_ -*Date* _2008-10-31_ -*Proposed by* link:Ichthyostega[] -------------------------------------- - - -Use Case analysis ------------------ - -The only way to defeat "featuritis" is to build upon a coherent design -- - + -which in turn relies upon a more or less explicit understanding what the -application should be like, and the way the prospective user is thought to work -with the program. Today, a generally accepted 'method' for building up such -an understanding is to do a *use case analysis*. Such a formal analysis would -require to identify all usage scenarios with the involved actors and parts of -the system, and then to refine them in detail and break them down into distinct -use cases. Here, I'll try a rather informal variant of such an analysis. I'll -restrain myself to describing the most important usage situations. - -'please participate in the discussion. It well may be that everything detailed - here is self-evident, but I doubt so. At least the grouping and the omissions - kind-of reflect a certain focus of the project' - - -Describing basic Lumiera usage situations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The fundamental assumption is that the user works on a project, which is -reflected in the fact that the user is working on a single session over an -extended period of time (several hours to several years). External media will -be imported and incorporated into this session, additional media will be -created within this session, and finally there is at least one render or export -procedure to harvest the results of this work. - - -Scenario (1) : Exploring Media -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Various external media files are opened. You play, cue and examine the media. -Tagging, labeling and adding notes. Marking of interesting points and ranges. -Possibly breaking down into clips, or at least extract some ranges as clips. -Draft arranging the clips, applying some effects to check the result and thus -to find out about the viability of the footage. Playback of several media at -the same time (several videos, but also video and music). Grouping of assets -(media, clips, effects, markers) into folders. - - -Scenario (2) : Simple assembly -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You build up a simple linear cut sequence. Either by - - - using a single source media, trimming it and cutting away (a small number - of) unwanted parts - - playing source media and spilling over (insert, overwrite) some parts into - the final assembly - - dragging over the pre-organised clips from clip folders to build up the - assembly. - -Sound is either used immediately as-is (the soundtrack attached to the media), -or there is a similarly simple, linear music bed. Some people prefer to switch -sound off entirely for this kind of work. In any case, the link is either -automatic, or rather vague and soft (as music being vaguely correlated) - - -Scenario (3) : Augmenting an assembly -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Without the intention to rework it from scratch, an already existing simple -assembly is augmented, beautified and polished, maybe to conform with -professional standards. This includes the ``rescue'' of a somewhat questionable -assembly by repairing localized technical problems, but also shortening and -re-arranging, and in extreme cases even changing the narrative structure. A -distinctive property of this usage scenario is that work happens rather in the -context of 'tasks' (passes) -- not so much isolated operations: - - - the task may be to get the rhythm or overall tempo right, and thus you go - over the sequence and do trim, roll, shuffle or slide edits. - - you may want to ``fold-out'' parts of the sound, thus interweaving o-sound - and music - - there may be a sound overdubbing and replacing pass - - you may want to walk certain automation curves and adjust levels (sound - volume or tone, fade, brightness/contrast/colour) - - general polishing may include adding title overlays, fading in and out, - adding (typically a single type of) transition(s) in a coherent manner - - -Scenario (4) : Compositional work -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Here I define *compositional work* as a situation where you deal with -multiple more or less independent sequences going on in parallel, similar to a -musical score. Frequently, we encounter compositional parts embedded in a -otherwise linear work, and often those parts evolve when Scenario (3) is driven -to the extreme. - - - the most common situation is that o-sound, sound design and music work - together with the temporal structure created in the image edits. - - a movie with a complex narrative structure may induce compositional work on - a very large scale (and existing applications frequently fall short on - supporting such) - - _compositing_ often leads to compositional work. Special FX, masked objects - being arranged, artificial elements to be integrated. - - similarly any collage-like or heavily layered arrangements lead themselves - to requiring compositional work. - -The common distinctive property of all those situations is: objects are -embedded into a primary context and have to obey the rules of this context, and -at the same time have a close correlation to other objects which are embedded -in a completely different (``orthogonal'') context. (To give a catchy example: -assume, a CG monster has to be integrated. Besides the masked monster object, -you have several colouring and blurring layers at completely different levels -in the layering order, and at the same time you have correlated sound objects, -which need to be integrated into the general sound-scape. And now your primary -job is to get the movement and timings of the monster right in relation to the -primary timing grid established by the existing edit) - -The working style and thus the tool support necessary for compositional work is -completely different to Scenario (3). After an initial build-up (which often is -very systematic), the working profile can be characterized by tweaks to various -parameters to be done in-sync at widely separated sites within the session, -together with repeated cycles of ``do it'', ``assess the result'', ``undo all and -do some small detail differently''. Typically there is the need for much navigation -(contrast this to Scenario (3) where you work in _tasks_ or _passes_) - - -Scenario (5) : Working with Sound -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The degree of integrating sound work is worth discussing. Often, due to -limitations in existing tools, sound work is done in separate applications to a -large extent. Which in turn forces the whole production into a sequential -organisation scheme. First the edit has to be roughly final, and then the sound -people can step in. (Of course this is an simplification). To list the common -operations: - - - cleaning and preparing original sound - - fitting sound library elements or separately produced sound - - overdubbing - - playing or building music to match the rhythm of the edit or the original - footage - - montage of dialogue and/or noise correlated to the primary content of the - sequence - - sound design, shaping the pace and the feel of a sequence - - final balance mix - -While clearly some of those tasks are always better done within a dedicated -application, the ability to carry out this work partially within the main -session and even while the basic edit is still in flux -- may open new artistic -possibilities. - - -Scenario (6) : Large Projects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -At first sight, the operations and the work to be done in large projects is the -same as in small ones. But large projects tend to create sort of an additional -``layer'' on top of the usage scenarios described thus far, which will ``kick in'' -at various places. - - - work may be divided upon several editors, working on separate parts - (sequences) which then need to be re-integrated - - there may be a global asset organisation (naming scheme), which will be - extended locally, resulting in nested naming scopes. - - some quite basic stuff needs to be done in a coherent fashion, e.g. titles, - a certain transition (template), the way fade-outs are done, a certain - colour profile. Possibly, this stuff needs to be adjusted all over the - project. - - there will be a general (large scale) timing grid with distinct ``check points'' - and probably there is the need to navigate to the different parts of the - whole project. - - there may be the necessity to build several versions of the same project in - parallel (e.g. a short version and a extended director's cut) - - you may have to care for such nasty and tedious things as keeping sub-titles - in-sync while the edit is still in flux - - you may want to do integration builds, where you add placeholders just for - the purpose to get an impression of the work as a whole. - - -Scenario (7) : Teamwork -^^^^^^^^^^^^^^^^^^^^^^^ - -Several people work on a project. - - - A longer sequence might be split up into parts, each one edited by another - person. The parts will be collected and assembled by the chief editor. Edits - to the parts will still be possible, but a system of permissions allows to - lock down access to parts of the edit, so to prevent unexpected interferences. - - Arrangements based on the same resources can be branched, tagged and merged. - - Edits are logged with usernames - - Markers can be shown/hidden on a per creator base. - - Team members need ways to share and store notes and suggestion for each - other work. Annotations can be added to clips, markers or arrangements - - A pen tool could allow to scribble on top of frames or arrangements. An - expressive and fast way to leave suggestions about deletions, movements and - all other kinds of edits. - - -Scenario (8) : Script driven -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The application is started ``headless'' (without GUI) and controlled via an -API. Either an existing session is loaded, or a new session is created and -populated. Then, some operations have to be done in a systematic manner, -requiring a way to address parts of the session both unambiguously and in a way -easy to access and control from a programming environment (you can't just -``see'' the right clip, it needs to be tagged). Finally, there might be an -export or render step. A variation of this scenario is the automatic extraction -of some informations from an existing project. - - -Discussion -~~~~~~~~~~ - -.Pros -* describing such scenarios, even if hypothetical, create an anchor or point of - referral for feature/GUI design work to be done in detail -* relating features to working situations helps to see what is really important - and what is rather of technical merit -* compiling and discussing this list helps shaping the character of the - application as a whole -* the above compilation relates individual features to a general production - process. -* the goal of this compilation is to be _fairly complete_ - - -.Cons -* any of those descriptions is artificial -* sometimes it is better to develop an application technology driven, - especially when it is technologically challenging to get it to work properly. -* having such a large-scale vision may freak away people which otherwise - might jump in and implement some crazy but valuable new feature -* the listed usage scenarios intend to be _fairly complete_, which can be a - limitation or even self-deception. Better have an open ended list. -* the above compilation seems quite conventional and explicitly leaves out some - scenarios - - networked, distributed scenarios, compound applications - - television, life video, VeeJay-ing - - cartoons, animations, game design - - - -.Alternatives -* avoiding a general plan, just sharing a vague general vision -* just start out with one scenario directly at hand (e.g. the simple assembly) - and not worrying about the rest -* rather then defining those scenarios (which are necessarily hypothetical), - rather stick to the operation level. E.g. a use case would be rather - on the level of ``triming a clip'' -* doing a complete state-of-the art UML use case analysis. -* after having created the foundation, rather stick to an XP approach, i.e. - implement, integrate and release small ``usage stories'' - - - -Rationale -^^^^^^^^^ - -Well, after having considered, compiled and written such an concept, altogether -avoiding a big picture view of the application is not longer an option. To the -other extreme, we neither have the resources, nor the circumstances for doing a -rigid and formal analysis. Finally, the XP approach really sounds promising, -and it should be clear that it is in no way ruled out. Nothing hinders us to -have a detailed vision, but then to implement small usage stories which fit -into this vision. - -Besides, another consideration. The above compilation builds upon the notion, -that there is a common denominator of film making craft, a core editing art, -which has been shaped in the first 100 years of cinema, and which won't go away -within the next generation, even if the technological and practical -circumstances of production change quite dramatically. - - - - - - - - -Comments --------- -//comments: append below - -.Template e.g. for regular TV series -Constraints to fit all contents within fixed timeline, cover topic, select -collage of iconic scenes from archived and collected footage. Update intro and -credit roll for each episode. Add in stopmotion, and 3D model animations with -vocal commentaries. Gather together separate items from "outworkers". - -Tree:: '2008-12-27 08:36:36' - - -//endof_comments: - - - -Parked -~~~~~~ -We have to revisit this, possibly someone (or a group) who wants to work on -the workflow. For now its parked until revisited. - - Do 14 Apr 2011 03:06:42 CEST Christian Thaeter - - - - -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_parked/UseCases.txt b/doc/devel/rfc_parked/UseCases.txt new file mode 120000 index 000000000..f0e56ff79 --- /dev/null +++ b/doc/devel/rfc_parked/UseCases.txt @@ -0,0 +1 @@ +../rfc/UseCases.txt \ No newline at end of file diff --git a/doc/devel/rfc_pending/ApplicationInstall.txt b/doc/devel/rfc_pending/ApplicationInstall.txt deleted file mode 100644 index 44ed4cdea..000000000 --- a/doc/devel/rfc_pending/ApplicationInstall.txt +++ /dev/null @@ -1,225 +0,0 @@ -ApplicationInstall -================== - -// please don't remove the //word: comments - -[grid="all"] -`------------`----------------------- -*State* _Draft_ -*Date* _Di 11 Jan 2011 17:00:55 CET_ -*Proposed by* Ichthyostega -------------------------------------- - -[abstract] -********************************************************************************* -Lumiera should be a _freely relocatable_ application bundle. -Relying only on the relative folder structure within this bundle, the application -will be fully functional at any location, provided that the external library -dependencies are resolvable using the standard mechanisms of the platform. -The setup mechanism must be obvious, self-explanatory and must not rely -on compiled in magic or buildsystem trickery. Yet packaging into a FSH conforming -installation location should be supported by the same mechanisms. -********************************************************************************* - -Description ------------ -//description: add a detailed description: -Lumiera is planned to become a large professional application bundle, relying -on several external resources for proper operation. An installed Lumiera -application will be more like Gimp, Blender, OpenOffice or Eclipse, -not like bash, autotools or emcas. - -Besides that, it can be expected that Lumiera frequently will be used in a -project or studio like setup, where the application isn't installed, but just -unZIPped / unTARed and used as-is. Thus, it should be sufficient to unpack -the application bundle and point it to the session file and maybe the -media storage. - -The Eclipse platform can serve as a model for the setup of an modern -application of that style: It can be just unpacked, and when looking -into the folder structure, the meaning of the parts is obvious, and the -basic bootstrap is controlled by two short text based INI files. -While Lumiera presumably won't get _that_ heavyweight and is clearly -not intended to become a general business application platform like OSGi -- -the underlying principles can serve as a point of reference for modern -development standards. - -This leads to the following conclusions: - -- we need to define a standard folder layout for the bundle -- the application must not rely on any compiled-in absolute paths -- the executable should fetch the directly accompanying shared modules -- all other lib dependencies should be handled by the system mechanisms -- the bootstrap configuration likewise must not be compiled-in -- this configuration must be human readable and clearly exhibit its purpose -- the same system must be able to adapt to a FSH conforming installation layout - -Judging from our current planning and the existing codebase, Lumiera -is on a good way in that direction, yet some cleanup needs to be done, -especially removing convenience shortcuts from the early days of development -and catching up with the repair of some traits of sloppyness here and there. - -Library resolution -~~~~~~~~~~~~~~~~~~ -In former days, it was common habit to compile-in a hard wired absolute -+RPATH+. This can be considered obsolete practice; for example, the Debian -policy forbids doing so. This is the result from numerous maintainability -problems in the past. On the other hand, the GNU linker and other modern -linkers support a relative resolution of shared modules directly accompanying -an specific executable. The Debian policy allows this, if and only if these -shared modules are installed with the same binary package and only used by -this specific executable(s). Together, this is exactly what we need to -solve our requirement. - -Thus, the build process enables the new-style DT-tags in the Elf binary -and sets the +DT_RUNPATH+ with an value relative to +$ORIGIN+, which resolves -to the path of the currently executing binary. Moreover, it is _sufficient_ -to set this on the initial executable _only,_ because this creates a common -searchpath for all lib resolution events in the scope of that loaded executable. -Besides that, we need to care that our private libraries have a unique +SONAME+, -in this case all starting with the prefix +liblumiera*+. Note moreover that this -new-style +DT_RUNPATH+ indeed _can_ be overridden by an +LD_LIBRARY_PATH+ in the -environment, should there be the need for very special experiments. - -Bootstrap location -~~~~~~~~~~~~~~~~~~ -Thus, a single relative library folder becomes the only hard wired start -configuration. In our case, the folder +$ORIGIN/modules+ was chosen. The -root of the package then holds all the binaries depending on these common -internal libraries, that is the +lumiera+ executable and any accompanying -special tools. As usual with such large application bundles, these get -only _symlinked_ into the +/usr/bin+ folder on installation. - -For sake of clarity, after starting the executable, the _same location_ -is used to load the bootstrap configuration. This configuration in turn -defines all further locations like the extended configuration, project -templates, plugin search path, the GUI module to load, the search path -for icons and GUI resources, project templates and similar basics. - -Relative paths and the location of the executable -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -According to the general requirements outlined above, most paths should -be given in a relative fashion. Unfortunately there is no portable solution -for self-discovering the currently running executable. But at least there -is a solution for all current major platforms. Under Linux, this information -can be retrieved from the kernel through the +/proc+ filesystem. - -Again for sake of clarity, the same token +$ORIGIN+ was chosen to denote -this executable location (note: this is _not_ the current working directory). -Moreover, due to the folder layout detailed above, this coincides with the -root of the application bundle, thus making for a self-explanatory convention. -Besides +$ORIGIN+, these search paths later on likely will contain locations -below the user's home directory, e.g. +~/.lumiera/themes+ - - -Tasks -~~~~~ -// List what needs to be done to implement this Proposal: -* identify what impedes such a modern setup procedure ([green]#✔ done#) -* rectify the folder structure created in the build target - directory ([green]#✔ done#) -* build the executables in a way to allow relative resolution of the - internal shared modules ([green]#✔ done#) -* replace the compiled-in path definitions for plugin loading by a - configurable bootstrap ([green]#✔#) -* add an working library implementation for a config loader ([green]#✔ done#) -* add a mechanism for establishing the path of the current execubable. + - This is _non-portable_ ([green]#✔ done#) -* wire the prepared API in the GUI to use this working config loader - for resolving GUI resources ([green]#✔ done#) -* try to extract the path search code from the existing config loader, - or build a new solution based on standard libraries ([green]#✔ done#) -* introduce an output root directory into the buildsystem, allowing - for package builds ([green]#✔#) -* define a _Debian packaging_ as proof-of-concept ([green]#✔ done#) - - -Discussion -~~~~~~~~~~ - -Pros -^^^^ -* self-contained -* self-explanatory -* based on _best practices_ -* conforming with FSH and Debian policy - - -Cons -^^^^ -* requires work -* raises the bar at the implementation side -* requires an bootstrap sequence to be explicitly performed - on application startup -* breaks with some beloved habits of the Unix community - - -Alternatives -^^^^^^^^^^^^ -//alternatives: explain alternatives and tell why they are not viable: -I can think of two alternatives - -. dealing with all those problems _later_ -. not making an concept, rather sticking to UNIX habits - -The first alternative is indeed worth considering, because we're settling -some things to be really implemented way later, which bears some dangers. -But, on the other hand, it is a common practice known from extreme programming -to deliver early and regularly, which effectively means to set up the deploy -path of an application really early in the development cycle. The rationale -is that -- according to general experience -- the deployment always turns -up some very specific problems and constraints, which can be a serious -threat when discovered late in the development process. - -The second alternative isn't really applicable IMHO. The original UNIX philosophy -breeds on an academic setup and really excels with small nifty commandline utils -combined by pipes, each specialised to do a single thing very well. These utils -are more like the objects within our implementation. The concept of large -application software bundles and desktop software was always a bit alien -within the classic UNIX environment. - - - -Rationale ---------- -//rationale: Give a concise summary why it should be done *this* way: - -This RfC can be seen as an commitment to an professional approach and as -clarification: Traditionally, the Unix community hailed a lot of _black magic_ -practices like compiled-in installation paths, macro magic, +sed+ and +awk+ -trickery, inline code compiled on-the-fly, relying on very specific and -un-obvious behaviour of some build script, configuration via environment -variables and a lot of similar idioms. These practices might be adequate -in a quickly moving Research & Development setup, but turned out to be -not so helpful when it comes to industrial strength development, -as they are known to lead to maintenance problems. - - - - -//Conclusion -//---------- -//conclusion: When approbate (this proposal becomes a Final) -// write some conclusions about its process: - - - - -Comments --------- -//comments: append below - -.State -> Draft -There is now a complete implementation of this concept on my ``proc'' branch. + -Moreover, I was able to define an initial Debian packaging for Lumiera on top -of that implementation. - -During that work, I had opportunity to visit various areas of the existing codebase, -which reminded me of several small issues, which seem to become unhealthy when lying -around unfixed for such a long time. Probably I'll start a clean-up initiative and -try to bring these points to discussion separately. - - So 13 Feb 2011 20:04:00 CET Ichthyostega - - -//endof_comments: diff --git a/doc/devel/rfc_pending/ApplicationInstall.txt b/doc/devel/rfc_pending/ApplicationInstall.txt new file mode 120000 index 000000000..46e568659 --- /dev/null +++ b/doc/devel/rfc_pending/ApplicationInstall.txt @@ -0,0 +1 @@ +../rfc/ApplicationInstall.txt \ No newline at end of file diff --git a/doc/devel/rfc_pending/DesignParamAutomation.txt b/doc/devel/rfc_pending/DesignParamAutomation.txt deleted file mode 100644 index 9ed2d9b4a..000000000 --- a/doc/devel/rfc_pending/DesignParamAutomation.txt +++ /dev/null @@ -1,84 +0,0 @@ -[grid="all"] -`------------`----------------------- -*State* _Idea_ -*Date* _2008-03-06_ -*Proposed by* link:Ichthyostega[] -------------------------------------- - - -Design the handling of Parameters and Automation ------------------------------------------------- -Parameters of Plugin Components and/or Render Nodes play a role at various -levels of the application. - + -Thus it seems reasonable to do a formal requirements analysis and design prior -to coding. - - -Description -~~~~~~~~~~~ -Regarding components directly participating in the render (which may be -implemented by plugins), we distinguish between *configuration* (static) and -*parameters* (dynamic). The point of reference for this distinction is the -render process: a plugin configuration may well be variable in some manner, -e.g. the plugin may provide different flavours of the same algorithm. But this -choice has to be fixed prior to feeding the corresponding plugin asset to the -builder. Contrary to such fixed configuration setup, the _parameters_ are -considered to be _variable_ during the rendering process. They can be changed -on-the-fly from GUI, and they may be automated. Probably, each Render Node will -have at least one such _parameter_ -- namely a bypass switch. - - -Tasks -^^^^^ - - * we need to work out an introspection mechanism for parameters - - asses what different types of parameters we need - - find out how much structured parameters will be (do simple values - suffice?) - - define how parameters can be discovered/enumerated - - define a naming scheme for parameters, so they can be addressed - unambiguously - * value parameters have a value range. Work out how to handle this - * parameters may need a specific presentation in the GUI - - linear/logarithmic scale, scale reference - - selecting the right widget - -So... - -. find out to which extend we need these properties -. find out what parts of the App will have what requirements? -. chose a best fitting implementation based on this information - -A closely related issue is the handling of *Automation*. The current draft -calls for an abstract interface "ParamProvider", which just allows the -link:Plugin/RenderComponent[] to pull a current value, without knowing if the -ParamProvider is a GUI widget or an automation data set with interpolation. The -component using the param value should not need to do any interpolation. We -should re-asses and refine this draft as needed. Note: Render Nodes are -stateless; this creates some tricky situations. - - - - - - - -Alternatives -^^^^^^^^^^^^ -?? (any ideas?) - - -Rationale -~~~~~~~~~ - - - - - - -Comments --------- - - -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/DesignParamAutomation.txt b/doc/devel/rfc_pending/DesignParamAutomation.txt new file mode 120000 index 000000000..bec0a6722 --- /dev/null +++ b/doc/devel/rfc_pending/DesignParamAutomation.txt @@ -0,0 +1 @@ +../rfc/DesignParamAutomation.txt \ No newline at end of file diff --git a/doc/devel/rfc_pending/DeveloperDocumentationStructure.txt b/doc/devel/rfc_pending/DeveloperDocumentationStructure.txt deleted file mode 100644 index 1f88d130e..000000000 --- a/doc/devel/rfc_pending/DeveloperDocumentationStructure.txt +++ /dev/null @@ -1,136 +0,0 @@ -Developer Documentation Structure -================================= - -// please don't remove the //word: comments - -[grid="all"] -`------------`----------------------- -*State* _Idea_ -*Date* _Mon Aug 2 18:03:25 2010_ -*Proposed by* Christian Thaeter -------------------------------------- - -[abstract] -******************************************************************************** -I describe here how to bring the Lumiera Developer Documentation into an simple -hierarchical structure. Previously we accumulated a lot Documentation which -ended in quite a few different places. This should be tidied up. -******************************************************************************** - -Description ------------ -//description: add a detailed description: - -I propose to reorganize the developer documentation in the following way: - - * make a 3 (or more, see below) level documentation structure: - 1. The entry level becomes the 'Lumiera: The inner Core' document which shall - not go into details but give a hint what everything is made for. This - will be the first introductory doc for new developers. - 2. second level are the RFC's which descibe the design as planned on a - general level, not going (except for some example snippets) into - implementation details. - 3. the third level is the doxygen documentation which describes what - actually got implemented in detail. This can be further split into - an external reference and a internal part. - -We using test-driven-development, our tests are our specifications. This leads -to the idea that ideas, design and intentions for tests should be documented -there too. In a higher level abstract human written form. I propose to use my -pipadoc documentation extractor (that means, writing asciidoc within the code as -special comments) for this. - - -Tasks -~~~~~ -// List what would need to be done to implement this Proposal in a few words: -// * item ... - - * Go over the old content of the asciidoced tiddlywikis, integrate it either in - the "Lumiera: The inner Core" document or write single RFC's for them. - * The 'proc' tiddlywiki is a bit special, we need a plan how to integrate this. - Possibly making a own document-dir for this, or refactor it in plenty RFC's. - This is ichthyos decision. - * Decide how to proceed with the UML model - - - -Pros -^^^^ -// add just a fact list/enumeration which make this suitable: - -Much easier entry to the whole developer documentation. Reading the "Inner Core" -document should be sufficient to get a good idea about the Lumiera design and -layout. All details are linked from there and thus easily findable. - - -Cons -^^^^ -// fact list of the known/considered bad implications: - -There are some open ends yet, doxygen for example doesn't integrate nicely, we -possibly can't link to single doxygen entities since these have no permanent -link (to my understanding, to be investigated). Other parts like the UML model -are not yet decided and moving the other existing content over needs some (not -really much) work. - -Alternatives ------------- -//alternatives: explain alternatives and tell why they are not viable: - -Spring 2010 we discussed and decided an overall website and documentation structure. -We could just stick to that. - - -Rationale ---------- -//rationale: Describe why it should be done *this* way: - -This approach fits nicely into our overall infrastructure and the way we wanted -to do things. Using git and asciidoc mostly, making the developer documentation -part of the source tree and reasonable easy available/maintainable to -developers. - -//Conclusion -//---------- -//conclusion: When approbated (this proposal becomes a Final) -// write some conclusions about its process: - - - - -Comments --------- -//comments: append below - -* The general idea of having three levels, with 'The Inner Core' as entry point, - looks OK for me. -* beyond that -- we had a detailed discussion about the overall website structure, - which includes the documentation. Why should we overthrow these results now and - re-start the discussion? Lets just stick to this agreed on structure! -* especially I don't like the way this proposal tries to squeeze everything into - an completely uniform structure. It is simply not true that the RFCs are just the - second level, and doxygen would cover the 3^rd^ level. Look at the existing - documentation to see why. - - RFCs are a 'kind' of document, not a 'hierarchy level.' Indeed, our existing - RFCs span all three hierarchy levels, and this is OK so and should remain this - way. (And yes, I like the RFCs much and want to retain them) - - RFCs are well suited to topics requiring discussion and agreement by the whole - core developer team. I see no point in 'pseudo-RFC-ing' the individual design - decisions only relevant for an isolated part of the application and without - any potential for discussion. - - similarily, in the TiddlyWiki, besides just working notes (``extended brain'') - you'll find finished text pages belonging to all different levels, from very - high-level conceptual down to explanation of technical details, with - cross references and tags for categorisation (and this will be retained - when asciidocing the content). -* so my conclusion is rather having one overview text, and then the split into - *conceptual* and *technical* documentation, each of which has a separate sub - structure not necessarily congruent to the structure on the other half. RFCs, - UML model and doxygen are just separate and consistent bodies of documentation - and can be referred to from the main documentation. (I agree with the observation - regarding permanent links into doxygen. But I can't imagine there isn't some - existing solution to this problem) - -- link:Ichthyostega[] 2010-10-15 - -//endof_comments: diff --git a/doc/devel/rfc_pending/DeveloperDocumentationStructure.txt b/doc/devel/rfc_pending/DeveloperDocumentationStructure.txt new file mode 120000 index 000000000..e95a5fda2 --- /dev/null +++ b/doc/devel/rfc_pending/DeveloperDocumentationStructure.txt @@ -0,0 +1 @@ +../rfc/DeveloperDocumentationStructure.txt \ No newline at end of file diff --git a/doc/devel/rfc_pending/EngineInterfaceOverview.txt b/doc/devel/rfc_pending/EngineInterfaceOverview.txt deleted file mode 100644 index e9e931661..000000000 --- a/doc/devel/rfc_pending/EngineInterfaceOverview.txt +++ /dev/null @@ -1,271 +0,0 @@ -Engine Interface Overview -========================= -:Date: 2010 - -// please don't remove the //word: comments - -[grid="all"] -`------------`----------------------- -*State* _Draft_ -*Date* _2010-04-16_ -*Proposed by* link:Ichthyostega[] -------------------------------------- - - -Overview Engine Interface(s) ----------------------------- - -******************************************************************************** -At the Engine Interfaces, Lumiera's Backend and Session get connected and work -together to produce rendered output. This design proposal intends to give an -overview of the connection points and facilities involved, to define some terms -and concepts and to provide a foundation for discussion and working out the -APIs in detail. -******************************************************************************** - - - -Participants -~~~~~~~~~~~~ - *Render Process*:: represents an ongoing calculation as a whole - *Engine Model*:: encloses the details of the current engine configuration and - wiring - *Dispatcher*:: translates a render process into the (planned) invocation of - individual nodes - *Scheduler*:: cares for calculations actually to happen, in the right order - and just in time, if at all - *Node*:: abstraction of an processing unit, supports planning by the - dispatcher, allows to pull data, thereby driving the actual calculation. - -Render Process -~~~~~~~~~~~~~~ -The render process brackets an ongoing calculation as a whole. It is not to be -confused with a operating system process or thread; rather it is a point of -reference for the relevant entities in the GUI and Proc-Layer in need to -connect to such a "rendering", and it holds the specific definitions for this -calculation series. A render process -_corresponds to a single data stream_ to be rendered. Thus, when the play - controller of some timeline in the model is -in _playing_ or _paused_ state, typically multiple corresponding render -processes exist. - -* there is an displayer- or output slot, which got allocated on creation - of the process -* the process disposes calculated data frames "into" this slot -* the process can be paused/started and stopped (aborted, halted). -* some processes allow for changing parameters dynamically (e.g. speed, - direction) -* each process has to ensure that the output/display slot gets closed or - released finally - -.Process parameters -A process is linked to a single stream data format (a -> -link:StreamTypeSystem.html[stream implementation type]). + -It is configured with _frame quantisation_ and _timings_, and a _model port_ -identifier and _channel selector_. - - quantisation:: - translates time values into frame numbers. (In the most general - case this is a function, connected to the session) - - timings:: - a definition to translate global model time units in real clock time, - including _alignment_ to an external _time grid_. - - model port:: - a point in the (high level) model where output can be produced. + - This might be a global pipe in one of the model's timelines, or - it might be a _probe point_. - - channel:: - within the session and high level model, details of the stream - implementation are abstracted. Typically, a global pipe (master bus - or subgroup) corresponds to a multichannel stream, and each of these - channels might be hooked up to an individual render process - (we have to work out if that's _always the case_ or just under - _some circumstances_) - - -[NOTE] -=================== -While certainly the port and channel definition is fixed, unfortunately the -quantisation and the timings are'nt. The timings may be changed in the middle -of an ongoing render process, due to changed playback speed, shuffling or -requirements forwarded from chase-and-lock synchronisation to an external -source. We still need to discuss if Lumiera is going to support variable -framerates (several media professionals I've talked to were rather positive we -need to support that -- personally I'm still in doubt we do). Variable -framerates force us to determine the frame numbers by an integration over time -from a start position up to the time position in question. The relevant data to -be integrated is located in the session / high-level model; probably we'll then -create an excerpt of this data, but still the less quantisation will be a -function of time. Anyway, it is the render processes job to translate all kinds -of parameter changes into relevant internal API calls to reconfigure the -calculation process to fit. -=================== - - - -Engine Model (low-level Model) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The low level model is a network of interconnected render nodes. It is created -by the build process to embody any configuration, setup and further -parametrisation derived from the high-level description within the session. But -the data structure of this node network is _opaque_ and considered an -implementation detail. It is not intended to be inspected and processed by -outward entities (contrast this to the high-level model within the session, -which provides an extensive discovery API and can be manipulated by model -mutating commands). We just provide a set of _query and information retrieval -functions_ to suit the needs of the calculation process. The engine model is -_not persisted._ - -* the engine model is partitioned by a _segmentation_ of the time axis. - Individual segments can be hot-swapped. -* the engine has _exit nodes,_ corresponding to the model ports mentioned above -* each exit node provides a stream type definition plus quantisation and - alignment constraints. - -Thus, for any pair (port, time) it is possible to figure out a segment and an -exit node to serve this position. The segmentation(s) for multiple ports might -differ. To allow for effective dispatching, the model should provide -convenience functions to translate these informations into frame number ranges. -The mentioned quantisation and alignment constraints stem from the fact that -the underlying media source(s) are typically themselves quantised and the -timings might be manipulated within the processing chain. We might or might not -be able to shift the underlying media source -(it might be a live input or it might be tied to a fixed timecode) - - - -Processing Node -~~~~~~~~~~~~~~~ -In this context, a node is a conceptual entity: it is an elementary unit of -processing. It might indeed be a single invocation of a _processor_ (plugin or -similar processing function), or it might be a chain of nodes, a complete -subtree, it might _represent_ a data source (file, external input or peer in -case of distributed rendering), or it might stand for a pipeline implemented in -hardware. The actual decision about these possibilities happened during the -build process and can be configured by rules. Information about these decisions -is retained only insofar it is required for the processing, most of the -detailed type information is discarded after the wiring and configuration step. -As mentioned above, each node serves two distinct purposes, namely to assist -with the planning and dispatching, and to pull data by performing the -calculations. - -Nodes can be considered _stateless_ -- pulling a node has no effect outside the -invocation context. While a node _might_ actually be configured to drive a -whole chain or subtree and propagate the pull request -_within_ this tree or chain internally, the node _never propagates a pull - request beyond its realm._ The pull() -call expects to be provided with all prerequisite data, intermediary and output -buffers. - - -Dispatching Step -~~~~~~~~~~~~~~~~ -The dispatcher translates a render process (actually a _calculation stream_ as -part of a render process) into sequences of node invocations, -which then can be analysed further (including planning the invocation of -prerequisites) and scheduled. This mapping is assisted by the engine model API -(to find the right exit node in the right segment), the render process (for -quantisation) and the involved node's invocation API (to find the -prerequisites) - - -Node Invocation API -~~~~~~~~~~~~~~~~~~~ -As nodes are stateless, they need to be embedded into an invocation context in -order to be of any use. The node invocation has two distinct stages and thus -the invocation API can be partitioned in two groups - -Planning -^^^^^^^^ -During the planning phase, the dispatcher retrieves various informations -necessary to _schedule_ the following pull call. These informations include - - * reproducible invocation identifier, usable to label frames for caching - * opaque source identifier (owned by the backed) in case this node - represents a source - * prerequisite nodes - * index (channel) of the prerequisite's output to be fed as input buffer(s) - * number and size of the output buffers required - * additional memory required - * control data frame(s) - - -Node pull -^^^^^^^^^ - * the pull call expects to be provided with all the resources announced during - the planning step - * moreover, the pull call needs to know (or some way to figure out) the time - coordinates - * after retrieving automation, the control flow forwards to the actual - processing function - * there is an result/error code (assuming the scheduler prefers error codes - over exceptions) - - -'''' - -Tasks -~~~~~ - * find out if we need to support variable framerate - ([green]#-> yes, implementation deferred#) - * find out about the exact handling of multichannel data streams ([green]#✔ done#) - * design and prototypical implementation of frame quantisation ([green]#✔ done#) - * design a buffer descriptor ([green]#✔ done#) - * design a buffer designation scheme [red]#TODO# - * expand on the node identification scheme [red]#TODO# - * clarify how control data frames can be addressed [red]#TODO# - - -Discussion -~~~~~~~~~~ - -Pros/Cons/Alternatives -^^^^^^^^^^^^^^^^^^^^^^ -Currently we're focussing on how to implement _this_ concept, not on -evaluating alternatives. Especially the idea of scheduling individual frame jobs -is a core concept of Lumiera. This RfC tries to bridge from the session model to -an engine based on these concepts. It's the attempt to link two concepts already -defined and decided on.... - - -Rationale -^^^^^^^^^ -* allow for optimal resource use and avoid blocking of threads -* shift away complexity from the engine into the builder, which is by far not - so performance critical -* allow to adjust the actual behaviour of the engine in a wide range, based on - actual measurements -* create a code structure able to support the foreseeable extensions (hardware - and distributed rendering) without killing maintainability - - - - -//Conclusion -//---------- -//conclusion: When approbate (this proposal becomes a Final) -// write some conclusions about its process: - - - - -Comments --------- -//comments: append below - -.State -> Draft -Requirements and details of the design are sufficiently clear meanwhile. -Ther seems to be not much room for alternative approaches, given our -general planning for the application - - Mi 11 Mai 2011 19:27:12 CEST Ichthyostega - - -//endof_comments: - -'''' -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] - diff --git a/doc/devel/rfc_pending/EngineInterfaceOverview.txt b/doc/devel/rfc_pending/EngineInterfaceOverview.txt new file mode 120000 index 000000000..9fe44386a --- /dev/null +++ b/doc/devel/rfc_pending/EngineInterfaceOverview.txt @@ -0,0 +1 @@ +../rfc/EngineInterfaceOverview.txt \ No newline at end of file diff --git a/doc/devel/rfc_pending/EngineInterfaceSpec.txt b/doc/devel/rfc_pending/EngineInterfaceSpec.txt deleted file mode 100644 index 2b4be964e..000000000 --- a/doc/devel/rfc_pending/EngineInterfaceSpec.txt +++ /dev/null @@ -1,234 +0,0 @@ -Engine Interface Spec -===================== - -// please don't remove the //word: comments - -[grid="all"] -`------------`----------------------- -*State* _Idea_ -*Date* _Mi 11 Mai 2011 17:53:16 CEST_ -*Proposed by* Ichthyostega -------------------------------------- - -[abstract] -******************************************************************************** -The main service of the Renderengine is to deliver a stream of calculations -bound by timing constraints. The parameters of this delivery can be reconfigured -******************************************************************************** - -Try to start the integration and overall design of the Lumiera Render Engine. -Point of reference is the functionality other parts of the application are relying on. - -Description ------------ -//description: add a detailed description: -The Engine is driven by the Player subsystem and draws on the low-level Model -(Render nodes network) for all local parameters and control data. The goal is -to deliver all the typical playback and rendering operations commonly found -in NLE applications (functional requirements). Moreover, this functionality -shall be delivered in a robust and reliable fashion, while making optimal -use of the available I/O bandwidth and computation power (non-functional -requirements). - -Requirements Specification -~~~~~~~~~~~~~~~~~~~~~~~~~~ -.functional -- simple calculation stream -- with or without defined end point -- deliver to fixed output buffer(s) with high timing precision -- ability to deliver individual data chunks (e.g. single frames) -- ``free wheeling'' operation for maximum calculation throughput -- throttled operation using idle calculation or bandwidth resources -- streams with regular stepping and arbitrary block sizes -- looping, seamless chaining of calculation streams with ongoing timing -- ability to pause and to change / combine any of the above any time -- ability to abort or change, providing reliable feedback on completion -- ability to relocate (shift in time) parts of an ongoing calculation stream -- support for chase-and-lock synchronisation - -.non-functional -- protection against overload and deadlocks -- gracious degradation in case of problems -- maintain a configurable quality-of-service level -- utilise precisely the resources actually available - -Functionality description in detail -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> see the link:EngineInterfaceOverview.html[Engine/Interface overview] for -a description of the involved entities and for definitions for common terms. - -Definitions -^^^^^^^^^^^ -Calculation Stream:: - A series of similar but parametrised calculations, - bound to deliver results in sequence and in accordance to timing constraints -Timed Delivery:: - Calculation result data has to be delivered within a fixed time interval - immediately preceding the delivery deadline, so it can be consumed by - an (possibly external) output process without the need for further buffering -Output Slot:: - An abstracted facility receiving calculated data; including a scheme - to organise the output buffers, which get handed over to an independent - thread running in parallel, or maybe even to an external process - -Operation description -^^^^^^^^^^^^^^^^^^^^^ -When *creating a calculation stream*, the _exit node_ and an already opened -_output slot_ are provided, plus the _timing parameters_ (frame duration, -stepping interval length, optional start and/or endtime) - -A *free wheeling calculation stream* is created in a similar fashion, just -without timing constraints on the output delivery; i.e. just the output slot -is parametrised differently. This invocation is used to create a ``mixdown'' -or ``final render'' to be saved into an output file. - -A *background rendering mandate* is created analogous, but _without_ providing -an output slot. Rather, it is expected that the engine will cache the generated -data internally. - -When calculation stream definitions are *chained*, the follow-up calculation -stream is expected to be delivered seamlessly after the preceding stream, -without interrupting the output timings. - -*Looping* is a special kind of chained calculations, where the same segment -is delivered continuously. But note, the loop boundaries are not necessarily -aligned with the frame spacing or the output timing requirements. - -For supporting *chase-and-lock*, the engine needs a mechanism to follow an -externally provided synchronisation goal, _without_ altering the output -delivery timings. Obviously, we need to build in a strategy for handling -this problem (because the solution is bound to be different for different -kinds of media). - -The engine can expect the output slot to support *de-clicking* or *flicker -protection* -- yet the engine needs to signal precisely when this is necessary - -Quality of service -^^^^^^^^^^^^^^^^^^ -The Engine is expected to provide different quality-of-service classes, -which are requested as part of the definition parameters for a calculation stream. - -- 'SYNC_PRIORITY' means to keep up to the delivery requirements, - even if this means failing to deliver data altogether. -- 'PERFECT_RESULT' means to deliver data perfect up to the definition, - even if this means violating the timing constraints. -- 'COMPROMISE' allows the engine to take some shortcuts in order to - deliver an roughly satisfactory behaviour. Likely there will be - multiple classes of compromise. - -The quality of service is partially implemented directly by the engine -and partially passed on as parameter to the individual node invocations. -For example, the engine might decide to switch down to proxy media, while -actually the node network will perform the actual switch and reconfiguration. - -The quality of service could be implemented as a strategy, to be consulted at -various decision points. The above cases would then be just some preconfigured -default strategies. - -Reconfiguration and abort -^^^^^^^^^^^^^^^^^^^^^^^^^ -There needs to be a separate ``control channel'' to cause various reconfigurations -during an ongoing calculation process. With the exception of the output slot, all -parameters defining an calculation stream might be changed on-the-fly -- including -the possibility to abort calculation altogether. - -The engine is _not required_ to react on such change requests immediately or -synchronously. The goal is rather to integrate such changes seamlessly. -Yet we _require_... - -- a guarantee that the change request is observed within some tolerance interval - (i.e. we may block waiting on the change to happen, without risking a deadlock) -- a reliable feedback _after_ the change has happened, by invoking a response signal - (functor/callback provided with the change request) -- a guarantee not to proceed with the original setup after this signalling - (read: after receiving this feedback, resources required only by the initial - setup may be deallocated) - -Especially note that the following things might be changed in the middle of an -ongoing calculation: - -- timing parameters of the calculation stream (frame durations, stepping interval) -- start and end time -- splitting and chaining of calculation streams (e.g introducing jumps) -- adjust the looping boundaries -- toggle _paused_ state -- change the exit node to use for pulling -- relocate the nominal time position of parts of the calculation stream; - especially we expect already calculated and cached data to be re-labeled -- invalidate parts of the (nominal) time axis, forcing recalculation -- abort individual calculation streams without interfering with others. - - -Tasks -~~~~~ -// List what needs to be done to implement this Proposal: -* analyse requirements of the player subsystem ([green]#✔ done#) -* determine further informations needed during calculation [,yellow]#WIP# -* find out about timing requirements and constraints in detaill [red]#TODO# -* define the interface functions in detail [red]#TODO# -* prepare a test fixture with mock-up calculations [red]#TODO# -* implement the invocation backbone with stubbed functionality [red]#TODO# - - -Discussion -~~~~~~~~~~ - -Pros -^^^^ -// add a fact list/enumeration which make this suitable: -// * foo -// * bar ... - - - -Cons -^^^^ -// fact list of the known/considered bad implications: -The requirements placed on life changes are quite high - - - -Alternatives -^^^^^^^^^^^^ -//alternatives: explain alternatives and tell why they are not viable: - - - -Rationale ---------- -//rationale: Give a concise summary why it should be done *this* way: -The expectations for the playback and render functionality of a NLE are -pretty much set. There isn't much room for reducing functionality. -So the goal for this RfC is to precisely define the inevitable -and break it down into tangible functionality on the implementation level. - - - -//Conclusion -//---------- -//conclusion: When approbate (this proposal becomes a Final) -// write some conclusions about its process: - - - - -Comments --------- -//comments: append below - -Discussed in the May developers meeting. Seems to be basically acceptable. -_Cehteh_ proposed some small adjustments: - -- making the _QualityOfService_ rather a strategy to be queried -- treating the rescheduling a bit separate from the other changes, because - that is very common and needs to be performant. -- introducing a separate scheduler/queue for time scheduled tasks, like - with rater soft realtime requirements - - So 15 Mai 2011 00:55:24 CEST Ichthyostega - - -//endof_comments: - -'''' -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/EngineInterfaceSpec.txt b/doc/devel/rfc_pending/EngineInterfaceSpec.txt new file mode 120000 index 000000000..a0c8a7ce9 --- /dev/null +++ b/doc/devel/rfc_pending/EngineInterfaceSpec.txt @@ -0,0 +1 @@ +../rfc/EngineInterfaceSpec.txt \ No newline at end of file diff --git a/doc/devel/rfc_pending/RefactorLiblumieraOut.txt b/doc/devel/rfc_pending/RefactorLiblumieraOut.txt deleted file mode 100644 index c31fb06a2..000000000 --- a/doc/devel/rfc_pending/RefactorLiblumieraOut.txt +++ /dev/null @@ -1,240 +0,0 @@ -Refactor Liblumiera Out -======================= - -// please don't remove the //word: comments - -[grid="all"] -`------------`----------------------- -*State* _Idea_ -*Date* _Fr 22 Apr 2011 10:46:50 CEST_ -*Proposed by* Christian Thaeter -------------------------------------- - -[abstract] -******************************************************************************** -liblumiera contains alot useful and reuseable code which is already in use by -other projects -******************************************************************************** - -Description ------------ -//description: add a detailed description: -Over the time we've put some efforts into the liblumiera. I've added -some from my code which predates the lumiera project which I am using -on many other projects. This now caused that I maintain this sources in -different unrelated projects and have to cross merge and update stuff -when I do updates and fixes somewhere. I think its time to factor the -reuseable parts out into a independent library (like glib does for -gtk), in fact I had this plan long ago. - - -.What parts are eligible for a standalone library - -Anything which is something tool alike and useful for other projects and not -tied to Lumiera only. This are the algorithms/datastructures, allocators, tool -macros. Additionally some of the src/common things should be moved into the -library. I give some lists below. - -.How to name it - -Long time ago my plan was to name it 'ctlib' or 'cehlib' but meanwhile there is -enough code done by others. So I'd propose a more neutral name, still -'lumieralib' or 'lulib' would be approbiate. The only thing we have to account -for is that some parts which are too specific for Lumiera and should not be -integrated into this spinoff need either to stay in a lumiera-internal lib -(src/lib/) as currently or being moved to the respective subsystems using them -(src/backend, src/proc, src/common, ...), so the names should not clash. - -.C, C++ ... - -For myself I need the C parts, while there is C++ code which interfaces to the -C implementations and also a lot code which does nice C++ things on its own. -This possibly means that we should in fact make 2 packages out of this, one C -and one C++ library (where the C++ part is more than just the wrappers, but -also the tools and tricks which are currently in src/lib/ and reuseable). - -.Who maintains it - -Despite a spin of I think we don't want to change anything from our current -practice and maintain it by the Lumiera developers. For many parts I feel -responsible for it, but its really a part of the Lumiera codebase, despite -independently useable. - -.How to maintain it - -We need to decide about build system and documentation system. As build system -we may right start using scons. For documentation the situation is a but -different since some of my code uses pipadoc/asciidoc and other uses doxygen. - -.What not to do - -Some of the code is currently quite specific to Lumiera while it could be made -more generic. This is *NOT* subject of this RFC we may or may not do such a -refactoring but this RFC and any work resulting from this should only restrict -to simple things like necessary namespace and variable renaming and integration -in the build system. - - -C Parts -------- - -Library -~~~~~~~ -What belongs to the library - -Containers -^^^^^^^^^^ - * cuckoo hashing (cuckoo.c|h) - * linked lists (llist.h slist.h) - * cache lists (mrucache.c|h) - * splay trees (psplay.c|h) - * priority queues (not done yet) - -Runtime tools -^^^^^^^^^^^^^ - * error handling (error.h error.c) used by the other facilities too - * clib convinience wrapers (safeclib.c|h) needs better name, maybe refactor - into new facilities - -Multithreading -^^^^^^^^^^^^^^ - * locking, condition variables etc. (condition.c|h (rec)mutex.c|h, rwlock ...) - -Memory management -^^^^^^^^^^^^^^^^^ - * Memory pools (mpool.c|h) - * Temporary buffers (tmpbuf.c|h) - -Metaprogramming -^^^^^^^^^^^^^^^ - * preprecessor tools (ppmpl.h) move common preprocessor macros here - * polymorphic call helper for C (vcall.h) - -Interface system and module loader -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -except for some hardcoded references to 'lumiera_org' and '.lum' plugin names -this is quite generic, possibly moving this over could be postponed, but might -eventually be done. - -From 'src/common' ------- -interface.c interfacedescriptor.h interface.h interfaceproxy.cpp -interfaceregistry.c interfaceregistry.h plugin.c plugin_dynlib.c plugin.h ------- - - -The 'config' system could become a candidate too if it ever gets finished and -proved useful, but for the time being its better kept in Lumiera. - - -Not Library -~~~~~~~~~~~ -Too specific to Lumiera: ------ -luid.c luid.h time.h ------ - - -C++ Parts ---------- - -For most of the C++ parts I am not sure, ichthyo should decided upon these -(please edit this here) - -Library -~~~~~~~ -These look 'generic' or wrap the C parts: ------- -singleton-factory.hpp singleton.hpp singleton-policies.hpp -singleton-preconfigure.hpp singleton-ref.hpp singleton-subclass.hpp -sync-classlock.hpp sync.cpp sync.hpp thread-local.hpp -typed-allocation-manager.hpp typed-counter.hpp util.cpp util-foreach.hpp -util.hpp variant.hpp ------- - -Not Sure -~~~~~~~~ ------- -access-casted.hpp advice advice.hpp allocation-cluster.cpp -allocation-cluster.hpp bool-checkable.hpp cmdline.cpp cmdline.hpp del-stash.hpp -diagnostic-context.hpp element-tracker.hpp error.hpp (currently too -lumiera specific) exception.cpp (as before) factory.hpp format.hpp -frameid.hpp functor-util.hpp handle.hpp hash-indexed.hpp iter-adapter.hpp -iter-adapter-stl.hpp iter-source.hpp itertools.hpp iter-type-binding.hpp -lifecycle.cpp lifecycleregistry.hpp lumitime-fmt.hpp lumitime.hpp -multifact-arg.hpp multifact.hpp meta/* null-value.hpp observable-list.hpp -opaque-holder.hpp optional-ref.hpp p.hpp query.cpp query.hpp ref-array.hpp -ref-array-impl.hpp result.hpp scoped-holder.hpp scoped-holder-transfer.hpp -scoped-ptrvect.hpp searchpath.cpp searchpath.hpp sub-id.hpp symbol.hpp -symbol-impl.cpp visitor-dispatcher.hpp visitor.hpp visitor-policies.hpp -wrapper.hpp wrapperptr.hpp appstate.cpp appstate.hpp basic-setup.cpp -basic-setup.hpp DIR_INFO external guifacade.cpp instancehandle.hpp option.cpp -option.hpp query subsys.cpp subsys.hpp subsystem-runner.hpp ----- - - -Not Library -~~~~~~~~~~~ ------- -logging.cpp nobug-init.cpp nobug-init.hpp streamtype.cpp streamtype.hpp test/* -time/* time.cpp tree.hpp ------ - -Tasks -~~~~~ -// List what needs to be done to implement this Proposal: -// * first step ([green]#✔ done#) - - * decide on name, namespaces [,yellow]#WIP# - * create git repository, setup boilerplace (build system, legalese) - [,yellow]#WIP# - * move all code over into the git repos, refactor (namespace renames) () - [,yellow]#WIP# - * make Lumiera use the new lib [,yellow]#WIP# - -Discussion -~~~~~~~~~~ - -Pros -^^^^ -// add a fact list/enumeration which make this suitable: - - * I am already reuse much of its code, making it independent makes maintaining - it less burden - - -Cons -^^^^ -// fact list of the known/considered bad implications: - - * new packages, new dependencies for Lumiera instead 'batteries included' - -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 - - -//endof_comments: - -'''' -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/RefactorLiblumieraOut.txt b/doc/devel/rfc_pending/RefactorLiblumieraOut.txt new file mode 120000 index 000000000..20241682f --- /dev/null +++ b/doc/devel/rfc_pending/RefactorLiblumieraOut.txt @@ -0,0 +1 @@ +../rfc/RefactorLiblumieraOut.txt \ No newline at end of file diff --git a/doc/devel/rfc_pending/ResourceManagementBudgeting.txt b/doc/devel/rfc_pending/ResourceManagementBudgeting.txt deleted file mode 100644 index ffa488e5c..000000000 --- a/doc/devel/rfc_pending/ResourceManagementBudgeting.txt +++ /dev/null @@ -1,121 +0,0 @@ -Resource Management: Budgeting -============================== - -// please don't remove the //word: comments - -[grid="all"] -`------------`----------------------- -*State* _Idea_ -*Date* _Fri Jul 23 20:33:32 2010_ -*Proposed by* Christian Thaeter -------------------------------------- - -[abstract] -****************************************************************************** -The Profiler will give some Idea about how much Resources can me used to -optimally utilize the system. Knowing this number leads to the next challenge, -distributing the resources to different subsystems, jobs and objects. I here -introduce a budgeting system which takes care for this. -****************************************************************************** - - -Description ------------ -//description: add a detailed description: - -The idea is quite simple, for each kind of resource we have a global budget -manager which accounts for the available and used amounts of this resource. - -Each user of a resource has its own account managing his share on the resource. - -The system is completely voluntary giving only hints how much of a resource is -available for anyone. - - - -[source,C] ------------------------------------------------------------------------------- -typedef ssize_t budget_count; - -struct budgetmanager -{ - rwlock lock; - - void (*callback)(); // called on resource shortage - (resource collector) - - int sum_priorities; // sum of all client budgets .. each - client is granted available/(sum_priorities/own_priority) of the resource - - budget_count available_prefs; // configuration from preferences - budget_count available_profile; // tuned by profiler - int available_factor; // how much % from prefs vs profile - - budget_count available; // caclulated from above - budget_count allocated; // actively in use -}; - -struct budget -{ - BudgetManager manager; - int priority; - - budget_count allocated; -}; ------------------------------------------------------------------------------- - - - - -Tasks -~~~~~ -// List what would need to be done to implement this Proposal in a few words: -// * item ... - - - - -Discussion -~~~~~~~~~~ - -Pros -^^^^ -// add just a fact list/enumeration which make this suitable: -// * foo -// * bar ... - - - -Cons -^^^^ -// fact list of the known/considered bad implications: - - - -Alternatives -^^^^^^^^^^^^ -//alternatives: if possible explain/link alternatives and tell why they are not - viable: - - - -Rationale ---------- -//rationale: Describe 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 - - -//endof_comments: diff --git a/doc/devel/rfc_pending/ResourceManagementBudgeting.txt b/doc/devel/rfc_pending/ResourceManagementBudgeting.txt new file mode 120000 index 000000000..bff328cb6 --- /dev/null +++ b/doc/devel/rfc_pending/ResourceManagementBudgeting.txt @@ -0,0 +1 @@ +../rfc/ResourceManagementBudgeting.txt \ No newline at end of file diff --git a/doc/devel/rfc_pending/ResourceManagementProfiling.txt b/doc/devel/rfc_pending/ResourceManagementProfiling.txt deleted file mode 100644 index bfd1b2731..000000000 --- a/doc/devel/rfc_pending/ResourceManagementProfiling.txt +++ /dev/null @@ -1,182 +0,0 @@ -Resource Management: Profiling -============================== - -// please don't remove the //word: comments - -[grid="all"] -`------------`----------------------- -*State* _Idea_ -*Date* _Fri Jul 23 19:34:29 2010_ -*Proposed by* Christian Thaeter -------------------------------------- - -[abstract] -****************************************************************************** -From the beginning on we planned some kind of 'profiling' to adapt dynamically -to workload and machine capabilities. I describe here how statistic data can be -gathered in a generic way. This will later work together with other components -tuning the system automatically. -****************************************************************************** - - -Description ------------ -//description: add a detailed description: - -I just introduce some ideas about the planned profiling framework here, nothing -is defined/matured yet this is certainly subject for futher discussion and -refinement. - -.Requirements/Evaluation generic:: - Profiling should be sufficiently abstracted to have a single set of - datastructures and algorithms to work on a broad range of subjects - being profiled. Moreover the profiling core just offers unitless - counters, semantic will be added on top of that on a higher level. - - least possible overhead:: - Profiling itself must not cost much, it must not block and should avoid - expensive operations. Simple integer arithmetic without divisions is - suggested. - - accurate:: - We may sample data on in stochastic way to reduce the overhead, - nevertheless data which gets sampled must be accurately stored and - processed without rounding losses and drifts. - - transient values:: - It's quite common that some values can be far off either in maximum or - in minimum direction, the system should adapt to this and recover from - such false alarms. Workload also changes over time we need to find some - way to measure the current/recent workload an grand total over the - whole application runtime is rather uninteresting. While it is also - important that we adapt slow enough not to get into some osccilating - cycle. - - active or passive system:: - Profiling can be only passive collecting data and let it be analyzed by - some other component or active triggering some action when some limits - are reached. I am yet a bit undecided and keep it open for both. - - - - - - - -.Brainstorming in Code -[source,C] ------------------------------------------------------------------------------- - -typedef int64_t profile_value; - -struct profile -{ - ProfileVTable vtable; - - /* - Using trylock for sampling makes it never contend on the lock but some - samples are lost. Should be ok. - */ - mutex_t lock; /* with trylock? */ - - - /* statistics / running averages */ - - /* n being a small number 2-5 or so */ - profile_value max[n]; /* n maximum values seen so far, - decreased by recovery */ - profile_value min[n]; /* n minimum values seen so far, - increased by recovery */ - - /* store sum & count, but average calculation implies a division and will be - only done on demand */ - profile_value count; /* count profile calls */ - profile_value sum; /* sum up all calls, average = - sum/count */ - - /* current is the sampled value to be integrated */ - - /* trend is caclulated before theb new run_average */ - profile_value trend; /* trend = (trend + - (run_average-current))>>1 */ - - /* we may need some slower diverging formula for running average */ - profile_value run_average; /* run_average = (run_average + - current)>>1) */ - - - /* active limits, define whats good and whats bad, calls back to vtable when - limit is hit */ - profile_value max_limit; - profile_value min_limit; - /* do we want limits for trends too? */ - - /* we count how often we hit limits, a hit/miss ratio will give a good value - for optimization */ - profile_value hit_cnt; - profile_value high_miss_cnt; - profile_value low_miss_cnt; - - /* recovery state */ - int rec_init; - int rec_current; - int rec_percent; - - - void* extra; -}; ------------------------------------------------------------------------------- - - -Tasks -~~~~~ -// List what would need to be done to implement this Proposal in a few words: -// * item ... - - - - -Discussion -~~~~~~~~~~ - -Pros -^^^^ -// add just a fact list/enumeration which make this suitable: -// * foo -// * bar ... - - - -Cons -^^^^ -// fact list of the known/considered bad implications: - - - -Alternatives -^^^^^^^^^^^^ -//alternatives: if possible explain/link alternatives and tell why they are not - viable: - - - -Rationale ---------- -//rationale: Describe 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 - - -//endof_comments: diff --git a/doc/devel/rfc_pending/ResourceManagementProfiling.txt b/doc/devel/rfc_pending/ResourceManagementProfiling.txt new file mode 120000 index 000000000..022da4af2 --- /dev/null +++ b/doc/devel/rfc_pending/ResourceManagementProfiling.txt @@ -0,0 +1 @@ +../rfc/ResourceManagementProfiling.txt \ No newline at end of file diff --git a/doc/devel/rfc_pending/StreamTypeSystem.txt b/doc/devel/rfc_pending/StreamTypeSystem.txt deleted file mode 100644 index 12323606f..000000000 --- a/doc/devel/rfc_pending/StreamTypeSystem.txt +++ /dev/null @@ -1,268 +0,0 @@ -Stream Type System -================== - -// please don't remove the //word: comments - -[grid="all"] -`------------`----------------------- -*State* _Draft_ -*Date* _2008-10-05_ -*Proposed by* link:Ichthyostega[] -------------------------------------- - - - -******************************************************************************** -.Abstract -Especially in the Proc-Layer, within the Builder and at the interface to the -Engine we need sort of a framework to deal with different »kinds« of -media streams. + -This is the foundation to be able to define what can be connected and to -separate out generic parts and isolate specific parts. -******************************************************************************** - - -Description ------------ -//description: add a detailed description: -The general idea is that we need meta information, and -- more precisely -- -that _we_ need to control the structure of this metadata. Because it has -immediate consequences on the way the code can test and select the appropriate -path to deal with some data or a given case. This brings us in a difficult -situation: - - * almost everything regarding media data and media handling is notoriously - convoluted - * because we can't hope ever to find a general umbrella, we need an extensible - solution - * we want to build on existing libraries rather then re-inventing media - processing. - * a library well suited for some processing task not necessarily has a type - classification system which fits our needs. - -The proposed solution is to create an internal Stream Type System which acts as -a bridge to the detailed (implementation type) classification provided by the -library(s). Moreover, the approach was chosen especially in a way as to play -well with the rule based configuration, which is envisioned to play a central -role for some of the more advanced things possible within the session. - - -Terminology -~~~~~~~~~~~ - * *Media* is comprised of a set of streams or channels - * *Stream* denotes a homogeneous flow of media data of a single kind - * *Channel* denotes an elementary stream, which -- _in the given context_ -- - can't be decomposed any further - * all of these are delivered and processed in a smallest unit called *Frame*. - Each frame corresponds to a time interval. - * a *Buffer* is a data structure capable of holding one or multiple Frames of media data. - * the *Stream Type* describes the kind of media data contained in the stream - - -Concept of a Stream Type -~~~~~~~~~~~~~~~~~~~~~~~~ - -The Goal of our Stream Type system is to provide a framework for precisely -describing the ``kind'' of a media stream at hand. The central idea is to -structure the description/classification of streams into several levels. -A complete stream type (implemented by a stream type descriptor) contains -a tag or selection regarding each of these levels. - -Levels of classification -^^^^^^^^^^^^^^^^^^^^^^^^ - - * Each media belongs to a fundamental *kind of media*, examples being _Video, - Image, Audio, MIDI, Text,..._ This is a simple Enum. - * Below the level of distinct kinds of media streams, within every kind we - have an open ended collection of *Prototypes*, which, within the high-level - model and for the purpose of wiring, act like the "overall type" of the - media stream. Everything belonging to a given Prototype is considered to be - roughly equivalent and can be linked together by automatic, lossless - conversions. Examples for Prototypes are: stereoscopic (3D) video versus the - common flat video lacking depth information, spatial audio systems - (Ambisonics, Wave Field Synthesis), panorama simulating sound systems (5.1, - 7.1,...), binaural, stereophonic and monaural audio. - * Besides the distinction by prototypes, there are the various *media - implementation types*. This classification is not necessarily hierarchically - related to the prototype classification, while in practice commonly there - will be some sort of dependency. For example, both stereophonic and monaural - audio may be implemented as 96kHz 24bit PCM with just a different number of - channel streams, but we may as well have a dedicated stereo audio stream - with two channels multiplexed into a single stream. - - -Working with media stream implementations -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -For dealing with media streams of various implementation type, we need -_library_ routines, which also yield a _type classification system_ suitable -for their intended use. Most notably, for raw sound and video data we use the -http://gmerlin.sourceforge.net/[GAVL] library, which defines a fairly complete -classification system for buffers and streams. For the relevant operations in -the Proc-Layer, we access each such library by means of a Façade; it may sound -surprising, but actually we just need to access a very limited set of -operations, like allocating a buffer. _Within_ the Proc-Layer, the actual -implementation type is mostly opaque; all we need to know is if we can connect -two streams and get an conversion plugin. - -Thus, to integrate an external library into Lumiera, we need explicitly to -implement such a Lib Façade for this specific case, but the intention is to be -able to add this Lib Façade implementation as a plugin (more precisely as a -"Feature Bundle", because it probably includes several plugins and some -additional rules) - - -Link between implementation type and prototype -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -At this point the rules based configuration comes into play. Mostly, to start -with, determining a suitable prototype for a given implementation type is sort -of a tagging operation. But it can be supported by heuristic rules and an -flexible configuration of defaults. For example, if confronted with a media -with 6 sound channels, we simply can't tell if it's a 5.1 sound source, or if -it's a pre mixed orchestra music arrangement to be routed to the final balance -mixing or if it's a prepared set of spot pick-ups and overdubbed dialogue. But a -heuristic rule defaulting to 5.1 would be a good starting point, while -individual projects should be able to set up very specific additional rules -(probably based on some internal tags, conventions on the source folder or the -like) to get a smooth workflow. - -Moreover, the set of prototypes is deliberately kept open ended. Because some -projects need much more fine grained control than others. For example, it may -be sufficient to subsume any video under a single prototype and just rely on -automatic conversions, while other projects may want to distinguish between -digitized film and video NTSC and PAL. Meaning they would be kept in separate -pipes an couldn't be mixed automatically without manual intervention. - - -connections and conversions -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * It is _impossible to connect_ media streams of different kind. Under some - circumstances there may be the possibility of a _transformation_ though. For - example, sound may be visualized, MIDI may control a sound synthesizer, - subtitle text may be rendered to a video overlay. Anyway, this includes some - degree of manual intervention. - * Streams subsumed by the same prototype may be _converted_ lossless and - automatically. Streams tagged with differing prototypes may be _rendered_ - into each other. - * Conversions and judging the possibility of making connections at the level - of implementation types is coupled tightly to the used library; indeed, most - of the work to provide a Lib Façade consists of coming up with a generic - scheme to decide this question for media streams implemented by this - library. - - -Tasks -~~~~~ -// List what needs to be done to implement this Proposal: - * draft the interfaces ([green]#✔ done#) - * define a fall-back and some basic behaviour for the relation between - implementation type and prototypes [,yellow]#WIP# - * find out if it is necessary to refer to types in a symbolic manner, or if it - is sufficient to have a ref to a descriptor record or Façade object. - * provide a Lib Façade for GAVL [,yellow]#WIP# - * evaluate if it's a good idea to handle (still) images as a separate distinct - kind of media - - - -Discussion -~~~~~~~~~~ - -Alternatives -^^^^^^^^^^^^ -//alternatives: explain alternatives and tell why they are not viable: -Instead of representing types by metadata, leave the distinction implicit and -instead implement the different behaviour directly in code. Have video tracks -and audio tracks. Make video clip objects and audio clip objects, each -utilising some specific flags, like sound is mono or stereo. Then either -switch, switch-on-type or scatter out the code into a bunch of virtual -functions. See the Cinelerra source code for details. - -In short, following this route, Lumiera would be plagued by the same notorious -problems as most existing video/sound editing software. Which is, implicitly -assuming ``everyone'' just does ``normal'' things. Of course, users always were -and always will be clever enough to work around this assumption, but the problem -is, all those efforts will mostly stay isolated and can't crystallise into a -reusable extension. Users will do manual tricks, use some scripting or rely on -project organisation and conventions, which in turn creates more and more -coercion for the ``average'' user to just do ``normal'' things. - -To make it clear: both approaches discussed here do work in practice, and it's -more a cultural issue, not a question guided by technical necessities to select -the one or the other. - - -Rationale ---------- -//rationale: Give a concise summary why it should be done *this* way: - - * use type metadata to factor out generic behaviour and make variations in - behaviour configurable. - * don't use a single classification scheme, because we deal with distinctions - and decisions on different levels of abstraction - * don't try to create an universal classification of media implementation type - properties, rather rely on the implementation libraries to provide already a - classification scheme well suited for _their_ needs. - * decouple the part of the classification guiding the decisions on the level - of the high level model from the raw implementation types, reduce the former - to a tagging operation. - * provide the possibility to incorporate very project specific knowledge as - rules. - -//Conclusion -//---------- -//conclusion: When approbate (this proposal becomes a Final) -// write some conclusions about its process: - - - - -Comments --------- -//comments: append below -As usual, see the -http://www.lumiera.org/wiki/renderengine.html#StreamType[Proc-Layer impl doku] -for more information and implementation details. - -Practical implementation related note: I found I was blocked by this one in -further working out the details of the processing nodes wiring, and thus make -any advance on the builder and thus to know more precisely how to organize the -objects in the link:EDL/Session[]. Because I need a way to define a viable -abstraction for getting a buffer and working on frames. The reason is not -immediately obvious (because initially you could just use an opaque type). The -problem is related to the question what kind of structures I can assume for the -builder to work on for deciding on connections. Because at this point, the -high-level view (pipes) and the low level view (processing functions with a -number of inputs and outputs) need in some way to be connected. - -The fact that we don't have a rule based system for deciding queries currently -is not much of a problem. A table with some pre configured default answers for -a small number of common query cases is enough to get the first clip rendered. -(Such a solution is already in place and working.) + - -- link:Ichthyostega[] 2008-10-05 - -Woops fast note, I didn't read this proposal completely yet. Stream types could -or maybe should be coopertatively handled together with the backend. Basically -the backend offers one to access regions of a file in a continous block, this -regions are addressed as "frames" (this are not necessary video frames). The -backend will keep indices which associate this memory management with the frame -number, plus adding the capabilitiy of per frame metadata. This indices get -abstracted by "indexing engines" it will be possible to have different kinds of -indices over one file (for example, one enumerating single frames, one -enumerating keyframes or gops). Such a indexing engine would be also the place -to attach per media metadata. From the proc layer it can then look like `struct -frameinfo* get_frame(unsigned num)` where `struct frameinfo` (not yet defined) -is something like `{ void* data; size_t size; struct metadata* meta; ...}` + - -- link:ct[] 2008-10-06 - -Needs Work -~~~~~~~~~~ -There are a lot details to be worked out for an actual implementation but we -agreed that we want this concept as proposed here. - - Do 14 Apr 2011 03:06:42 CEST Christian Thaeter - - -//endof_comments: - -'''' -Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/StreamTypeSystem.txt b/doc/devel/rfc_pending/StreamTypeSystem.txt new file mode 120000 index 000000000..8b989a554 --- /dev/null +++ b/doc/devel/rfc_pending/StreamTypeSystem.txt @@ -0,0 +1 @@ +../rfc/StreamTypeSystem.txt \ No newline at end of file diff --git a/doc/devel/rfc_pending/WebsiteNavigation.txt b/doc/devel/rfc_pending/WebsiteNavigation.txt deleted file mode 100644 index 172b6543f..000000000 --- a/doc/devel/rfc_pending/WebsiteNavigation.txt +++ /dev/null @@ -1,160 +0,0 @@ -WebsiteNavigation -================= - -// please don't remove the //word: comments - -[grid="all"] -`------------`----------------------- -*State* _Idea_ -*Date* _Mi 08 Dez 2010 11:32:32 CET_ -*Proposed by* Ichthyostega -------------------------------------- - -[abstract] -******************************************************************************** -The Lumiera website is assumed to accumulate a lot of content. Thus we need -to care about making that content accessible, to help finding the relevant -topics and to keep the overall structure intact. This RfC is to collect, -discuss and agree upon the guidelines and requirements. -******************************************************************************** - -Description ------------ -//description: add a detailed description: - -Issues to care -~~~~~~~~~~~~~~ - -Navigation:: - The page hierarchy becomes at least 5 levels deep, likely even deeper. - When reading a page, the current subtree leading down to this page should - be right at hand; especially access to the siblings and the parent's siblings - is important. For re-accessing content, it is necessary to be able to drill - down to an known location (``within the design docs, detailing the application, - I need the configuration section'') + - -> we need an *auto generated navigation* and an embedded *menu tree widget* in the web pages. - -Tagging:: - There should be an easy way to categorise individual pages *by keyword(s)* - and an automatically generated indexing by tags, possibly with an per tag - overview page. - -Search:: - The usual *site search*. It should include the contents of the issue tracker. - Even today such a scoped search is valuable and even necessary for working - with the informations collected within the Lumiera project - -Sanity:: - Each relevant page needs to be reachable. There are some additional pages and - especially subdirectories which should not be linked into the website navigation. - Moreover, all (internal) links on the pages should be valid. + - -> this could be addressed by a **sanity checker script** - -Usage situations -~~~~~~~~~~~~~~~~ - -(a) working on content -^^^^^^^^^^^^^^^^^^^^^^ -Working on content should be readily accessible for _everyone_. One time contributions -are especially encouraged. This leads to the following usage scenario: - -A contributor has some informations to share or wants to do some additions or modifications. -(S)he locates somehow the place where relevant informations are stored, adds some text, -possibly adds a new page or splits another page in two. - -_Note_: no awareness of the issues of navigation can be assumed. The occasional contributor -won't notice any concern which isn't right at hand. - -(b) maintaining a subsystem -^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Some person(s) will be responsible for a subsystem or some segment of the informations -on the website. This responsibility is content centric. It might include frequent rearranging, -regrouping and reordering of pages to accommodate the increasing scope of informations. - -_Note_: while here some awareness of website organisational issues can be assumed, -any requirement to care for external organisational issues is a burden and distracts -from the actual work to be done -- thus it is likely to be short circuited or postponed -``for later''. Note especially, reorganising content in a subsection *must not* incur -the burden of re-doing the same reorganisation steps mirrored in some central navigation -configuration or table of contents. (this is a knock out criterion) - -(c) maintaining the website -^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The website maintainer is responsible for the overall sanity of the website, without -being familiar with all details of ongoing work in some part or section of the information. -Another concern here is the outward perception of the website, which might incur changes -on the top level navigation or some rewording of overview pages. - -_Note_: this kind of work is rather unrewarding. There is the danger of collisions with the -work of the subsystem maintainer - - -Conclusion: Requirements for any navigation solution -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * ability to to pick up a nested page structure - * ability to cope with any additions and changes in the lower levels automatically, without help by the user - * ability to override: - - - not including some subdirectories - - including links-to-external at arbitrary positions - -optional/additional features -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The following features would be handy, but can be considered optional - - * ability to change the displayed title of a page in the navigation - * ability to control the ordering of pages in the navigation - * complete manual override of the visible content of a specific subdirectory - - - -Tasks -~~~~~ -// List what would need to be done to implement this Proposal in a few words: -// * item ... - - - -Discussion -~~~~~~~~~~ - -Pros -^^^^ -// add just 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: Describe 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 - - -//endof_comments: diff --git a/doc/devel/rfc_pending/WebsiteNavigation.txt b/doc/devel/rfc_pending/WebsiteNavigation.txt new file mode 120000 index 000000000..d9f14895f --- /dev/null +++ b/doc/devel/rfc_pending/WebsiteNavigation.txt @@ -0,0 +1 @@ +../rfc/WebsiteNavigation.txt \ No newline at end of file From 5310d8a571cf1f856a9d1dbba36d1efe92a330ec Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 30 Aug 2012 21:53:26 +0200 Subject: [PATCH 400/496] RFC: Tags --- doc/devel/rfc/SemanticTags.txt | 146 +++++++++++++++++++++++++ doc/devel/rfc_pending/SemanticTags.txt | 1 + 2 files changed, 147 insertions(+) create mode 100644 doc/devel/rfc/SemanticTags.txt create mode 120000 doc/devel/rfc_pending/SemanticTags.txt diff --git a/doc/devel/rfc/SemanticTags.txt b/doc/devel/rfc/SemanticTags.txt new file mode 100644 index 000000000..363207705 --- /dev/null +++ b/doc/devel/rfc/SemanticTags.txt @@ -0,0 +1,146 @@ +Semantic tags +============= + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _Do 30 Aug 2012 21:06:54 CEST_ +*Proposed by* Christian Thaeter +------------------------------------- + +******************************************************************************** +.Abstract +We have a lot documentation which needs to be cross referenced. Adding well the +known 'tags' concept and extend it slightly with some semantics will aid future +automatic processing. +******************************************************************************** + +Description +----------- +//description: add a detailed description: + +Every document (including sourcecode) could extended with some metadata, aka tags +which are then used to build automatic crossreferences. + +Commonly tags are just 'words' which are picked up and crossreferenceds. I propose +to extend this scheme slightly. + +Overall this scheme must be very natual and easy to use. A user should not need to +know about the underlying machinery and a tag as in a single lowercase 'word' should +be sufficient in almost all cases. Moreover Tags should be optional. + + +.Ontology + +To give tags some sematics we introduce a simple ontology: + +- Tags can have namespaces, delimited by a dot 'foo.bar.baz'. + Tags are looked up from right to left 'baz' would suffice as long it is unique. + Non unique cases will be handled in context (sometimes non uniqunes is desired) +- We introduce simple "Is a" and "Has a" relationships. These are defined by the + casing of the tag: 'ALL_UPPERCASE' means "Is a" and anything else (including + mixed case) means "Has a". Note that for most cases the "Is a" relation will be + defined implicitly, ín normal cases one doesnt need to care. +- define some tag algebra for lookups (group tags by comma and semicolons, where + comma means 'and' and semicolon means 'or'). Used to query the tag database. + regex/globbing might become handy too. + +.Implicit Tags + +Tags can be implicit by generating them from the document: + +- Derrive tags from the type and location of the Document. + RFC's are 'RFC', source files are 'SOURCE.C' and so on. + +- Derrive Tags from the content of the document. + Asciidoc titles will be used here. A simple preprocessor + generates a tag from a title (make it CamelCase, simplify etc.) + The resulting tag is only used iff it is unique + + +.Use this tags + +Tags are collected/discovered by some script which creates a tag-database +(possibly plaintext asciidoc files) as big project index linking back to the content, +details need to be worked out. + +We create special asciidoc macros for crossreferencing tags for example: 'RFC:foobar' +'SOURCE:builder', details need to be worked out later. + +Note: this Proposal is about including tags in the first place, processing them is only +suggested and left out for later. + + +Tasks +~~~~~ +// List what needs to be done to implement this Proposal: +// * first step ([green]#✔ done#) +// * second step [,yellow]#WIP# + +We need to define how to integrate tags in different documents syntactically. +For RFC's these will likely become a part of the initial table. in other Asciidoc +documents they could be a special comment or header. For Source files special comments +will be used. + +Tags themself will be added lazily on demand (unless we find someone with the patience +to go over all documents and tag them properly). + +Creating the infrastructure handling this tags (cross indexing etc) is not part of +this proposal, nevertheless we planning this since some time and it will be defined in +other RFC's. + + +Discussion +~~~~~~~~~~ + +Pros +^^^^ +// add a fact list/enumeration which make this suitable: +// * foo +// * bar ... + + * Gives a simple graspable way to build a cross reference over the whole project + + +Cons +^^^^ +// fact list of the known/considered bad implications: + + * adding tags and developing the tools manging them will take some time + + +Alternatives +^^^^^^^^^^^^ +//alternatives: explain alternatives and tell why they are not viable: + +We have the ht/dig search function over the Website which give a much simpler way to +find documents. + + +Rationale +--------- +//rationale: Give a concise summary why it should be done *this* way: + +It is very urgent and important that we make our content much easier accessible. + + + +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: + + + + +Comments +-------- +//comments: append below + + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/SemanticTags.txt b/doc/devel/rfc_pending/SemanticTags.txt new file mode 120000 index 000000000..661e961b3 --- /dev/null +++ b/doc/devel/rfc_pending/SemanticTags.txt @@ -0,0 +1 @@ +../rfc/SemanticTags.txt \ No newline at end of file From e9a2367600cbd9411069525d14e3c1e9111adc72 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 31 Aug 2012 02:47:13 +0200 Subject: [PATCH 401/496] FIX: rfc.sh link detection and discard --- admin/rfc.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/admin/rfc.sh b/admin/rfc.sh index 5e76eaf28..743ea4c8f 100755 --- a/admin/rfc.sh +++ b/admin/rfc.sh @@ -258,9 +258,10 @@ function process_file() exit 1 esac - local oldpath=./doc/devel/rfc_*/$basename + local oldpath + for oldpath in ./doc/devel/rfc_*/$basename; do :; done - if [[ "$oldpath" != './doc/devel/rfc_*/'"$basename" ]]; then + if [[ -h "$oldpath" ]]; then if [[ "$oldpath" != "$linkdest/$basename" ]]; then git mv "$oldpath" "$linkdest/$basename" fi @@ -454,8 +455,15 @@ comment) ;; discard) name=$(unique_name "$1") + if [[ "$name" ]]; then - git rm "${name}" + for link in ./doc/devel/rfc_*/${name##*/}; do :; done + + if [[ -h "$link" ]]; then + git rm -f "${link}" || rm "${link}" + fi + + git rm -f "${name}" || rm "${name}" fi ;; wrap) From 5da7f0cd6e567c1815377bc7f2759b0c2abcfb1a Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 31 Aug 2012 04:47:01 +0200 Subject: [PATCH 402/496] RFC: Git Commit Message Format Formal description for the format of commit messages. --- doc/devel/rfc/GitCommitMessageFormat.txt | 167 ++++++++++++++++++ .../rfc_pending/GitCommitMessageFormat.txt | 1 + 2 files changed, 168 insertions(+) create mode 100644 doc/devel/rfc/GitCommitMessageFormat.txt create mode 120000 doc/devel/rfc_pending/GitCommitMessageFormat.txt diff --git a/doc/devel/rfc/GitCommitMessageFormat.txt b/doc/devel/rfc/GitCommitMessageFormat.txt new file mode 100644 index 000000000..2396940d3 --- /dev/null +++ b/doc/devel/rfc/GitCommitMessageFormat.txt @@ -0,0 +1,167 @@ +Git Commit Message Format +========================= + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _Fr 31 Aug 2012 03:54:14 CEST_ +*Proposed by* Christian Thaeter +------------------------------------- + +******************************************************************************** +.Abstract +This RFC describes the format of commit messages, based on already used +practice. +******************************************************************************** + +Description +----------- +//description: add a detailed description: + +Git commit messages are almost free form text, the only exception is that git +threats the first line in a commit message specially to form the header one +sees in various outputs. + +Since we aim to automate some processing we refine the format for commit +messages sightly more. + + +.General + +Commit Messages will be shown in space limited areas (lists on webpages, +emails, tooltips). Unlike source code where we are quite lax about line +lengths commit messages should not exceed 80 characters per line. + +The first line is treated as Header as described below, followed by an empty +line and then the Body of the commit message. The Body is optional but +recommended. + + +.Header + +The Header is free form text explaining the purpose of the commit in a few +words. It may start with one uppercased keyword and a colon if appobiate. This +Keywords are optional but recommended since automatic processing acts upon +them. Normal work doesn't have any of these keywords but just free form text. + +'WIP:':: + 'Work in Progress', commits marked as this may be incomplete and fail + in various ways. For example automatic git-bisecting will skip them. + WIP should take precedence, if for example one needs multiple commits + to fix a bug, then only the final commit is tagged as FIX: and the + leading commits are WIP: + +'FIX:':: + Bugfix. The Text should explain what error got fixed, Details like a + bugtracker number or other references shall not be part of the Header. + +'RFC:':: + This commit adds or modifies a RFC but doesn't touch the codebase + (tests/examples are permitted). + +'DOC:':: + This commit only alters documentation but no code. + +'STASH:':: + When 'git-stash' is not enough (for example one wants to move + unfinished changes changes to another repository or commit as backup and amend + things later). Normally such commits should not remain in a published + repository and not become merged. + +'DRAFT:':: + Much like WIP: but will not break compilation and is sane to use. For + example mockups, documentation and skeleton code for new facilities + may use this. + +'TODO:':: + This commit adds a documentation, comments or tests about something to + be done. Same rules as NoBug's TODO apply. + +'FIXME:':: + This commit adds a documentation, comments or tests about something to + be fixed. Aka a known bug which can not be fixed instantly for some + reason. Same rules as NoBug's FIXME apply. + +'PLANNED:':: + This commit adds a documentation, comments or tests about something + planned. Same rules as NoBug's PLANNED apply. + + +... any other suggestions? ... + + +.Body + +Adding a body is optional but recommended but for the most simple changes. +A body, if present should separate from the header by one empty line. + +It is suggested not to make any lines longer than 80 characters and use +asciidoc formatting. In most cases this means just free form text and maybe +use of bulleted list. + +Care should be taken to write clean understandable commit messages. In some +extent they may repeat the comments and documentation from the committed code +in a short form. Think that anyone else reading only the commit message should +understand whats going on. + + +Tasks +~~~~~ +// List what needs to be done to implement this Proposal: +// * first step ([green]#✔ done#) +// * second step [,yellow]#WIP# + + +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: + +This RFC is based on existing practice, we almost done it this way. some minor +glitches are present in the project history (no colon after keywords, +lowercase keywords). Automatic processing becomes simpler when we formalize +these things in an unambigous way. Commits failing this definitions might +confuse the toolchain (builddrone) but this failures shall not be critical. + + +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: + + + + +Comments +-------- +//comments: append below + + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/GitCommitMessageFormat.txt b/doc/devel/rfc_pending/GitCommitMessageFormat.txt new file mode 120000 index 000000000..1de41b3f6 --- /dev/null +++ b/doc/devel/rfc_pending/GitCommitMessageFormat.txt @@ -0,0 +1 @@ +../rfc/GitCommitMessageFormat.txt \ No newline at end of file From 2613efca439c06ae70241f6e40f85d480cb17aae Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Fri, 31 Aug 2012 04:55:40 +0200 Subject: [PATCH 403/496] Add full-text search command to rfc.sh --- admin/rfc.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/admin/rfc.sh b/admin/rfc.sh index 743ea4c8f..bbec05e0c 100755 --- a/admin/rfc.sh +++ b/admin/rfc.sh @@ -374,6 +374,9 @@ process) done : ;; +search) + grep -r -C3 -n "$1" ./doc/devel/rfc | less -F + ;; find|list|ls) if [[ "$2" ]]; then find_rfc "$1" | xargs grep -i -C3 -n "$2" From 34f45e28b263769dd92f404b0f11d3e9f4b9695c Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Wed, 12 Sep 2012 21:58:11 +0200 Subject: [PATCH 404/496] add 'DONE' to the GitCommitMessage rfc --- doc/devel/rfc/GitCommitMessageFormat.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/devel/rfc/GitCommitMessageFormat.txt b/doc/devel/rfc/GitCommitMessageFormat.txt index 2396940d3..b6c0c9237 100644 --- a/doc/devel/rfc/GitCommitMessageFormat.txt +++ b/doc/devel/rfc/GitCommitMessageFormat.txt @@ -89,6 +89,11 @@ them. Normal work doesn't have any of these keywords but just free form text. planned. Same rules as NoBug's PLANNED apply. +'DONE:':: + Final commit/merge when some noteworthy part is done. The idea here is + that finished things could be easily filtered out for our quarterly reports. + + ... any other suggestions? ... From 77a04f1e3f3696007efbcbc944bf535252317d6d Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 13 Sep 2012 01:40:04 +0200 Subject: [PATCH 405/496] RFC: add ALPHA, BETA, RELEASE to the CommitMessage RFC --- doc/devel/rfc/GitCommitMessageFormat.txt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/doc/devel/rfc/GitCommitMessageFormat.txt b/doc/devel/rfc/GitCommitMessageFormat.txt index b6c0c9237..0aa4638e2 100644 --- a/doc/devel/rfc/GitCommitMessageFormat.txt +++ b/doc/devel/rfc/GitCommitMessageFormat.txt @@ -75,6 +75,10 @@ them. Normal work doesn't have any of these keywords but just free form text. example mockups, documentation and skeleton code for new facilities may use this. +'DONE:':: + Final commit/merge when some noteworthy part is done. The idea here is + that finished things could be easily filtered out for our quarterly reports. + 'TODO:':: This commit adds a documentation, comments or tests about something to be done. Same rules as NoBug's TODO apply. @@ -88,10 +92,17 @@ them. Normal work doesn't have any of these keywords but just free form text. This commit adds a documentation, comments or tests about something planned. Same rules as NoBug's PLANNED apply. +'ALPHA:':: + Notifies the CI system that this commit defines an ALPHA release, the CI + may take this and build an package accordingly -'DONE:':: - Final commit/merge when some noteworthy part is done. The idea here is - that finished things could be easily filtered out for our quarterly reports. +'BETA:':: + Notifies the CI system that this commit defines an BETA release, the CI + may take this and build an package accordingly + +'RELEASE:':: + Notifies the CI system that this commit defines an production release, the CI + may take this and build an package accordingly ... any other suggestions? ... From 0c7e5822f0acc863610de86cb5e68b08aaac4895 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 13 Sep 2012 03:57:08 +0200 Subject: [PATCH 406/496] RFC: some refinements about git commit messages * when headers are used, then only the defined ones * metadata may follow headers directly * relax the absence if bugtracker data --- doc/devel/rfc/GitCommitMessageFormat.txt | 30 +++++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/doc/devel/rfc/GitCommitMessageFormat.txt b/doc/devel/rfc/GitCommitMessageFormat.txt index 0aa4638e2..94522e089 100644 --- a/doc/devel/rfc/GitCommitMessageFormat.txt +++ b/doc/devel/rfc/GitCommitMessageFormat.txt @@ -38,13 +38,31 @@ The first line is treated as Header as described below, followed by an empty line and then the Body of the commit message. The Body is optional but recommended. +This formalized keywords for headers are optional, if in doubt then don't use any. +But if one uses them, then use only one defined here since automatic processing +only knows about these. + .Header The Header is free form text explaining the purpose of the commit in a few -words. It may start with one uppercased keyword and a colon if appobiate. This -Keywords are optional but recommended since automatic processing acts upon -them. Normal work doesn't have any of these keywords but just free form text. +words. It may start with one uppercased keyword and a colon if appobiate directly +followed by some (optional, defined elsewhere) metadata. This Keywords are +optional but recommended since automatic processing acts upon them. Normal commits +need these keywords and are just free form text. + +To be exact, here is a regex matching valid Headers: + + ^[[:upper:]]+:[[:punct:]]?[[:alnum:][:punct:]]* + +Legal headers are for example: + + DONE: some feature + FIX:#1234 Segv when starting + RELEASE:1.0 Party + + +.List of Defined headers: 'WIP:':: 'Work in Progress', commits marked as this may be incomplete and fail @@ -54,8 +72,8 @@ them. Normal work doesn't have any of these keywords but just free form text. leading commits are WIP: 'FIX:':: - Bugfix. The Text should explain what error got fixed, Details like a - bugtracker number or other references shall not be part of the Header. + Bugfix. The Text should explain what error got fixed. A referenence to + a bug number is not optional and not needed. 'RFC:':: This commit adds or modifies a RFC but doesn't touch the codebase @@ -105,7 +123,7 @@ them. Normal work doesn't have any of these keywords but just free form text. may take this and build an package accordingly -... any other suggestions? ... +Note: This list will be updated as need arises .Body From 61e5a6a47b6003ee93f5769f48a9e4825413cead Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 13 Sep 2012 03:59:45 +0200 Subject: [PATCH 407/496] RFC: finalize GitCommitMessageFormat --- doc/devel/rfc/GitCommitMessageFormat.txt | 9 ++++++++- .../GitCommitMessageFormat.txt | 0 2 files changed, 8 insertions(+), 1 deletion(-) rename doc/devel/{rfc_pending => rfc_final}/GitCommitMessageFormat.txt (100%) diff --git a/doc/devel/rfc/GitCommitMessageFormat.txt b/doc/devel/rfc/GitCommitMessageFormat.txt index 94522e089..3446ff87d 100644 --- a/doc/devel/rfc/GitCommitMessageFormat.txt +++ b/doc/devel/rfc/GitCommitMessageFormat.txt @@ -5,7 +5,7 @@ Git Commit Message Format [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Final_ *Date* _Fr 31 Aug 2012 03:54:14 CEST_ *Proposed by* Christian Thaeter ------------------------------------- @@ -194,6 +194,13 @@ Comments -------- //comments: append below +.State -> Final +//add reason + +We decided on the sept. 2012 devel meeting to finlize this RFC. + +Christian Thaeter:: 'Do 13 Sep 2012 03:57:23 CEST' ~~ + //endof_comments: diff --git a/doc/devel/rfc_pending/GitCommitMessageFormat.txt b/doc/devel/rfc_final/GitCommitMessageFormat.txt similarity index 100% rename from doc/devel/rfc_pending/GitCommitMessageFormat.txt rename to doc/devel/rfc_final/GitCommitMessageFormat.txt From d1558bbe95cfd3ae695dcca918bb3a5af6c0f997 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 18 Sep 2012 02:31:44 +0200 Subject: [PATCH 408/496] Fix navigation for RfCs --- doc/devel/rfc.txt | 9 +++++---- doc/devel/rfc/index.txt | 7 +++++-- doc/devel/rfc_final/index.txt | 6 ++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/doc/devel/rfc.txt b/doc/devel/rfc.txt index 9879727e2..d435feded 100644 --- a/doc/devel/rfc.txt +++ b/doc/devel/rfc.txt @@ -1,18 +1,19 @@ Design Process ============== -//Menu: include rfc -//Menu: include rfc_parked +//Menu: include rfc_final //Menu: include rfc_pending +//Menu: include rfc_parked //Menu: include rfc_dropped -//Menu: put child 'rfc_dropped' after 'rfc' +//Menu: put child 'rfc_dropped' after 'rfc_final' + How it Works ------------ Design Process entries (Request for Comment) can be created by anyone. Each entry goes through several stages until it's accepted (or rejected). -All our RfC entries are filed here, either in the link:rfc/index.html[RfC accepted] section, +All our RfC entries are filed here, either in the link:rfc_final/index.html[RfC accepted] section, or as link:rfc_pending/index.html[pending RfC] or as link:rfc_dropped/index.html[RfC dropped]. * Every proposal starts out as _*Idea*_, allowing other people to review and comment on it, while still working out details. diff --git a/doc/devel/rfc/index.txt b/doc/devel/rfc/index.txt index 23814933a..ecb1fd47c 100644 --- a/doc/devel/rfc/index.txt +++ b/doc/devel/rfc/index.txt @@ -1,10 +1,13 @@ Design Proposal Pool ==================== -//Menu: off +//Menu: discard //Menu: sort children -> read link:../rfc.html[more about Lumiera RfC] and the Design Process -This is the poll where all Design Proposals are stored. +This is the pool where all Design Proposals are stored. +According to the current state of each RfC (pending, final, dropped, parked), +there is also a symlink from the respective subdirectory to the actual document +stored here. diff --git a/doc/devel/rfc_final/index.txt b/doc/devel/rfc_final/index.txt index c9e8a4515..a29cff26d 100644 --- a/doc/devel/rfc_final/index.txt +++ b/doc/devel/rfc_final/index.txt @@ -6,6 +6,8 @@ Accepted Design Proposals -> read link:../rfc.html[more about Lumiera RfC] and the Design Process -The RfC entries listed here where discussed, maybe modified and finally agreed upon -during some developers meeting in the past. So they represent design decisions taken. +The RfC entries listed here where discussed, modified and finally agreed upon +during some developers meeting in the past. + +So they represent design decisions taken and can be considered binding. From b01159c0650643ef9bae797ae4ec7fb238c1ca08 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 18 Sep 2012 02:43:51 +0200 Subject: [PATCH 409/496] link the "Intro" text more prominently --- doc/devel/meeting_summary/index.txt | 7 +++++++ doc/index.txt | 8 ++++++-- doc/user/index.txt | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/doc/devel/meeting_summary/index.txt b/doc/devel/meeting_summary/index.txt index ed3cf1f5a..06b15df36 100644 --- a/doc/devel/meeting_summary/index.txt +++ b/doc/devel/meeting_summary/index.txt @@ -12,6 +12,13 @@ Anyone interested in Lumiera development is also encouraged to read mailing list archives and other documentation. +************************ +During Summer 2011 again the regular monthly meethings +were mostly informal detail discussions plus some +organisational planning. Not much to account for. +************************ + + 11 Jan 2012 ----------- diff --git a/doc/index.txt b/doc/index.txt index 36ab3139a..268479de4 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -4,12 +4,16 @@ This documentation section contains documentation for both users and developers. == User == -The user manual containing the full reference for using Lumiera : +As a start, you should visit the introductory text +link:user/intro/intro.html[Lumiera (as seen) from Outer Space] + +A user manual containing the full reference for using Lumiera is planned: * link:user/manual.html[User Manual] _(planned)_ == Developer == -Informations about the structure of Lumiera, they give an overview on the main components of the application : +Information about the structure and concepts Lumiera is based upon. +These documents give an overview on the main components of the application: * link:design/index.html[Design Documents] diff --git a/doc/user/index.txt b/doc/user/index.txt index 6b967999c..f52a9f06d 100644 --- a/doc/user/index.txt +++ b/doc/user/index.txt @@ -5,7 +5,7 @@ User Documentation // Menu : attach child 'manual' after 'intro' * link:manual.html[User Manual] _(planned)_ - * The following document might become an introductory overview: + + * The following document contains an introductory overview: + link:intro/intro.html[Lumiera (as seen) from Outer Space] * link:intro/Glossary.html[Glossary of common terms] From fa8351248c0ba093e196fbeb0a8d1e36efaf9c24 Mon Sep 17 00:00:00 2001 From: Benny Lyons Date: Sun, 26 Aug 2012 16:49:58 +0100 Subject: [PATCH 410/496] Reworked at Froscon 2012 --- doc/user/intro/intro.txt | 180 +++++++++++++++++++++++---------------- 1 file changed, 106 insertions(+), 74 deletions(-) mode change 100644 => 100755 doc/user/intro/intro.txt diff --git a/doc/user/intro/intro.txt b/doc/user/intro/intro.txt old mode 100644 new mode 100755 index 1e846a3f1..eaf61e796 --- a/doc/user/intro/intro.txt +++ b/doc/user/intro/intro.txt @@ -2,32 +2,33 @@ Lumiera (as seen) from Outer Space ================================== :Author: Lumiera_Core_Developers :Date: Summer 2010 - + [abstract] ****************************************************************************** -The Lumiera Community creates a non linear video editing and compositing FOSS -application for Linux/Unix/Posix Operating Systems, suitable for professional -and quality oriented work, building on common open source video, sound and GUI -toolkits and libraries, providing flexibility and a high degree of -configurability and full control of all parameters, but at the same time a -smooth workflow which scales well to larger and more complicated editing -projects. This Document outlines the Design from some distance, -helping people to understand the Ideas behind Lumiera and understand the tools -they get to work with. It is aimed for workflow designers any anyone who wants -to know how the program works in general. +The Lumiera Community is in the process of making a non-linear video editing +and compositing FOSS application for Linux/Unix/Posix Operating Systems. The +application is geared towards professional, high-quality work; but +it is equally suitable for low-end users, due to its in-design scalability. +Lumiera builds on common open source video, sound and GUI toolkits and +libraries, being highly flexibile, configurable---user-control over a broad +spectrum of configurable parameters---and with smoth workflows that scale well +to larger more intricate projects as and more smaller projects. + +This document outlines the design from a more general perspective, +providing potential users with sufficient insight into the tools and technology +behind Lumiera to get started working with Lumiera quickly. ****************************************************************************** // 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; -some things are not worked out in detail yet. Although this document is heavily -cross-linked we try to start with a broad overview and work out more detailed -things towards the end. +// It contains many hyper-links to explanations which are denoted by an arrow ->. +Lumiera is still under active development. Here we describe planned features +without explicitly tagging them; some points have still to be worked out in +detail. Although this document is heavily cross-linked, we try to start with a +broad overview and developing details towards the end. Vision @@ -35,41 +36,48 @@ Vision // objective and goals of the project -Lumiera claims to be a _professional non-linear video editor_. To start with, we should +Lumiera strives towards being 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'': +It's more of an attitude or frame of mind -- doing work seriously, and to be subject to any +kind of wider goal, demand, or purpose. + +The concept of professionality in film editing can mean something of an artistic +nature, a narrative or having a meaning to convey, a political message, or +portray something to an audience. + +Anyhow, for the tools, the editing software used to this end, we can identify +several properties and requirements, to be labeled ``professional'': + +With this perspective in mind, we can identify a number of key properties +of professional film production tools: Reliability:: - Whatever happens, your work must be safe, protected against software - glitches and incompatibilities. Ideally Lumiera should be very stable and - never crash, in practice even crashes or power outages should not - result in lost work. + Your work must be safe and protected at all costs against software + glitches and incompatibilities. Ideally, Lumiera should be reliable, + very stable and not crash. In practice, even crashes or power outages + should not result in data or work loss.. 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. - + The demands placed on high-quality, cinema grade digital video material + requires crisp-quality without any compromsise throught the entire work + flow in the final product. All rendering will have to be reproducable + down to the last digit. + 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. + Professionals want to get things done, in time and content, but ideally + with control over all details. The fine balance of these goals is a + central goal of workflow design and usability. Scalability and Adaptability:: Projects and budgets differ, hardware advances, Lumiera must scale - in different dimensions and use the available resources as best as it + in different dimensions and use available resources as best it can. From small Laptops to multi core computers and Renderfarms. 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 + current state of technology but must be flexible enough to extend the + system without breaking compatibility. Projects you create nowadays with + Lumiera should be usable in the foreseeable future, there at least needs to be a guaranteed upgrade path. @@ -137,24 +145,24 @@ final video. Pulling not Pushing ~~~~~~~~~~~~~~~~~~~ -On a first glance, it looks fairly natural to set up the graphs xref:graphs[->] +At a first glance, it looks fairly natural to set up the graphs xref:graphs[->] as described above and then push data into the system through the input nodes whereas the final result can then be seen soon on the output node. Several -multimedia frameworks use this approach. But it has a lot of shortcomings -which make it inappropriate for non-linear video editing. +multimedia frameworks use this approach. However this scheme exhibits a number +of shortcomings which make it inappropriate for non-linear video editing. -Lumiera instead pulls data though the pipe, that is a request starts at the -output node and makes it way up to the inputs. This has certain advantages -xref:pull[->], explained later. +Lumiera instead pulls data though the pipe, i.e., a request starts at the +output node and makes its way up to the inputs. This has certain advantages +xref:pull[->], which will be explained later. Don't waste work ~~~~~~~~~~~~~~~~ -Rendering A/V Data can be quite CPU intensive, to ensure that we do not waste -CPU power by rendering things twice, or the worse, have to throw results away -because they couldn't be rendered in time, we use sophisticated caching -xref:caching[->] and profiling xref:profiling[->]. +Rendering A/V Data can be quite CPU intensive. To ensure that we do not waste +any CPU power by rendering things twice, or worse still, having to throw away +results because it couldn't rendered in time, we use in Lumiera sophisticated +caching xref:caching[->] and profiling xref:profiling[->]. The visible Universe @@ -180,18 +188,14 @@ xref:screenconcept[->] to adapt to different workplaces and workflows. Viewer ~~~~~~ -[red]#to be written# - -// only one viewer type used for everything -// how is audio integrated in the viewer -// effects may add overlays (masking/rotoscoping, information for example) -// these may be manipulateable in the viewer, but not part of the rendered -// video. Maybe effects can add widgets to the viewer too (how, where?) -// one can open as many viewers he needs -// these can be attached everyhere in the processing graph (pre/post effect) -// have bus in front to adapt output format -// detachable window, fullscreen, external screen +The viewer is an area where material can be displayed, i.e., ``play-back'', +which also supports audio playback connections. As there are many sources that +can be displayed, a viewer is attached to a source via the viewer switch board. +Timelines, probepoints, wiretaps and individual clips are examlpes of sources +that can be atached to a viewer. Moreover, the number of viewers open at any one +time is only limited by the hardware, and each viewer can be collapsed, hooked +up to a beamer or monitor. Transport Controls @@ -222,13 +226,26 @@ no magic here. Timeline View ~~~~~~~~~~~~~ -hierarchical tracks, not just a list +A timeline is a container that provides a time axis and an output. The output +can de derived from various sources and have different configurations. An +output can have various configurations, for each output configuration, there +will be one timeline. A timeline does not temporally arrange material, this is +performed by a sequence, which can be snapped to a timeline. -Format Independent Timeline, one can put anything on the timeline. -the busses constrain what kind of data is pulled out and in turn the -builder creates a processing graph which does the necessary conversions and -stuff. +A typical film will define many sequences, but only a few timelines. A sequence +contains a number of tracks which are ordered in a hierarchy. Tracs do not have +any format associated with them and more or less anything can be put into a +track. Consequently, audio and video material can be equally assigned to a +track, there is no discrimination between audio and video in the Lumiera concept +of a track. +A timeline must be assigned to viewer if playback viewing is desired. + +//Format Independent Timeline, one can put anything on the timeline. +//the busses constrain what kind of data is pulled out and in turn the +//builder creates a processing graph which does the necessary conversions and +//stuff. +// // Q: how to handle interaction, for example when some conversion can only be // done in a lossy way and some conversion node may or may not be inserted // (i mean gui wise)? @@ -246,19 +263,30 @@ stuff. Busses ~~~~~~ + The GUI provides a separate _bus view_, showing the master busses (subgroups) -in a manner similar to an audio mixing desk. Any bus is just a means to collect -and sum up the output of a specific kind of media (video, audio, number of channels), -produced by various processing elements and other busses. Conceptionally, these global -busses are considered a part of the timeline +in a manner similar to an audio mixing desk. Any bus is just a means of collecting +and and adding (aka overlaying) together the output of various kinds of media +(video, audio, number of channels), produced by various processing elements and +from other busses. These global busses can be concieved as being part of the +timeline. Asset View ~~~~~~~~~~ +We can conceive the Asset View as the timeline's book keeper: it manages the various +constituent in the timeline. Moreover, in addition to managing timeline +constituents, raw material, clips, bins (folders) are managed by the Asset +View, i.e., typical management operations including, deleting, adding, +naming, tagging, groupping into bins, etc. all occur here. + +Plugins are also managed in the Asset View. + + Manages all assets available in one project. * source media/footage/soundfiles - * prepared clips, known subprojects + * all available effects and transitions * internal artefacts like sequences and automation data sets @@ -285,18 +313,20 @@ Manages all assets available in one project. Dark Matter ----------- -[red]#to be written# -coarse overview about things the user does not see but have some contact -with, details later... +The material in this section provides a cursory view of features not required by +a typical user, but of more importance to people loking under the hod, i.e., +programers, etc. -Now lets take a look under the hood. Session storage ~~~~~~~~~~~~~~~ [red]#to be written# + + +//databank with logging, no data loss. // not generateable data // its the timeline mostly @@ -406,6 +436,8 @@ and only provide limited functionality without any plug-ins. Lumiera will not reinvent the wheel. One major goal is to provide considerable functionality via well-designed, external code supplied to Lumiera by plug-ins. +How are Plugins Implemented? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 451c9c582e40d38860d3e00a30dd1b4fba22100b Mon Sep 17 00:00:00 2001 From: Benny Lyons Date: Wed, 29 Aug 2012 16:54:05 +0100 Subject: [PATCH 411/496] Added some intro stuff --- doc/user/tutorials/contributing.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/user/tutorials/contributing.txt b/doc/user/tutorials/contributing.txt index df10c1a0d..6658706ae 100644 --- a/doc/user/tutorials/contributing.txt +++ b/doc/user/tutorials/contributing.txt @@ -1,6 +1,22 @@ Contributing to Lumiera ======================= +All files in the Lumiera project are managed by *Git*. Although *Git* was +primarily brought to life to manage source code, it plays a fundamental role in +Lumiera project communication and contributing to Lumiera. +If you'd like to contribute to Lumiera, you will have to acquire some +understanding of *Git* at some stage or other. Please note, this is not the only +way to contribute to Lumiera, you can always send direct contributions to the +mailing list. + + +a powerful tool +that not only + + + which is a distributed +source code management system. + The Lumiera project uses an infrastructure based on *Git*, the distibuted sourcecode management software. This deliberately places the barrier for contributing very low: No formal ``commit right'' is necessary; you can @@ -10,6 +26,8 @@ For starters, follow the http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html[basic instructions] for using Git, notably the following parts: +http://gitref.org/ + ------------------------------------------------------------ $ git config --global user.name "Your Name Comes Here" $ git config --global user.email you@yourdomain.example.com From 63cc4f49956dd721cec754e49c629c87e75a9252 Mon Sep 17 00:00:00 2001 From: Benny Lyons Date: Wed, 29 Aug 2012 16:55:00 +0100 Subject: [PATCH 412/496] More corrections. Text for I/O --- doc/user/intro/intro.txt | 95 ++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 32 deletions(-) diff --git a/doc/user/intro/intro.txt b/doc/user/intro/intro.txt index eaf61e796..6e77f2523 100755 --- a/doc/user/intro/intro.txt +++ b/doc/user/intro/intro.txt @@ -7,17 +7,17 @@ Lumiera (as seen) from Outer Space [abstract] ****************************************************************************** The Lumiera Community is in the process of making a non-linear video editing -and compositing FOSS application for Linux/Unix/Posix Operating Systems. The +and compositing FOSS application for Linux/Unix/Posix operating systems. The application is geared towards professional, high-quality work; but it is equally suitable for low-end users, due to its in-design scalability. Lumiera builds on common open source video, sound and GUI toolkits and libraries, being highly flexibile, configurable---user-control over a broad -spectrum of configurable parameters---and with smoth workflows that scale well -to larger more intricate projects as and more smaller projects. +spectrum of configurable parameters---and with smooth workflows that scale well +to larger more intricate projects and smaller projects. This document outlines the design from a more general perspective, providing potential users with sufficient insight into the tools and technology -behind Lumiera to get started working with Lumiera quickly. +behind Lumiera to start working with Lumiera quickly. ****************************************************************************** // all things starting with '//' are asciidoc comments and drafts/notes while @@ -28,7 +28,7 @@ behind Lumiera to get started working with Lumiera quickly. Lumiera is still under active development. Here we describe planned features without explicitly tagging them; some points have still to be worked out in detail. Although this document is heavily cross-linked, we try to start with a -broad overview and developing details towards the end. +broad overview, then develop details towards the end. Vision @@ -39,7 +39,7 @@ Vision Lumiera strives towards being 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 frame of mind -- doing work seriously, and to be subject to any -kind of wider goal, demand, or purpose. +kind of wider goal, requirement, or purpose. The concept of professionality in film editing can mean something of an artistic nature, a narrative or having a meaning to convey, a political message, or @@ -87,20 +87,21 @@ Fundamental Forces // 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 -be built up on that foundation. +these principles in mind will help you understand how more interesting things can +be built up from these fundamental principles. -Open ended combining of Building Blocks -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Open Ended Combination of Building Blocks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Lumiera is not so much defined in terms of _features_ -- rather it allows to -combine basic _building blocks._ These basic modules, entities or objects each -have a distinct _type_ explicitly limiting the connections. Within these -limits, any conceivable combination shall be supported without further hidden -limitations. +Lumiera is not so much defined in terms of _features_. It is more like a +workshop, where individual _basic building-blocks_ can be combined together in +interesting ways to form more complex structures. These basic modules, entities +or objects each have a distinct _type_ explicitly limiting the +connections. Within these limits, any conceivable combination is supported +without further hidden limitations. -Lumiera is neither a set of Lego bricks, nor is it the business application +Lumiera is neither a set of Lego bricks, nor is it a business application driven by finite usage stories. @@ -112,13 +113,17 @@ user may, if desired, directly manipulate through the GUI clips, individual effects, masks, and even the placements xref:placement[->] used to stitch the objects together, which is comparatively low-level. On the other hand, these abstractions shield the user from the actual technical details like format -conversions and the accessing of individual channels. +conversions and accessing individual channels. To complement this approach, Lumiera does _not_ rely on hard wired, global conventions -- rather we allow to build up project specific conventions and rules xref:rules[->] to fit the given requirements and preferred working -style. To help getting started, Lumiera will ship with a fairly conventional -project template and default configuration. +style. + +To ease the user in getting started with Lumiera, it will be supplied with a +rather conventional template and a default configuration will be supplied that +is relative conventional. + [[graphs]] @@ -174,15 +179,17 @@ Now its time to take a look at the prelimary Lumiera GUI: image:{l}/images/lumiera_gui_small.png[Current Lumiera GUI Screenshot] -The GUI is a plugin by itself and only one way to work Lumiera, it will become -possible to create special-purpose GUIs or control Lumiera in different ways, -like a headless rendernode xref:rendernode[->] or frameserver -xref:frameserver[->]. Completely script driven interfaces for automated -processing are also planned. -The GUI screenshot you see above is faily default as when you start Lumiera up -for the first time (the plan is to add a 2nd Viewer to the default -configuration). While we support a much more sophisticated screen concept +Lumiera consists of three major parts: the GUI, proc and backend. The initial +GUI Lumiera will ship with is not the only GUI possible, indeed Lumiera can work +satisfactorily without a GUI, for example, for specail purposes. +headless rendernode xref:rendernode[->] or frameserver +xref:frameserver[->]. Later scripts will be written for automated +processing. Special purpose GUIs are also envisaged later. + +The GUI screenshot you above is more or less the default you see when when you +start Lumiera up for the first time. A 2nd viewer is planned for later to be +added to the default. We support a much more sophisticated screen concept xref:screenconcept[->] to adapt to different workplaces and workflows. @@ -314,10 +321,11 @@ Dark Matter ----------- The material in this section provides a cursory view of features not required by -a typical user, but of more importance to people loking under the hod, i.e., +a typical user, but of more importance to people loking under the hud, i.e., programers, etc. - +Most of the material in this section is to be found in the proc layer and in the +backend. Session storage @@ -365,9 +373,32 @@ rendering... rules system [red]#to be written# -I/O Subsystem -~~~~~~~~~~~~~ -[red]#to be written# +Input-Output Subsystem +~~~~~~~~~~~~~~~~~~~~~~~ + +Lumiera will process large quantities of data and it is of critical imnportance +to perform this efficiently. The input and output subsystem are all processed +in the backend, in fact, this is one very important function provided by the +back end. + +The typical Lumiera user will have many clips, in various configurations located +at various places. All this data will have to be stored by the backend, +moreover all this data will have to be rapidly retrieved from storage and +provided to the user. The demands on memory are high: huge chunks of data, +that can be quickly stored and equally quickly fetched, even if stored over +longer periods of time. Moreover, due to the scalability requirement of Lumiera, +this process will have to perform adequately on lower-end hardware, and perform +efficiently on higher-end hardware. + +Lumiera will break down processes that need to be processed into smaller units +called _tasks_. Typically, there will be many hundreds of tasks waiting for +processing at any one time. These tasks are qued for processing and the order in +which this is performed is managed by the _scheduler_. This is all done in the +back end. + +Apart from memory, the backend will be responsible for accessing and saving +files. It will be of the utmost to do this efficiently. This will be carried out +in the backend using low-level mechanisms. // file handling // vault, work, cache From e039c9391c16f1de3cda82f0ce673dfb9441dbdf Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 7 Oct 2012 05:55:12 +0200 Subject: [PATCH 413/496] RFC: draft a new RfC to describe required website authoring support tools --- doc/devel/rfc/WebsiteSupportMarkup.txt | 231 ++++++++++++++++++ .../rfc_pending/WebsiteSupportMarkup.txt | 1 + 2 files changed, 232 insertions(+) create mode 100644 doc/devel/rfc/WebsiteSupportMarkup.txt create mode 120000 doc/devel/rfc_pending/WebsiteSupportMarkup.txt diff --git a/doc/devel/rfc/WebsiteSupportMarkup.txt b/doc/devel/rfc/WebsiteSupportMarkup.txt new file mode 100644 index 000000000..0f17c2253 --- /dev/null +++ b/doc/devel/rfc/WebsiteSupportMarkup.txt @@ -0,0 +1,231 @@ +WebsiteSupportMarkup +==================== + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _Sa 06 Okt 2012 16:47:44 CEST_ +*Proposed by* Ichthyostega +------------------------------------- + +******************************************************************************** +.Abstract +The Lumiera Website and Documentation uses a lightweight, plaintext based +Infrastructure built on top of Asciidoc and Git. To help with everyday +authoring and editorial tasks, a set of support facilities is proposed, +to be integrated seamlessly into the existing infrastructure. The front-end +to these tools is specific markup, allowing for *cross-linking*, *tag*-based +*link lists* and semi-automatic building of *glossary pages*. +******************************************************************************** + +Description +----------- +//description: add a detailed description: +Some time ago, the Lumiera core developer team _decided against_ using a Content +Management System for the Website and Documentation. While the rationale backing those +decisions still remains valid, CMS do exist for a reason. The everyday task of authoring +and editing a large body of text poses some specific challenges -- this RfC proposes a +set of rather simple support tools to help coping with these. + + +Use Cases +~~~~~~~~~ + +Authoring:: + For now, the _authoring of new content_ is mostly a responsibility of the core developers. + It needs to be done close to the actual coding activities, typically once a new facility + is roughly finished. It is crucial for this activity not to impose a _context switch_ + on the writer. It must be ``just writing down the obvious'' -- otherwise developers tend + to defer this activity ``for later''. This situation creates some unique problems... + + - during the write-up, the coder-author realises several external presuppositions, which, + during the act of coding, seemed to be ``obvious'' and ``self evident''. + - since expanding on all of those secondary topics is out of question (the author will rather + abandon the task of documentation altogether), the only solution is to allow for *cross linking*, + even if the created links are dangling for the moment. + - the author can't effort to locate other documents and determine URLs; he won't even be willing + to consult a markup syntax reference. Because, what he's about to write is essentially hard to + put into words and thus requires his full attention. + +Integrating Content:: + This task is often prompted by some kind of external cause: it might be someone asking for + explanations and while trying to respond, it was determined that ``this should be in the documentation''. + Thus, some existing markup is extracted from an external system and pasted into some location, + ``just for the moment''. Of course, this content will be forgotten and rest there for + years to come. To deal with this situation... + + - adapting the structural cross-references of the integrated markup needs to be an easy task. + - we need a way to hook the new content somehow into our existing categorisation + - the person integrating the content wont't be willing to visit a lot of other locations, + or to read a syntax reference for some kind of advanced markup. + +Editorial work:: + The _editor_ is reviewing existing content. He'll try to emulate an assumed user's point of view + to judge the adequacy of the presentation. This leads to rearranging and re-locating pages and whole + trees of pages, even splitting some of them, and adding introductory paragraphs and pages here and + there -- all without the ability to read and understand any of the reviewed material in-depth. + + - this work will be performed on the primary content using its ``natural'' representation: that is, + the editor will copy and move files in the file system and edit text. There is no room for using + any external system (unless such an external system is a fully integrated authoring environment). + - the editor needs an easy way for creating thematic groupings and overview pages. + - rearranging and splitting of pages must not break any meta-markup. + + +Tools for the task at hand +~~~~~~~~~~~~~~~~~~~~~~~~~~ +This RfC proposes a set of tools to cope with those problems. More specifically, this proposal details +a possible _front end_ for these tools, in a way which blends well with the existing Git / Asciidoc +website infrastructure. + +Cross-linking by textual ID +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Some kind of markup allowing the author _at his own discretion_ (read: not automagically) to create +a *cross-link* to another piece of information, identified just by a textual short-hand or ID (read: not +requiring any kind of URL). The markup must be very lightweight and should be very similar, if not identical +to the markup for setting an external link, e.g. `link:SomeTopic[see the explanation of Some Topic]` + +Variations and extensions + +- we might consider detecting _CamelCaseWords_. This has some Pros and Cons. If we do so, we need some + easy to use escape mechanism, and this CamelCase detection must not trigger within code examples and + other kinds of literal quotes. +- specifying the displayed link text should be optional +- we might consider adding some domain prefixes, e.g. +link:rfc:SomeTopic+ or +link:ticket:SomeTopic+ + or +link:code:SomeTopic+ + +Obviously, these cross-links needs to be resilient towards content reorganisation after-the-fact. +Effectively this mandates introducing some kind of indirection, since we can't effort to regenerate the +whole website's HTML pages after each localised change. A possible solution could be to make the rendered +cross link invoke a JavaScript function, which in turn consults some kind of index table. Another solution +would be to let the cross link point to a server sided script. + +Tag extractor and Index +^^^^^^^^^^^^^^^^^^^^^^^ +Define suitable ways for attaching *tags* to various kinds of content. The syntax will be tailored to the +kind of content in question, typically placing the tags in some kind of comment or specific header. For +larger documents, it would be desirable to attach tags in a more fine-grained manner, e.g. tag only one +paragraph or sub-section (but this is a nice-to-have, since it is anything but trivial to implement). + +Based on these tags, there should be a mechanism to integrate a list of links into some Asciidoc page. +Obviously this list needs to be dynamic, e.g. by using JavaScript or by including pre-fabricated HTML +fragments into an IFrame, since it is impossible to re-generate all overview pages whenever some new +resource gets tagged. + +Tags should optionally support a key-value structure, allowing for additional structures and +functionality to be built on top. E.g. the cross-linking facility detailed above could rely on +additional tags +id:SomeTopic+ for disambiguation. The values in such a key-value definition +should be an ordered list, allowing to use all, or alternatively for the first-one or last-one +to take precedence. + +Definition entries +^^^^^^^^^^^^^^^^^^ +Define a suitable format to promote an existing piece of information into a *definition*. While not +interfering with the presentation at the textual location of this definition, this mechanism should +allow to extract this definition and present it within a glossary of terms. It would be nice if +such a generated glossary could provide an automatic back-link to the location where the definition +was picked up initially. + +Of course, defining such a markup includes some reasoning about the suitable format of a glossary +and definition-of-terms. (Scope? Just a sentence? Just a paragraph? How to utilise existing headings?) + +Additionally, this term-definition facility could be integrated with the other facilities described above: + +- cross links could pick up the ID of term definitions +- tags could be used to create focussed definition lists. + + +Constraints +~~~~~~~~~~~ +Please consider that the user of these facilities, i.e. the author or documentation editor, is in no way +interested in using them. He will not recall any fancy syntax, and he won't stick to any rules for sure. +So, anything not outright obvious is out of question. + +- since we don't want fully dynamic page generation and we can't afford regenerating the whole website + for each small update, all of these facilities need some way to adapt after-the-fact. +- we need to build leeway into the system at various places. E.g. the cross-link facility needs a + strategy to generate and match the IDs and order possible matches in a sensible way. What initially + links to some doxygen comment might later on point to a glossary if applicable. +- since content will be re-arranged just by editing text, each markup needs to be close to the + related content text, to increase the chances of keeping it intact. + + + +Tasks +~~~~~ +// List what needs to be done to implement this Proposal: + * identify the actual use case(s) ([green]#✔ done#) + * define the required facilities ([green]#✔ done#) + * consider an implementation strategy [yellow-background]#WIP# + * define a suitable markup [yellow-background]#WIP# + * write the necessary scripts [red yellow-background]#TBD# + * test and integrate it into the website [red yellow-background]#TBD# + + +Discussion +~~~~~~~~~~ + +Pros +^^^^ + * in line with the general spirit of our Website infrastructure + * can be adopted gradually + + + +Cons +^^^^ + * the required scripts are non-trivial + * added complexity to the page template and website framework + * running multiple scripts on git push might become a performance bottleneck + + + +Alternatives +^^^^^^^^^^^^ +//alternatives: explain alternatives and tell why they are not viable: +. _status quo_: not doing anything to address this issues won't hurt us much right now, + + but increasingly works against building a well structured body of information + +. _using a mature CMS_: this is what most people do, so it can't be uttermost wrong. + + Yet still, the benefits of running a CMS need to outweigh the known problems, especially + + * lock-in, ``insular'' ecosystem, being tied to the evolution of a platform ``not invented here'' + * separation from the code tree, lack of seamless SCM integration + * the general penalties of using a database backed system + +. _writing our own integrated authoring framework_: obviously, this would be the perfect solution... + + Anyone(TM) to volunteer? + + +Rationale +--------- +//rationale: Give a concise summary why it should be done *this* way: +Since we still want to run our website based on very lightweight infrastructure, we need to +amend some of the shortcomings and provide a minimal set of support tools. The primary purpose +of these tools is to reduce the burden of providing structured access to the documentation content. +Using some special markup and a preprocessor/extractor script allows for gradual adoption and seamless +integration with the existing content. The proposed markup is deliberately kept simple and self-evident +for the user; the price to pay for that ease of use comes in terms of script complexity -- the latter +can be considered a one-time investment. + + + +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: + + + + +Comments +-------- +//comments: append below + + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/WebsiteSupportMarkup.txt b/doc/devel/rfc_pending/WebsiteSupportMarkup.txt new file mode 120000 index 000000000..b0601a272 --- /dev/null +++ b/doc/devel/rfc_pending/WebsiteSupportMarkup.txt @@ -0,0 +1 @@ +../rfc/WebsiteSupportMarkup.txt \ No newline at end of file From f710095f6ddb71adf5bc751d5c134e4ecd285e3c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 7 Oct 2012 05:55:35 +0200 Subject: [PATCH 414/496] adapt to newer Asciidoc syntax --- doc/devel/template/new_rfc.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/devel/template/new_rfc.sh b/doc/devel/template/new_rfc.sh index eccaecc8d..a78af057b 100644 --- a/doc/devel/template/new_rfc.sh +++ b/doc/devel/template/new_rfc.sh @@ -27,7 +27,8 @@ Tasks ~~~~~ // List what needs to be done to implement this Proposal: // * first step ([green]#✔ done#) -// * second step [,yellow]#WIP# +// * second step [yellow-background]#WIP# +// * third step [red yellow-background]#TBD# Discussion From 17968bf109dd783327113b0ea1b2542f89407af4 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 7 Oct 2012 08:06:30 +0200 Subject: [PATCH 415/496] create a submenu entry showing all RfC --- doc/devel/{rfc.txt => design_process.txt} | 2 ++ doc/devel/rfc/index.txt | 16 +++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) rename doc/devel/{rfc.txt => design_process.txt} (97%) diff --git a/doc/devel/rfc.txt b/doc/devel/design_process.txt similarity index 97% rename from doc/devel/rfc.txt rename to doc/devel/design_process.txt index d435feded..df982d575 100644 --- a/doc/devel/rfc.txt +++ b/doc/devel/design_process.txt @@ -1,12 +1,14 @@ Design Process ============== +//Menu: include rfc //Menu: include rfc_final //Menu: include rfc_pending //Menu: include rfc_parked //Menu: include rfc_dropped //Menu: put child 'rfc_dropped' after 'rfc_final' +//Menu: attach child 'rfc' How it Works diff --git a/doc/devel/rfc/index.txt b/doc/devel/rfc/index.txt index ecb1fd47c..7c0ca2c92 100644 --- a/doc/devel/rfc/index.txt +++ b/doc/devel/rfc/index.txt @@ -1,13 +1,19 @@ Design Proposal Pool ==================== -//Menu: discard +//Menu: label (all) //Menu: sort children -> read link:../rfc.html[more about Lumiera RfC] and the Design Process -This is the pool where all Design Proposals are stored. -According to the current state of each RfC (pending, final, dropped, parked), -there is also a symlink from the respective subdirectory to the actual document -stored here. +This is the pool of all RfC Design Proposals. + +Please use the navigation menu to access individual entries. +++++ +


+++++ +'''' +[small]#technically, all the RfC documents are stored within this pool directory; + +symlinks are used to link them into the subgroupings, according to the current state +(pending / accepted / dropped / parked)# + From e6fa99b3dd53ff4adc8f18826cc247252a8e62ac Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 7 Oct 2012 08:06:55 +0200 Subject: [PATCH 416/496] commenting on website related RfC --- doc/devel/rfc/WebsiteNavigation.txt | 45 +++++++++++--------------- doc/devel/rfc/WebsiteSupportMarkup.txt | 16 ++++++++- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/doc/devel/rfc/WebsiteNavigation.txt b/doc/devel/rfc/WebsiteNavigation.txt index 172b6543f..144c41a79 100644 --- a/doc/devel/rfc/WebsiteNavigation.txt +++ b/doc/devel/rfc/WebsiteNavigation.txt @@ -5,7 +5,7 @@ WebsiteNavigation [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Draft_ *Date* _Mi 08 Dez 2010 11:32:32 CET_ *Proposed by* Ichthyostega ------------------------------------- @@ -110,37 +110,22 @@ The following features would be handy, but can be considered optional Tasks ~~~~~ -// List what would need to be done to implement this Proposal in a few words: -// * item ... - - - -Discussion -~~~~~~~~~~ - -Pros -^^^^ -// add just 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: + * define markup for the various features ([green]#✔ done#) + * get a technical solution for the menu to work ([green]#✔ done#) + * write a script to traverse contents and generate the menu ([green]#✔ done#) + * test and integrate it into the website ([green]#✔ done#) Rationale --------- //rationale: Describe why it should be done *this* way: +Maintaining the navigation within a website beyond just some pages is a daunting task. +When frequent rearrangements of pages are to be expected, the only viable solution is +to automate this task. Moreover, there needs to be a secondary path to each page, +asside of the direct links which might or might not be provided. A automatically +generated navigation menu separate of the actual page content helps to address +these issues. @@ -156,5 +141,13 @@ Comments -------- //comments: append below +.State -> Draft +//add reason +A Menu generator script based on these principles is deployed and working +since a looooong time now. We still need to build the tagging facility though. +This is covered by another RfC. + +Ichthyostega:: 'So 07 Okt 2012 07:30:17 CEST' ~~ + //endof_comments: diff --git a/doc/devel/rfc/WebsiteSupportMarkup.txt b/doc/devel/rfc/WebsiteSupportMarkup.txt index 0f17c2253..2b4c6ad28 100644 --- a/doc/devel/rfc/WebsiteSupportMarkup.txt +++ b/doc/devel/rfc/WebsiteSupportMarkup.txt @@ -195,7 +195,7 @@ Alternatives * separation from the code tree, lack of seamless SCM integration * the general penalties of using a database backed system -. _writing our own integrated authoring framework_: obviously, this would be the perfect solution... + +. _writing our own integrated authoring framework_: obviously, this would be the perfect solution... Anyone(TM) to volunteer? @@ -224,6 +224,20 @@ Comments -------- //comments: append below +To put this RfC into perspective, I'd like to add that Benny and myself reworked several +of the introductory pages during our last meeting at FrOSCon 2012. We had some discussions +about what needs to be done in order to make the existing content more readily available. + +In the previous years, I've written a good deal of the existing content, so I might claim +some knowledge about the real world usage situation. This RfC is an attempt to share my +understanding about the inherent impediments of our setup and infrastructure. Especially, +when compared with a full-featured wiki or CMS, a list of the most lacking features +can be distilled; I am in no way against fancy stuff, but if we're about to dedicate +some effort to our infrastructure, it should be directed foremost towards fixing +those stuff which matters in practice. + +Ichthyostega:: 'So 07 Okt 2012 07:31:25 CEST' ~~ + //endof_comments: From 64e6d37bb85557d1c5ff4f901a57ea916dc33bd3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 8 Oct 2012 06:53:22 +0200 Subject: [PATCH 417/496] RFC architecture draft for metadata handling and serialisation --- doc/devel/rfc/SystematicMetadata.txt | 155 +++++++++++++++++++ doc/devel/rfc_pending/SystematicMetadata.txt | 1 + 2 files changed, 156 insertions(+) create mode 100644 doc/devel/rfc/SystematicMetadata.txt create mode 120000 doc/devel/rfc_pending/SystematicMetadata.txt diff --git a/doc/devel/rfc/SystematicMetadata.txt b/doc/devel/rfc/SystematicMetadata.txt new file mode 100644 index 000000000..ab407416c --- /dev/null +++ b/doc/devel/rfc/SystematicMetadata.txt @@ -0,0 +1,155 @@ +SystematicMetadata +================== + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _Mo 08 Okt 2012 04:39:16 CEST_ +*Proposed by* Ichthyostega +------------------------------------- + +******************************************************************************** +.Abstract +_give a short summary of this proposal_ +******************************************************************************** + +Lumiera is a metadata processing application: _Data_ is _media data_, and everything +else is _metadata_. Since our basic decision is to rely on existing libraries for +handling data, the ``metadata part'' is what _we are building anew._ + +This RfC describes a fundamental approach towards metadata handling. + + +Description +----------- +//description: add a detailed description: +Metadata is conceived as a huge uniform tree. This tree is conceptual -- it is +never represented as a whole. In the implemented system, we only ever see parts +of this virtual tree being cast into concrete data representations. These parts +are like islands of explicitly defined and typed structure, yet they never need +to span the whole virtual model, and thus there never needs to be an universal +model data structure definition. Data structure becomes implementation detail. + +Parts of the system talk to each other by _describing_ some subtree of metadata. +This description is _always pushed:_ the receiver implements an API allowing the +sender to navigate to some path-like scope and populate it with values, similar +to populating a filesystem. It is up to the receiver to assemble these information +into a suitable representation. Some receiver might invoke an object factory, while +another serialises data into an external textual or binary representation. + + +Abstract Metadata Model +~~~~~~~~~~~~~~~~~~~~~~~ +The conceptual model for metadata is close to what the JSON format uses: + +There are primitive values as +null+, string, number and boolean. Compund values +can be arrays or records, the latter being a sub-scope populated with key-value pairs. + +We might consider some extensions + + * having data values similar to BSON of MongoDB: integrals, floats, timestamps + * introducing two _special magic keys_ for records: `"type"` and `"id"` + + +Sources and Overlays +~~~~~~~~~~~~~~~~~~~~ +Metadata is delivered from _sources_, which can be _layered_. Similarly, on the +receiving side, there can be multiple _writeable layers_, with a routing strategy +to decide which writeable layer receives a given metadata element. This routing +is implemented within a pipeline connecting sender and receiver; if the default +routing strategy isn't sufficient, we can control the routing by introducing a +a meta-tree in some separate branch, this way making the metadata self-referential. + + +Some points to note +~~~~~~~~~~~~~~~~~~~ +- this concept doesn't say anything about the actual meaning of the metadata elements, + since that is always determined by the receiver, based on the current context. +- likewise, this concept doesn't state anything about the actual interactions, the + involved parts and how the interaction is initiated and configured; this is considered + an external topic, which needs to be solved within the applicable context (e.g. the + session has a specific protocol how to retrieve a persisted session snapshot) +- there is no separate _system configuration_ -- configuration appears just as a + local record of key-value pairs, which is interpreted according to the context. +- in a similar vein, this concept deliberately doesn't state anything regarding the + handling of _defaults_, since these are so highly dependent on the actual context. + + +Tasks +~~~~~ +// List what needs to be done to implement this Proposal: +// * first step ([green]#✔ done#) + * define the interaction API [yellow-background]#WIP# + * scrutinise this concept to find the pitfalls [yellow-background]#WIP# + * build a demonstration prototype, where the receiver fabricates an object [red yellow-background]#TBD# + + +Discussion +~~~~~~~~~~ + +Pros +^^^^ +- the basic implementation is strikingly simple, much simpler than building + a huge data structure or any kind of serialisation/deserialisation scheme +- parts can be combined in an open fashion, we don't need a final concept up-front +- even complex routing and overlaying strategies become manageable, since they can be + treated in isolation, local for a given scope and apart from the storage representation +- library implementations for textual representations can be integrated. + + + +Cons +^^^^ +- the theoretical view is challenging and rather uncommon +- a naive implementation holds the whole data tree in memory twice +- how the coherent ``islands'' are combined is only a matter of invocation order + and thus dangerously flexible + + + + +Alternatives +^^^^^^^^^^^^ +//alternatives: explain alternatives and tell why they are not viable: +The classical alternative is to define a common core data structure, which +needs to be finalised quickly. Isolated functional modules will then be written +to work on that common data set, which leads to a high degree of coupling. +Since this approach effectively doesn't scale well, what happens in practice is +that several independent storage and exchange systems start to exist in parallel, +e.g. system configuration, persisted object model, plug-in parameters, presentation +state. + + + +Rationale +--------- +//rationale: Give a concise summary why it should be done *this* way: +Basically common (meta) data could take on a lot of shapes between two extremes: + +- the _precise typed structure_, which also is a contract +- the _open dynamic structure_, which leaves the contract implicit + +The concept detailed in this RfC tries to reconcile those extremes by avoiding +a global concrete representation; + +this way the actual interaction -- with the necessity +of defining a contract -- is turned into a local problem. + + +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: + + + + +Comments +-------- +//comments: append below + + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/SystematicMetadata.txt b/doc/devel/rfc_pending/SystematicMetadata.txt new file mode 120000 index 000000000..e4e30fde0 --- /dev/null +++ b/doc/devel/rfc_pending/SystematicMetadata.txt @@ -0,0 +1 @@ +../rfc/SystematicMetadata.txt \ No newline at end of file From ee4e6905d2c2624114762c2f84e34acf5d064b84 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 12 Jan 2012 05:51:55 +0100 Subject: [PATCH 418/496] Documentation: integrate the time quantisation concept pages These pages from the TiddlyWiky feature a complete glossary of terms relevant for time and timecode handling, plus the architecural decisions related to this topic --- doc/design/architecture/TimeQuant.txt | 212 +++++++++++++++++++++++++ doc/design/architecture/TimeUsage.txt | 186 ++++++++++++++++++++++ doc/devel/images/uml.time-entities.png | Bin 0 -> 19835 bytes wiki/renderengine.html | 6 +- 4 files changed, 401 insertions(+), 3 deletions(-) create mode 100644 doc/design/architecture/TimeQuant.txt create mode 100644 doc/design/architecture/TimeUsage.txt create mode 100644 doc/devel/images/uml.time-entities.png diff --git a/doc/design/architecture/TimeQuant.txt b/doc/design/architecture/TimeQuant.txt new file mode 100644 index 000000000..8b55cd2aa --- /dev/null +++ b/doc/design/architecture/TimeQuant.txt @@ -0,0 +1,212 @@ +Time values and Time quantisation +================================= +:Author: Hermann Voßeler +:Email: +:Date: Spring 2011 + +//Menu: label Time Quantisation + +.Definitions +The term »Time« 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 + +time distance:: + a range of time, a *distance* on the time axis, measured with the same + arbitrary precision as time points. Distances can be determined by + _subtracting_ two time points, consequently they are _signed numbers._ + +offset:: + 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_ 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 link:TimeMutation[mutation messages], 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 commitment 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. + +wall clock and system time:: + The core property of any external real world time is that it is _running_ -- + we have to synchronise to an external time source. This implies the presence + of a _running synchronisation process,_ with the authority to adjust the time + base; + + + contrast this to the internal time, which is static and unconnected -- + +quantised time:: + The *act of quantisation* transforms a continuous property into a *discrete* + structure. Prominent examples can be found in the domain of micro physics and + with digital information processing. In a broader sense, any measurement or + _quantification_ also encompasses a quantisation. Regarding time and time + measurement, quantisation means alignment to a predefined *time grid*. + Quantisation necessarily is an _irreversible process_ -- possible + additional information is discarded. + + + Note that quantisation introduces a *time origin* and a *reference scale* + +frame count:: + within the context of film and media editing, the specification of a *frame number* + is an especially important instance of quantisation. + + + all the properties of quantisation apply indeed to this special case: it is a + time measurement or specification, where the values are aligned to a grid, and + there is a reference time point where the counting starts (origin) and a + reference scale (frames per second). Handling of quantised time values in + Lumiera is defined such as to ensure the presence of all those bits of + information. Without such precautions, operating with bare frame numbers leads + itself to all kinds of confusions, mismatches, quantisation errors and + unnecessary limitations of functionality. + +timecode:: + Quantisation also is the foundation of all kinds of formalised time specifications + actually even a frame count is some kind of (informal) timecode -- other timecodes + employ a standardised format. //Every// presentation of time values and every + persistent storage and exchange of such values is based on time codes. + Yet quantisation and time code aren't identical: a given quantised time value + typically can be cast into multiple timecode formats. + + + +Patterns for handling quantised time +------------------------------------ + +When it comes to actually handling quantised time values, several patterns are +conceivable for dealing with the quantisation operation and representing +quantised data. As guideline for judging these patterns, the general properties +of time quantisation, as detailed above, should be taken into account. +Quantising a time value means both _discarding information,_ while at the same +time _adding explicit information_ pertaining the assumptions of the context. + +.casual handling +this is rather an frequently encountered *anti pattern*. When reading such +code, the most striking observation is the sense of general unawareness of the +problem, which is then ``discovered'' on a per case base, which leads to +numerous repetitions of the same basic undertakings, but done with individual +treatment of each instance (not so much copy-n-paste). + +Typical code smells: + + * the rounding, modulo and subtract-base operations pertinent with scale + handling are seemingly inserted as bugfix + * local code path forks to circumvent or compensate for otherwise hard wired + calculations based on specific ways to invoke a function + * playing strikingly clever tricks or employing heuristics to "figure out" the + missing scale information from accessible context after the fact + * advertising support for some of the conceivable cases as special feature, or + adding it as plugin or extension module with limited scope + * linking parts of the necessary additional information to completely + unrelated other structures, thus causing code tangling and scattering + * result or behaviour of calculations depends on the way things are set up in + a seemingly contingent way, forcing users to stick to very specific procedures + and ordered steps. + +.static typing +an analysis of the cases to be expected establishes common patterns and some +base cases, which are then represented by distinct types with well established +conversions. This can be combined with generic programming for the common parts. +Close to the data input, a factory establishes these statically typed values. + +.Summary +**************************************************************************** +Lumiera uses (fixed) static typing for the plain _time values_, with a class +wrapping a simple 64bit integer as common denominator. The strict typing is +used to enforce a sane conversion path to quantised time values. For these +grid alinged values, and especially for the common time codes, the principle +of _delayed quantisation_ is preferred; the implementation relies on runtime +typing with type erasure, but provides conversion to statically tagged values +for the most important special formats + +image:{imgd}/uml.time-entities.png["Time and Time Quantisation in Lumiera"] +**************************************************************************** + +.tagged values +quantised values are explicitly created out of continuous values by a quantiser +entity. These quantised data values contain a copy of the original data, +adjusted to be exactly aligned with respect to the underlying time grid. In +addition, they carry a tag or ID to denote the respective scale, grid or +timecode system. This tag can be used later on to assess compatibility or to +recast values into another timecode system. + +.delayed quantisation +with this approach, the information loss is delayed as long as possible. +Quantised time values are rather treated as promise for quantisation, while the +actual time data remains unaltered. Additionally, they carry a tag, or even a +direct link to the responsible quantiser instance. Effectively, these are +specialised time values, instances of a sub-concept, able to stand-in for +general time values, but exposing additional accessors to get a quantised value. + + +discussion +~~~~~~~~~~ +For Lumiera, the static typing approach is of limited value -- it excels when +values belonging to different scales are actually treated differently. There are +such cases, but rather on the data handling level, e.g. sound samples are always +handled block wise. But regarding time values, the unifying aspect is more +important, which leads to prefering a dynamic (run time typed) approach, while +_erasing_ the special differences most of the time. Yet the dynamic and open +nature of the Lumiera high-level model favours the delayed quantisation pattern; +the same values may require different quantisation depending on the larger model +context an object is encountered in. This solution might be too general and +heavy weight at times though. Thus, for important special cases, the accessors +should return tagged values, preferably even with differing static type. Time +codes can be integrated this way, but most notably the *frame numbers* used +for addressing throughout the backend, can be implemented as such specifically +typed tagged values; the tag here denotes the quantiser and thus the underlying +grid -- it should be implemented as hash-ID for smooth integration with code +written in plain C. + +At the level of individual timecode formats, we're lacking a common denominator; +thus it is preferrable to work with different concrete timecode classes through +_generic programming._ This way, each timecode format can expose operations +specific only to the given format. Especially, different timecode formats expose +different _component fields,_ modelled by the generic *Digxel* concept. +There is a common baseclass `TCode` though, which can be used as marker +or for _type erasure._ + +-> more on link:TimeUsage.html[usage situations] +//// +// TODO integrate those cross links ++ +-> Timecode link:TimecodeFormat[format and quantisation] ++ +-> Quantiser link:QuantiserImpl[implementation details] +//// + diff --git a/doc/design/architecture/TimeUsage.txt b/doc/design/architecture/TimeUsage.txt new file mode 100644 index 000000000..10ef5cf16 --- /dev/null +++ b/doc/design/architecture/TimeUsage.txt @@ -0,0 +1,186 @@ +Time Usage Situations +===================== +:Author: Hermann Voßeler +:Email: +:Date: Spring 2011 + +//Menu: label Time Usage + +the following collection of usage situations helps to shape the details of the +time values and time quantisation design. + +-> see also link:TimeQuant.html[more about time quantisation] + +time position of an object:: + indeed the term ``time position'' encompasses two quite different questions + + + * a time or timing specification within the object + * determining the time point in reference to an existing scale + +time and length of an object:: + basically the same situation, + + but the length of a time span (duration) could be treated in two ways for quantisation + + + * having a precise specification and then quantise the start and endpoint + * quantise the start position and then establish an (independently quantised length) + +moving and resizing an object:: + this can in itself be done in two different ways, and each of them can be + applied in a quantised flavour, which sums up to 8 possible combinations, + considering that position and length are 2 degrees of freedom. + + + * a variable can be _changed_ by an offset + * a variable can be _defined_ to a new value + * another (hidden) degree of freedom lies in how to apply an quantised offset + to an unquantised value (and reversed), 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 link:PlayProcess[render or playback process] 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. + +time value arithmetic:: + Client code as well as the internal implementation of time handling needs to + do arithmetic operations with time values. Time values are additive and + totally ordered. Distance, as calculated by subtraction, can be made into a + metric. Another and quite different question is to what extent a quantised + variant of this arithmetics is required. + +relative placement:: + because of the divergence between quantised and unquantised values the + question arises, if placement relative to another object refers to the raw + position or the already quantised position. Basically all the variations + discussed for _time and length of an object_ also do apply here. + + +notable issues +-------------- + +*Direct quantisation of length is not possible*. This is due to the non-linear +nature of all but the most trivial time grids: Already such a simple addition +like a start offset destroys linearity, and this still the more is true within a +compound grid where the grid spacing changes at some point. Thus, the length has +to be re-established at the target position of an time interval after each +change involving quantisation. Regarding the _strategy_ to apply when +re-establishing the length, it seems more appropriate to treat the object as an +entity which is moved, which means to do quantisation in two steps, first the +position, then the endpoint (the second option in the description above). But it +seems advisable not to hard wire that strategy -- better put it into the +quantiser. + +We should note that the problems regarding quantised durations also carry over +to _offsets:_ it is difficult to *define the semantics of a quantised offset*. +Seemingly the only viable approach is to have a _intended offset,_ and then +to apply a re-quantisation to the target after applying the (raw) offset. + +*When to materialise a quantisation*. Because of the basic intention to retain +information, we prefer to delay the actual applicatio of the quantisation operation +to the values stored internally as much as possible. But not materialising +immediately at quantisation has the downside of possibly accumulating off-grid +values without that being evident. Most notably, if we apply the raw offsets +produced by GUI interactions, the object's positions and lengthes are bound to +accumulate spurious information never intended by the user. + +Thus, an especially important instance of that problem is *how to deal with +updates in a quantised environment*. If we handle quantisation stictly as a +view employed on output, we run into the problems with accumulating spurious +information. On the other hand, allowing for quantised changes inevitably pulls +in all the complexity of mixing quantised and non-quantised values. It would be +desirable somehow to move these distinctions out of the scope of this design and +offload them onto the client (code using these time classes). + +Another closely related problem is *when to allow mutations*, if at all (-> see +link:TimeMutation[more here...]). We can't completely do away with mutations, +simply because we don't have a pure functional language at our disposal. The +whole concept of _reference semantics_ doesn't play so well with immutable +objects. The Lumiera high-level (session) model certainly relies on objects +intended to be _manipulated._ Thus we need a re-settable length field in +`MObject` and we need a time variable for position calculations. Yet we +could make any _derived objects_ into immutable descriptor records, which +certainly helps with parallelism. + +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 +link:Segmentation[timeline segment] to address, or for retrieving parameter +automation. -> link:FrameDispatcher[see frame dispatching]. + +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. When following this line of thought, 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 +----------------------------------- + +The way time value and quantisation handling is designed in Lumiera creates a +typical usage path, which actually is a one-way route. We might start out with a +textual representation according to a specific *timecode* format. Assumed we +know the implicit underlying *time grid* (coordinate system, framerate), this +timecode string may be parsed. This brings us (back) to the very origin, which +is a raw `TimeValue` (*internal time* value). Now, this time value might be +manipulated, compared to other values, combined into a *time span* (time point +and duration -- the most basic notion of an _object_ to be manipulated in the +Session). Anyway, at some point these time values need to be related to some +*time scale* again, leading to *quantised* time values, which -- finally -- +can be cast into a timecode format for external representation again, thus +closing the circle. + + +substantial problems to be solved +--------------------------------- +* how to link:TimeGridAlignment[align multiple grids] +* how to integrate link:TimeMutation[modifications of quantised values]. ([green]#✔ solved#) +* how to isolate the Time/Quantisation part from the grid MetaAsset in the + session -> we use the link:Advice[Advice] system ([green]#✔ solved#) +* how to design the relation of Timecode, Timecode formatting and Quantisation + -> link:TimecodeFormat[more here] [yellow-background]#WIP# + + diff --git a/doc/devel/images/uml.time-entities.png b/doc/devel/images/uml.time-entities.png new file mode 100644 index 0000000000000000000000000000000000000000..91c7958764689a72584a4104d39229483dce8765 GIT binary patch literal 19835 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
-
+
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.
@@ -7249,14 +7249,14 @@ Based on this time::Mutation design, we provide a specialised element for dealin
 :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.
+:While the basic continuous time values don't imply any commitment 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.
 ;wall clock and system time
 :The core property of any external real world time is that it is //running// -- we have to synchronise to an external time source.
 :This implies the presence of a //running synchronisation process,// with the authority to adjust the time base;
 :contrast this to the internal time, which is static and unconnected -- 
 ;quantised time
 :The ''act of quantisation'' transforms a continuous property into a ''discrete'' structure. Prominent examples can be found in the domain of micro physics and with digital information processing. In a broader sense, any measurement or //quantification// also encompasses a quantisation. Regarding time and time measurement, quantisation means alignment to a predefined ''time grid''. Quantisation necessarily is an //irreversible process// -- possible additional information is discarded.
-:Note that quantisation introduces an ''time origin'' and a ''reference scale''
+:Note that quantisation introduces a ''time origin'' and a ''reference scale''
 ;frame count
 :within the context of film and media editing, the specification of a ''frame number'' is an especially important instance of quantisation.
 :all the properties of quantisation apply indeed to this special case: it is a time measurement or specification, where the values are aligned to a grid, and there is a reference time point where the counting starts (origin) and a reference scale (frames per second). Handling of quantised time values in Lumiera is defined such as to ensure the presence of all those bits of information. Without such precautions, operating with bare frame numbers leads itself to all kinds of confusions, mismatches, quantisation errors and unnecessary limitations of functionality.

From 687102feb3d2d067a0ff25c742ef4cb8d65c2e26 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 15 Jan 2012 03:41:41 +0100
Subject: [PATCH 419/496] planning next steps: how to transform DummyPlayer
 into a real player

---
 src/proc/play/play-process.hpp        |  9 ++++++---
 src/proc/play/render-configurator.cpp | 10 ++++++----
 wiki/renderengine.html                | 27 ++++++++++++++++-----------
 3 files changed, 28 insertions(+), 18 deletions(-)

diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp
index bf0733a72..afdedb3c9 100644
--- a/src/proc/play/play-process.hpp
+++ b/src/proc/play/play-process.hpp
@@ -98,9 +98,12 @@ namespace play {
       
     public:
       
-      /** building a Feed effectively involves the EngineService
-       *  to establish an actual rendering plan. Which is abstracted
-       *  here through the RenderConfigurator instance */
+      /** building a Feed effectively requires the definition
+       *  of a rendering plan through the EngineService.
+       * @param CalcStreams definition of the individual
+       *        calculation "continuations" for the engine.
+       *        These correspond to already running
+       *        render calculations. */
       Feed (engine::CalcStreams const&);
     };
   
diff --git a/src/proc/play/render-configurator.cpp b/src/proc/play/render-configurator.cpp
index ee0552dfe..ebf9eef45 100644
--- a/src/proc/play/render-configurator.cpp
+++ b/src/proc/play/render-configurator.cpp
@@ -79,7 +79,7 @@ namespace play {
 //    using std::tr1::placeholders::_1;
 //    using lib::transform;
     
-    class DefaultRenderProcessBuilder
+    class LumieraRenderProcessBuilder
       : public RenderConfigurator
       {
         
@@ -112,7 +112,7 @@ namespace play {
         
         
       public:
-        DefaultRenderProcessBuilder(POutputManager outputManager, Timings playbackSpeed)
+        LumieraRenderProcessBuilder (POutputManager outputManager, Timings playbackSpeed)
           : outputResolver_(outputManager)
           , playbackTimings_(playbackSpeed)
           , renderQuality_(EngineService::QoS_DEFAULT)
@@ -128,7 +128,7 @@ namespace play {
   
   
   /** @internal this builder function is used by the PlayService
-   * when it comes to creating a new PlayProcess. The generated RenderConfigurator
+   * when it comes to creating a new PlayProcess. The generated ConnectFunction
    * embodies the specific knowledge how to configure and setup the rendering or
    * playback at the EngineFacade, based on the general playback speed and
    * quality desirable for this playback process to be initiated.
@@ -143,7 +143,9 @@ namespace play {
   RenderConfigurator::ConnectFunction
   buildRenderConfiguration (POutputManager outputPossibilities, Timings playbackTimings)
   {
-    shared_ptr specialConfig (new DefaultRenderProcessBuilder (outputPossibilities, playbackTimings));
+    /////////////////////////////////////////////TODO this is the point to inject a Dummy implementation or anything bypassing the Lumiera engine!  
+      
+    shared_ptr specialConfig (new LumieraRenderProcessBuilder (outputPossibilities, playbackTimings));
     
     return bind (&RenderConfigurator::buildActiveFeed, specialConfig, _1 );
   }
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 487c00c8d..ab9ebafed 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1778,15 +1778,20 @@ The Method of pushing frames to the Viewer widget is changed fundamentally durin
 
 
-
+
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.
+
+!Evolving the real implementation {{red{TODO 1/2012}}}
+As it stands, the ~DisplayFacade is a placeholder for parts of the real &rarr; OutputManagement, to be implemented in conjunction with the [[player subsystem|Player]] and the render engine. As of 1/2012, the intention is to turn the DisplayService into an OutputSlot instance -- following this line of thought, the ~DisplayFacade might become some kind of OutputManager, possibly to be [[implemented within a generic Viewer element|ViewerPlayConnection]]
 
-
+
A service within the GUI to manage output of frames generated by the lower layers of the application.
 *providing the actual implementation of the DisplayFacade
 *creating and maintaining of [[displayer slots|DisplayerSlot]], connected to viewer widgets or similar
+
+{{red{TODO 1/2012}}} to be generalised into a service exposing an OutputSlot for display.
 
@@ -4517,10 +4522,10 @@ Yet it addresses some central concerns: 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.
-
-
__Joelholdsworth__ and __Ichthyo__ created this player mockup in 1/2009 to find out about the implementation details regarding integration and colaboration between the layers. There is no working render engine yet, thus we use a ~DummyImageGenerator for creating faked yuv frames to display. Within the GUI, there is a ~PlaybackController hooked up with the transport controls on the timeline pane.
+
+
__Joelholdsworth__ and __Ichthyo__ created this player mockup in 1/2009 to find out about the implementation details regarding integration and colaboration between the layers. There is no working render engine yet, thus we use a ~DummyImageGenerator for creating faked yuv frames to display. Within the GUI, there is a ~PlaybackController hooked up with the transport controls on the timeline pane. 
 # first everything was contained within ~PlaybackController, which spawns a thread for periodically creating those dummy frames
-# then, a ~PlayerService was factored out, now implemented within Proc-Layer (probably to be relocated into the backend for the final version). A new LayerSeparationInterface called ''~DummyPlayer'' was created and set up as a [[Subsystem]] within main().
+# then, a ~PlayerService was factored out, now implemented within ~Proc-Layer (later to delegate to the emerging real render engine implementation).<br/>A new LayerSeparationInterface called ''~DummyPlayer'' was created and set up as a [[Subsystem]] within main().
 # the next step was to support multiple playback processes going on in parallel. Now, the ~PlaybackController holds an smart-handle to the ~PlayProcess currently generating output for this viewer, and invokes the transport control functions and the pull frame call on this handle.
 # then, also the tick generation (and thus the handling of the thread which pulls the frames) was factored out and pushed down into the mentioned ~PlayProcess. For this to work, the ~PlaybackController now makes a display slot available on the public GUI DisplayFacade interface, so the ~PlayProcessImpl can push up the frames for display within the GUI
 [img[Overview to the dummy player operation|draw/PlayerArch1.png]]
@@ -4533,7 +4538,7 @@ When starting playback, the display slot handle created by these preparations is
 * this uses the provided slot handle to actually //allocate// the display slot via the Display facade. Here, //allocating// means registering and preparing it for output by //one single// ~PlayProcess. For the latter, this allocation yields an actually opened display handle.
 * moreover, the ~PlayProcessImpl aquires an TickService instance, which is still trotteling (not calling the periodic callback)
 * probably, a real player at this point would initiate a rendering process, so he can fetch the actual output frames periodically.
-* on the "upper" side of the ~DummyPlayer facade, an lib::Handle object is created to track and manage this ~PlayProcesImpl instance
+* on the "upper" side of the ~DummyPlayer facade, a lib::Handle object is created to track and manage this ~PlayProcesImpl instance
 The mentioned handle is returned to the ~PlaybackController within the GUI, which uses this handle for all further interactions with the Player. The handle is ref counting and has value semantics, so it can be stored away, passed as parameter and so on. All such handles corresponding to one ~PlayProcess form a family; when the last goes out of scope, the ~PlayProcess terminates and deallocates any resources. Conceptually, this corresponds to pushing the "stop" button. Handles can be deliberately disconnected by calling {{{handle.close()}}} &mdash; this has the same effect as deleting a handle (when all are closed or deleted the process ends).
 
 All the other play control operations are simply forwarded via the handle and the ~PlayProcessImpl. For example, "pause" corresponds to setting the tick frequency to 0 (thus temporarily discontinuing the tick callbacks). When allocating the display slot in the course of creating the ~PlayProcessImpl, the latter only accesses the Display facade. It can't access the display or viewer directly, because the GUI lives within an plugin; lower layers aren't allowed to call GUI implementation functions directly. Thus, within the Display facade a functor (proxy) is created to represent the output sink. This (proxy) Displayer can be used within the implementation of the perodic callback function. As usual, the implementation of the (proxy) Displayer can be inlined and doesn't create runtime overhead. Thus, each frame output call has to pass though two indirections: the function pointer in the Display facade interface, and the Glib::Dispatcher.
@@ -7743,8 +7748,8 @@ In case it's not already clear: we don't have "the" Render Engine, rat
 The &raquo;current setup&laquo; of the objects in the session is sort of a global state. Same holds true for the Controller, as the Engine can be at playback, it can run a background render or scrub single frames. But the whole complicated subsystem of the Builder and one given Render Engine configuration can be made ''stateless''. As a benefit of this we can run this subsystems multi-threaded without the need of any precautions (locking, synchronizing). Because all state information is just passed in as function parameters and lives in local variables on the stack, or is contained in the StateProxy which represents the given render //process// and is passed down as function parameter as well. (note: I use the term "stateless" in the usual, slightly relaxed manner; of course there are some configuration values contained in instance variables of the objects carrying out the calculations, but this values are considered to be constant over the course of the object usage).
 
-
-
within Lumiera's ~Proc-Layer, on the conceptual level there are two kinds of connections: data streams and control connections. The wiring details on how these connections are defined and controlled, how they are to be detected by the builder and finally implemented by links in the render engine.
+
+
within Lumiera's ~Proc-Layer, on the conceptual level there are two kinds of connections: data streams and control connections. The wiring deals with how to define and control these connections, how they are to be detected by the builder and finally implemented by links in the render engine.
 &rarr; see OutputManagement
 &rarr; see OutputDesignation
 &rarr; see OperationPoint
@@ -7752,14 +7757,14 @@ The &raquo;current setup&laquo; of the objects in the session is sort of
 
 
 !Stream connections
-A stream connection should result in media data traveling from a source to a sink. Here we have to distinguish between the high-level view and the situation in the render engine. At the session level and thus for the user, a //stream// is the elementary unit of "media content" flowing through the system. It can be described unambigously by having an uniform StreamType &mdash; this doesn't exclude the stream from being inherently structured, like containing several channels. The HighLevelModel can be interpreted as creating a system of stream connections, which can be categorised as yielding two kinds of connections:
+A stream connection should result in media data traveling from a source to a sink. Here we have to distinguish between the high-level view and the situation in the render engine. At the session level and thus for the user, a //stream// is the elementary unit of "media content" flowing through the system. It can be described unambigously by having an uniform StreamType &mdash; this doesn't exclude the stream from being inherently structured, like containing several channels. The HighLevelModel can be interpreted as //creating a system of stream connections,// which, more specifically can be categorised into two kinds or types of connections -- the rigit and the flexible parts:
 * the [[Pipes|Pipe]] are rather rigid ''processing chains'', limited to using one specific StreamType. Pipes are build by attaching processing descriptors (Effect ~MObjects), where the order of attachment is given by the placement.
 * there are flexible interconnections or ''routing links'', including the ability to sum or overlay media streams, and the possibility of stream type conversions. They are controlled by the OutputDesignation ("wiring plug"), to be queried from the placement at the source side of the interconnection (i.e. at the exit point of a pipe)
-Please note, the high-level model comprises a blue print for constructing the render engine. There is no data "flowing" through this model, thus any "wiring" may be considered conceptual. Any wiring specifications here just express the intention of getting things connected in a given way. Like e.g. a clip may find out (through query of his placement) that he's intended to produce output for some destination / bus / subgroup called "XYZ"
+Please note, the high-level model comprises a blue print for constructing the render engine. There is no real data "flowing" through this model, thus any "wiring" may be considered conceptual. Within this context, any wiring specifications just express //the intention of getting things connected in a specific way.// Consider the example of a clip, which might find out (through query of his placement) that he's intended to produce output for some destination or  bus or subgroup called "XYZ"
 
 The builder to the contrary considers matters locally. He's confined to a given set of objects handed in for processing, and during that processing will collect all [[plugs|WiringPlug]] and [[claims|WiringClaim]] encountered. While the former denote the desired //destination//&nbsp; of data emerging from a pipe, the wiring claim expresses the fact that a given object //claims to be some output destination.// Typically, each [[global pipe or bus|GlobalPipe]] raises such a claim. Both plugs and claims are processed on a "just in case they exist" base: When encountering a plug and //resolving//&nbsp; the corresponding claim, the builder drops off a WiringRequest accordingly. Note the additional resolution, which might be necessary due to virtual media and nested sequences (read: the output designation might need to be translated into another designation, using the media/channel mapping created by the virtual entity &rarr; see [[mapping of output designations|OutputMapping]])
 
-Processing the wiring request drives the actual connection step. It is conducted by the OperationPoint, provided by and executing within a BuilderMould and controlled by a [[processing pattern|ProcPatt]]. This rather flexible setup allows for wiring summation lines, include faders, scale and offset changes and various overlay modes. Thus the specific form of the connection wired in here depends on all the local circumstances visible at that point of operation:
+The Processing os such a wiring request drives the actual connection step. It is conducted by the OperationPoint, provided by and executing within a BuilderMould and controlled by a [[processing pattern|ProcPatt]]. This rather flexible setup allows for wiring summation lines, include faders, scale and offset changes and various overlay modes. Thus the specific form of the connection wired in here depends on all the local circumstances visible at that point of operation:
 * the mould is picked from a small number of alternatives, based on the general wiring situation.
 * the processing pattern is queried to fit the mould, the stream type and additional placement specifications ({{red{TODO 11/10: work out details}}})
 * the stream type system itself contributes in determining possible connections and conversions, introducing further processing patterns

From 24911dc9908ba0cd872b3eba57eebddaec8ef5f5 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 16 Jan 2012 04:50:21 +0100
Subject: [PATCH 420/496] solution to integrate an Engine Mock implementation

---
 src/proc/engine/engine-service-mock.cpp |  91 ++++++++++++++++++
 src/proc/engine/engine-service-mock.hpp | 119 ++++++++++++++++++++++++
 src/proc/play/render-configurator.cpp   |  52 +++++++++--
 src/proc/play/render-configurator.hpp   |   8 +-
 src/proc/play/timings.hpp               |  16 +++-
 5 files changed, 274 insertions(+), 12 deletions(-)
 create mode 100644 src/proc/engine/engine-service-mock.cpp
 create mode 100644 src/proc/engine/engine-service-mock.hpp

diff --git a/src/proc/engine/engine-service-mock.cpp b/src/proc/engine/engine-service-mock.cpp
new file mode 100644
index 000000000..b47b1516b
--- /dev/null
+++ b/src/proc/engine/engine-service-mock.cpp
@@ -0,0 +1,91 @@
+/*
+  EngineServiceMock  -  dummy render engine implementation for test/development
+
+  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.
+
+* *****************************************************/
+
+
+#include "proc/engine/engine-service-mock.hpp"
+
+//#include 
+//#include 
+//#include 
+//#include 
+
+
+
+namespace proc  {
+namespace engine{
+  
+//    using std::string;
+//    using lumiera::Subsys;
+//    using std::auto_ptr;
+//    using boost::scoped_ptr;
+//    using std::tr1::bind;
+  
+  
+  namespace { // hidden local details of the service implementation....
+    
+  } // (End) hidden service impl details
+  
+  
+  
+  /** storage for the EngineService interface object */
+  lib::Singleton EngineServiceMock::instance;
+  
+  
+  
+  /** */  
+  EngineServiceMock::EngineServiceMock()
+    { }
+  
+  
+  
+  /** core operation: activate the Lumiera Render Engine.
+   * Invoking this service effectively hooks up each channel
+   * of the given model exit point to deliver into the corresponding
+   * output sink on the given OutputConnection (which is assumed
+   * to be already allocated for active use by this connection).
+   * The generated calculation streams represent actively ongoing
+   * calculations within the engine, started right away, according
+   * to the given timing constraints and service quality.
+   */
+  CalcStreams
+  EngineServiceMock::calculate(ModelPort mPort,
+                               Timings nominalTimings,
+                               OutputConnection& output,
+                               Quality serviceQuality)
+  {
+    UNIMPLEMENTED ("MOCK IMPLEMENTATION: build a list of standard calculation streams");
+  }
+  
+  
+  
+  /** */
+  CalcStreams
+  EngineServiceMock::calculateBackground(ModelPort mPort,
+                                         Timings nominalTimings,
+                                         Quality serviceQuality)
+  {
+    UNIMPLEMENTED ("MOCK IMPLEMENTATION: build calculation streams for background rendering");
+  }
+  
+  
+  
+}} // namespace proc::engine
diff --git a/src/proc/engine/engine-service-mock.hpp b/src/proc/engine/engine-service-mock.hpp
new file mode 100644
index 000000000..2d865d223
--- /dev/null
+++ b/src/proc/engine/engine-service-mock.hpp
@@ -0,0 +1,119 @@
+/*
+  ENGINE-SERVICE-MOCK.hpp  -  dummy render engine implementation for test/development
+
+  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.
+
+*/
+
+/** @file engine-service-mock.hpp
+ ** Placeholder implementation of the render engine for test and diagnostics.
+ ** This implementation can be used as a drop-in replacement of the real engine.
+ ** Of course, it is lacking most of the functionality; it is just usable to detect
+ ** and verify the actual engine setup and invocation that \em would happen.
+ ** 
+ ** @todo 1/2012 until the real render engine is usable, this mock implementation
+ **       will stand-in, allowing us to develop the other parts of the play/render subsystem.
+ ** 
+ ** @see proc::engine::EngineService "the real thing"
+ ** @see render-configurator.cpp (activating the mock or the real engine)
+ ** @see DummyPlayConnection       
+ ** @see EngineInterface_test
+ ** @see CalcStream_test
+ */
+
+
+#ifndef PROC_ENGINE_ENGINE_SERVICE_MOCK_H
+#define PROC_ENGINE_ENGINE_SERVICE_MOCK_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 "proc/engine/engine-service.hpp"
+#include "lib/singleton.hpp"
+//
+#include 
+//#include 
+//#include 
+
+
+namespace proc  {
+namespace engine{
+
+//    using std::string;
+//    using lumiera::Subsys;
+//    using lumiera::Display;
+//    using lumiera::DummyPlayer;
+  using mobject::ModelPort;
+  using proc::play::Timings;
+  
+  typedef EngineService::Quality Quality;
+  
+  
+  
+  
+  
+  /******************************************************
+   * 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 EngineServiceMock
+    : boost::noncopyable
+    {
+      
+      
+    public:
+      /** access point to the Engine Interface Mock implementation. */
+      static lib::Singleton instance;
+      
+      
+      EngineServiceMock();
+     ~EngineServiceMock() { }
+      
+      CalcStreams
+      calculate(ModelPort mPort,
+                Timings nominalTimings,
+                OutputConnection& output,
+                Quality serviceQuality = EngineService::QoS_DEFAULT);
+      
+      CalcStreams
+      calculateBackground(ModelPort mPort,
+                          Timings nominalTimings,
+                          Quality serviceQuality = EngineService::QoS_BACKGROUND);
+      
+      
+    protected:
+    };
+  
+  
+  
+  
+}} // namespace proc::engine
+#endif
diff --git a/src/proc/play/render-configurator.cpp b/src/proc/play/render-configurator.cpp
index ebf9eef45..f5e1c4d4c 100644
--- a/src/proc/play/render-configurator.cpp
+++ b/src/proc/play/render-configurator.cpp
@@ -25,6 +25,7 @@
 #include "proc/play/render-configurator.hpp"
 #include "proc/play/output-manager.hpp"
 #include "proc/engine/engine-service.hpp"
+#include "proc/engine/engine-service-mock.hpp"
 //#include "lib/itertools.hpp"
 
 //#include 
@@ -47,6 +48,7 @@ namespace play {
   using std::tr1::bind;
   using std::tr1::placeholders::_1;
   using engine::EngineService;
+  using engine::EngineServiceMock;
   
   typedef EngineService::QoS_Definition RenderQuality;
   
@@ -107,7 +109,14 @@ namespace play {
             Timings nominalTimings = activeOutputConnection.getTimingConstraints()
                                                            .constrainedBy(playbackTimings_);
             
-            return EngineService::instance().calculate (port, nominalTimings, activeOutputConnection, renderQuality_);
+            return activateEngine (port, nominalTimings, activeOutputConnection, renderQuality_);
+          }
+        
+      protected:
+        virtual engine::CalcStreams
+        activateEngine (ModelPort port, Timings timings, OutputSlot::Allocation& activeOutputConnection, RenderQuality quality)
+          {
+            return EngineService::instance().calculate (port, timings, activeOutputConnection, quality);
           }
         
         
@@ -119,6 +128,34 @@ namespace play {
           { }
         
       };
+    
+    class MockRenderProcessBuilder
+      : public LumieraRenderProcessBuilder
+      {
+        engine::CalcStreams
+        activateEngine (ModelPort port, Timings timings, OutputSlot::Allocation& activeOutputConnection,RenderQuality quality)
+          {
+            return EngineServiceMock::instance().calculate (port, timings, activeOutputConnection, quality);
+          }
+        
+      public:
+        MockRenderProcessBuilder (POutputManager outputManager, Timings playbackSpeed)
+          : LumieraRenderProcessBuilder(outputManager,playbackSpeed)
+          { }
+      };
+    
+    
+    
+    /** @internal decision point about how to configure the rendering */
+    inline RenderConfigurator*
+    how_to_render (POutputManager outputPossibilities, Timings playTimings)
+    {
+      if (playTimings.isMockEngineRun())
+        return new MockRenderProcessBuilder (outputPossibilities, playTimings);
+            
+      else
+        return new LumieraRenderProcessBuilder (outputPossibilities, playTimings);
+    }
   
     
   } // (End) hidden service impl details
@@ -129,9 +166,10 @@ namespace play {
   
   /** @internal this builder function is used by the PlayService
    * when it comes to creating a new PlayProcess. The generated ConnectFunction
-   * embodies the specific knowledge how to configure and setup the rendering or
-   * playback at the EngineFacade, based on the general playback speed and
-   * quality desirable for this playback process to be initiated.
+   * treats a single ModelPort to produce a suitable rendering setup, pulling data
+   * from this port; it thus embodies the specific knowledge how to configure and
+   * setup the rendering or playback at the EngineFacade, based on the playback
+   * speed and quality desirable for this playback process to be initiated.
    * @remarks building a special subclass here and managing this instance
    *          by smart-ptr. Then wrapping all of this up into a functor,
    *          which can thus be passed on by value. This functor will
@@ -141,11 +179,9 @@ namespace play {
    *          OutputManager
    */
   RenderConfigurator::ConnectFunction
-  buildRenderConfiguration (POutputManager outputPossibilities, Timings playbackTimings)
+  buildRenderConfiguration (POutputManager outputPossibilities, Timings playTimings)
   {
-    /////////////////////////////////////////////TODO this is the point to inject a Dummy implementation or anything bypassing the Lumiera engine!  
-      
-    shared_ptr specialConfig (new LumieraRenderProcessBuilder (outputPossibilities, playbackTimings));
+    shared_ptr specialConfig (how_to_render (outputPossibilities,playTimings));
     
     return bind (&RenderConfigurator::buildActiveFeed, specialConfig, _1 );
   }
diff --git a/src/proc/play/render-configurator.hpp b/src/proc/play/render-configurator.hpp
index 44b44f171..4ba8cc665 100644
--- a/src/proc/play/render-configurator.hpp
+++ b/src/proc/play/render-configurator.hpp
@@ -109,7 +109,13 @@ namespace play {
   
   
   /** Factory function to build a RenderConfigurator
-   *  specifically tailored for a given PlayProcess.
+   *  specifically tailored for a PlayProcess, about to be started.
+   * @param outputPossibilities an OutputManager instance describing
+   *        the situation where output is about to be generated (e.g.
+   *        a viewer in the GUI or a file to be rendered)
+   * @param playbackTimings characterisation of the kind of play/render.
+   *        Besides the required delivery interval, this might also define
+   *        quality-of-service expectations.
    * @return the public access point to an RenderConfigurator,
    *         wrapped as generic function object 
    */
diff --git a/src/proc/play/timings.hpp b/src/proc/play/timings.hpp
index 5c6963f17..649007979 100644
--- a/src/proc/play/timings.hpp
+++ b/src/proc/play/timings.hpp
@@ -70,8 +70,9 @@ namespace play {
   
   
   
-  /********************************************************************
-   * Data-Record: Generic frame timing specification.
+  /*****************************************************************************
+   * Generic frame timing specification. Defines the expected delivery interval,
+   * optionally also the expected quality-of-service
    * 
    * @note copyable value class
    * 
@@ -80,7 +81,16 @@ namespace play {
   class Timings
     {
     public:
-      //////////////TODO accessor functions here
+      /** push aside the real Lumiera engine and use a test mock.
+       * @todo 1/2012 hard wired -- until the engine becomes usable
+       */
+      bool
+      isMockEngineRun ()
+        {
+          return true;
+        }
+      
+      //////////////TODO further accessor functions here
       
       Timings
       constrainedBy (Timings additionalConditions)

From a2bcedc31e84b062abc93d1c1924bf0e66f67b18 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 20 Jan 2012 00:05:12 +0100
Subject: [PATCH 421/496] some unrelated unit test tweaks

---
 .../session/defs-manager-impl-test.cpp        |  2 +-
 .../proc/mobject/session/scope-path-test.cpp  |  2 +-
 tests/lib/singleton-testmock-test.cpp         | 64 ++++++++-----------
 3 files changed, 30 insertions(+), 38 deletions(-)

diff --git a/tests/components/proc/mobject/session/defs-manager-impl-test.cpp b/tests/components/proc/mobject/session/defs-manager-impl-test.cpp
index 9afa64b21..9df802a31 100644
--- a/tests/components/proc/mobject/session/defs-manager-impl-test.cpp
+++ b/tests/components/proc/mobject/session/defs-manager-impl-test.cpp
@@ -155,7 +155,7 @@ lumiera::query::setFakeBypass("stream("+sID+")"); //////////////////////////////
       forget (string pID)
         {
           PPipe pipe = Pipe::query ("pipe("+pID+")");
-          REQUIRE (find (pipe->getPipeID()), "need an object registered as default");
+          REQUIRE (find (pipe->getPipeID()), "test assumes pre-registered default pipe");
           long cnt = pipe.use_count();
           
           // now de-register the pipe as "default Pipe"
diff --git a/tests/components/proc/mobject/session/scope-path-test.cpp b/tests/components/proc/mobject/session/scope-path-test.cpp
index 01c9b95c6..e54fed27a 100644
--- a/tests/components/proc/mobject/session/scope-path-test.cpp
+++ b/tests/components/proc/mobject/session/scope-path-test.cpp
@@ -109,7 +109,7 @@ namespace test    {
           VERIFY_ERROR (NOT_IN_SESSION, Scope invalid (notRelated2anything) );
           
           Scope const& scopeOfEvil = fabricate_invalidScope();
-          REQUIRE (!scopeOfEvil.isValid());
+          CHECK (!scopeOfEvil.isValid());
           
           VERIFY_ERROR (INVALID_SCOPE, ScopePath outsideCurrentModel (scopeOfEvil) );
           
diff --git a/tests/lib/singleton-testmock-test.cpp b/tests/lib/singleton-testmock-test.cpp
index 27a0ce086..df8cfdfd4 100644
--- a/tests/lib/singleton-testmock-test.cpp
+++ b/tests/lib/singleton-testmock-test.cpp
@@ -112,7 +112,7 @@ namespace test{
   class SingletonTestMock_test : public Test
     {
       
-      Singleton instance;
+      Singleton sing;
       
       void
       run (Arg arg)
@@ -140,30 +140,26 @@ namespace test{
       void
       injectBoth ()
         {
-          TestSingletonO* sing = &instance();
-          sing->doIt();
-          sing->doIt();
-          CHECK (sing->getCnt() == 2);
+          sing().doIt();
+          sing().doIt();
+          CHECK (sing().getCnt() == 2);
           
-          instance.injectSubclass (new Mock_1);
-          sing = &instance();
-          sing->doIt();
-          sing->doIt();
-          sing->doIt();
-          sing->doIt();
-          sing->doIt();
-          CHECK (sing->getCnt() == 5);
+          sing.injectSubclass (new Mock_1);
+          sing().doIt();
+          sing().doIt();
+          sing().doIt();
+          sing().doIt();
+          sing().doIt();
+          CHECK (sing().getCnt() == 5);
           
-          instance.injectSubclass (new Mock_2);
-          sing = &instance();
-          sing->doIt();
-          CHECK (sing->getCnt() == 1);
+          sing.injectSubclass (new Mock_2);
+          sing().doIt();
+          CHECK (sing().getCnt() == 1);
           
-          instance.injectSubclass (0); // un-shadowing original instance
-          sing = &instance();
-          CHECK (sing->getCnt() == 2);
-          sing->doIt();
-          CHECK (sing->getCnt() == 3);
+          sing.injectSubclass (0);  // un-shadowing original instance
+          CHECK (sing().getCnt() == 2);
+          sing().doIt();
+          CHECK (sing().getCnt() == 3);
         }
       
       
@@ -171,8 +167,7 @@ namespace test{
       void
       noMock ()
         {
-          TestSingletonO& sing = instance();
-          sing.doIt();
+          sing().doIt();
         }
       
       
@@ -181,9 +176,8 @@ namespace test{
       void
       onlyMock ()
         {
-          instance.injectSubclass (new Mock_1);
-          TestSingletonO& sing = instance();
-          sing.doIt();
+          sing.injectSubclass (new Mock_1);
+          sing().doIt();
         }
       
       
@@ -194,16 +188,14 @@ namespace test{
       void
       firstMock ()
         {
-          instance.injectSubclass (new Mock_1);
-          TestSingletonO* sing = &instance();
-          sing->doIt();
-          sing->doIt();
-          CHECK (sing->getCnt() == 2);
+          sing.injectSubclass (new Mock_1);
+          sing().doIt();
+          sing().doIt();
+          CHECK (sing().getCnt() == 2);
           
-          instance.injectSubclass (0);
-          sing = &instance();
-          sing->doIt();
-          CHECK (sing->getCnt() == 1);
+          sing.injectSubclass (0);
+          sing().doIt();
+          CHECK (sing().getCnt() == 1);
         }
     };
   

From ec659b714130d5729082f318a259fb0e4fa8fb2b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 21 Jan 2012 02:30:01 +0100
Subject: [PATCH 422/496] a better way to inject the Render-Engine Mock

instead of (ab)using the Timings spect for a
runtime switch, better use the existing
MockInjector facility and thus turn the
mock engine mode into a global switch
---
 src/lib/singleton-preconfigure.hpp      | 14 ++++++++++++-
 src/lib/singleton.hpp                   |  4 ++--
 src/proc/engine/engine-service-mock.cpp |  2 --
 src/proc/engine/engine-service-mock.hpp |  6 ++----
 src/proc/engine/worker/tick-service.hpp |  9 +++++----
 src/proc/play/render-configurator.cpp   | 26 +++----------------------
 src/proc/play/timings.hpp               |  8 --------
 7 files changed, 25 insertions(+), 44 deletions(-)

diff --git a/src/lib/singleton-preconfigure.hpp b/src/lib/singleton-preconfigure.hpp
index faca11e86..32973ad90 100644
--- a/src/lib/singleton-preconfigure.hpp
+++ b/src/lib/singleton-preconfigure.hpp
@@ -75,7 +75,13 @@ namespace backend {
   class MediaAccessFacade;
   using lib::Singleton;
   
-} // namespace backend_interface
+} // namespace backend
+
+namespace proc {
+namespace engine {
+  class EngineService;
+  using lib::Singleton;
+}}
 
 
 
@@ -100,6 +106,12 @@ namespace lib {
   class Singleton
     : public MockInjector
     { };
+  
+  
+  template<>
+  class Singleton
+    : public MockInjector
+    { };
 
 } // namespace lib
 
diff --git a/src/lib/singleton.hpp b/src/lib/singleton.hpp
index f7af5ddba..33ccd9fa1 100644
--- a/src/lib/singleton.hpp
+++ b/src/lib/singleton.hpp
@@ -25,10 +25,10 @@
  ** This configuration header just pulls in some other implementation headers in 
  ** the right order. The basic class template for creating singletons resides in
  ** singleton-factory.hpp, besides we need policy classes defining how to create
- ** the singleton objects and  how to manage singleton lifecycle. Finally,
+ ** the singleton objects and how to manage singleton lifecycle. Finally,
  ** we want to preconfigure singleton factories for some important facilities;
  ** e.g. sometimes we want to include a hook for injecting Test Mock instances.
- **
+ ** 
  ** You'll find the default Policies in singleton-factory.hpp and the default
  ** definition of type lumiera::singleton in singleton-preconfigure.hpp
  ** 
diff --git a/src/proc/engine/engine-service-mock.cpp b/src/proc/engine/engine-service-mock.cpp
index b47b1516b..f72e8598b 100644
--- a/src/proc/engine/engine-service-mock.cpp
+++ b/src/proc/engine/engine-service-mock.cpp
@@ -46,8 +46,6 @@ namespace engine{
   
   
   
-  /** storage for the EngineService interface object */
-  lib::Singleton EngineServiceMock::instance;
   
   
   
diff --git a/src/proc/engine/engine-service-mock.hpp b/src/proc/engine/engine-service-mock.hpp
index 2d865d223..ce4fec5e2 100644
--- a/src/proc/engine/engine-service-mock.hpp
+++ b/src/proc/engine/engine-service-mock.hpp
@@ -85,14 +85,12 @@ namespace engine{
    * order and to enqueue these into the scheduler.
    */
   class EngineServiceMock
-    : boost::noncopyable
+    : public EngineService
     {
+      lib::ScopedPtrVect processors_;
       
       
     public:
-      /** access point to the Engine Interface Mock implementation. */
-      static lib::Singleton instance;
-      
       
       EngineServiceMock();
      ~EngineServiceMock() { }
diff --git a/src/proc/engine/worker/tick-service.hpp b/src/proc/engine/worker/tick-service.hpp
index 3f93dee56..1237727e3 100644
--- a/src/proc/engine/worker/tick-service.hpp
+++ b/src/proc/engine/worker/tick-service.hpp
@@ -25,10 +25,9 @@
  ** A timer service invoking a given callback periodically.
  ** This is a rough preliminary implementation as of 1/2009. We use it to
  ** drive the frame "creation" of a player dummy (the render engine is not 
- ** ready yet). The intention is to make this a real service later on, which
- ** might consolidate and sync various ongoing output processes to a common
- ** beat, which it implements by precision posix timers. Probably then this
- ** service will become part of the backend, or rely on a timing service.
+ ** ready yet). The intention is to use this service as part of a mock engine
+ ** setup, used to verify the construction of engine components. As an integration
+ ** test, we build a "dummy player", delivering some test data frames to the Gui.
  ** 
  ** @see proc::play::DummyPlayerService
  **  
@@ -39,6 +38,7 @@
 #define PROC_PLAY_TICKSERVICE_H
 
 
+#include "lib/error.hpp"
 #include "backend/thread-wrapper.hpp"
 
 #include 
@@ -49,6 +49,7 @@ namespace proc {
 namespace node {
 
   using std::tr1::function;
+  using std::tr1::bind;
   
   
   
diff --git a/src/proc/play/render-configurator.cpp b/src/proc/play/render-configurator.cpp
index f5e1c4d4c..ba378cae7 100644
--- a/src/proc/play/render-configurator.cpp
+++ b/src/proc/play/render-configurator.cpp
@@ -25,7 +25,6 @@
 #include "proc/play/render-configurator.hpp"
 #include "proc/play/output-manager.hpp"
 #include "proc/engine/engine-service.hpp"
-#include "proc/engine/engine-service-mock.hpp"
 //#include "lib/itertools.hpp"
 
 //#include 
@@ -48,7 +47,6 @@ namespace play {
   using std::tr1::bind;
   using std::tr1::placeholders::_1;
   using engine::EngineService;
-  using engine::EngineServiceMock;
   
   typedef EngineService::QoS_Definition RenderQuality;
   
@@ -129,32 +127,14 @@ namespace play {
         
       };
     
-    class MockRenderProcessBuilder
-      : public LumieraRenderProcessBuilder
-      {
-        engine::CalcStreams
-        activateEngine (ModelPort port, Timings timings, OutputSlot::Allocation& activeOutputConnection,RenderQuality quality)
-          {
-            return EngineServiceMock::instance().calculate (port, timings, activeOutputConnection, quality);
-          }
-        
-      public:
-        MockRenderProcessBuilder (POutputManager outputManager, Timings playbackSpeed)
-          : LumieraRenderProcessBuilder(outputManager,playbackSpeed)
-          { }
-      };
     
     
-    
-    /** @internal decision point about how to configure the rendering */
+    /** @internal decision point about how to configure the rendering.
+     * This would be the point to switch the render engine used. */
     inline RenderConfigurator*
     how_to_render (POutputManager outputPossibilities, Timings playTimings)
     {
-      if (playTimings.isMockEngineRun())
-        return new MockRenderProcessBuilder (outputPossibilities, playTimings);
-            
-      else
-        return new LumieraRenderProcessBuilder (outputPossibilities, playTimings);
+      return new LumieraRenderProcessBuilder (outputPossibilities, playTimings);
     }
   
     
diff --git a/src/proc/play/timings.hpp b/src/proc/play/timings.hpp
index 649007979..d40e23a54 100644
--- a/src/proc/play/timings.hpp
+++ b/src/proc/play/timings.hpp
@@ -81,14 +81,6 @@ namespace play {
   class Timings
     {
     public:
-      /** push aside the real Lumiera engine and use a test mock.
-       * @todo 1/2012 hard wired -- until the engine becomes usable
-       */
-      bool
-      isMockEngineRun ()
-        {
-          return true;
-        }
       
       //////////////TODO further accessor functions here
       

From 7e7ecc5d5171ee74f7081f9bc9e2d2a34e683645 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 21 Jan 2012 03:13:33 +0100
Subject: [PATCH 423/496] draft: integrating an engine mock implementation

---
 src/proc/engine/calc-stream.hpp               |  32 ++++-
 src/proc/engine/engine-service-mock.cpp       |   8 +-
 src/proc/engine/engine-service-mock.hpp       |   6 +-
 src/proc/engine/engine-service.cpp            |  36 +++++
 src/proc/engine/engine-service.hpp            |   5 +
 src/proc/engine/worker/dummy-tick.hpp         | 126 ++++++++++++++++++
 .../proc/play/output-slot-protocol-test.cpp   |   2 +-
 wiki/renderengine.html                        |  10 +-
 8 files changed, 210 insertions(+), 15 deletions(-)
 create mode 100644 src/proc/engine/worker/dummy-tick.hpp

diff --git a/src/proc/engine/calc-stream.hpp b/src/proc/engine/calc-stream.hpp
index 9772c1c34..d476d3e1a 100644
--- a/src/proc/engine/calc-stream.hpp
+++ b/src/proc/engine/calc-stream.hpp
@@ -47,6 +47,21 @@ namespace engine{
 //    using lumiera::DummyPlayer;
   
   
+  /**
+   * Abstract definition of the environment
+   * hosting a given render activity (CalcStream).
+   * Exposes all the operations necessary to adjust the
+   * runtime behaviour of the render activity, like e.g.
+   * re-scheduling with modified playback speed. Since the
+   * CalcStream is an conceptual representation of "the rendering",
+   * the actual engine implementation is kept opaque this way. 
+   */
+  class RenderEnvironmentClosure
+    {
+    public:
+      virtual ~RenderEnvironmentClosure() { }   ///< this is an interface 
+    };
+  
   
   
   
@@ -69,19 +84,24 @@ namespace engine{
    */
   class CalcStream
     {
+      RenderEnvironmentClosure* env_;
+      
+    protected:
+      CalcStream (RenderEnvironmentClosure& engine)
+        : env_(&engine)
+        { }
       
       friend class EngineService;
       
+      
     public:
       CalcStream()
-        {
-          UNIMPLEMENTED("build a disabled/dead calculation stream");
-        }
+        : env_(0)
+        { }
       
       CalcStream (CalcStream const& o)
-        { 
-          UNIMPLEMENTED("clone a calculation stream");
-        }
+        : env_(o.env_)
+        { }
       
      ~CalcStream() { }
       
diff --git a/src/proc/engine/engine-service-mock.cpp b/src/proc/engine/engine-service-mock.cpp
index f72e8598b..43c6e60ee 100644
--- a/src/proc/engine/engine-service-mock.cpp
+++ b/src/proc/engine/engine-service-mock.cpp
@@ -22,6 +22,7 @@
 
 
 #include "proc/engine/engine-service-mock.hpp"
+#include "proc/engine/worker/dummy-tick.hpp"
 
 //#include 
 //#include 
@@ -49,8 +50,13 @@ namespace engine{
   
   
   
-  /** */  
+  /**
+   * Initialise a mock render engine.
+   * This dummy implementation manages a collection of
+   * "Processors", each running in a separate thread.
+   */
   EngineServiceMock::EngineServiceMock()
+    : processors_()
     { }
   
   
diff --git a/src/proc/engine/engine-service-mock.hpp b/src/proc/engine/engine-service-mock.hpp
index ce4fec5e2..21c290043 100644
--- a/src/proc/engine/engine-service-mock.hpp
+++ b/src/proc/engine/engine-service-mock.hpp
@@ -51,14 +51,16 @@
 //#include "common/instancehandle.hpp"
 //#include "lib/singleton-ref.hpp"
 #include "proc/engine/engine-service.hpp"
-#include "lib/singleton.hpp"
+#include "lib/scoped-ptrvect.hpp"
 //
 #include 
 //#include 
 //#include 
 
 
-namespace proc  {
+namespace proc {
+namespace node { class DummyTick; }  
+  
 namespace engine{
 
 //    using std::string;
diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp
index 926cef268..0c9cea551 100644
--- a/src/proc/engine/engine-service.cpp
+++ b/src/proc/engine/engine-service.cpp
@@ -87,6 +87,42 @@ namespace engine{
   }
   
   
+  /** @internal build a representation of a single, ongoing calculation effort.
+   * This "CalcStream" is tied to the actual engine implementation, but only
+   * through an opaque link, representing this concrete engine as an
+   * RenderEnvironmentClosure. This enables the created CalcStream to be
+   * re-configured and adjusted while running.
+   */
+  CalcStream
+  EngineService::activateCalculation (RenderEnvironmentClosure& engineCallback)
+  {
+    return CalcStream (engineCallback); 
+  }
+  
+  
+  /** @internal extension point
+   * Install and activate a single, ongoing calculation effort.
+   * Configure and prepare all the internal components, pre-allocate
+   * resources and add entries to the registration tables to get this
+   * render activity into running state
+   * @return CalcStream representing this newly started rendering activity
+   * @note variations and especially mock implementations of the render engine
+   *       might choose to configure internal differently. As long as the 
+   *       CalcStream and the embedded RenderEnvironmentClosure are consistent,
+   *       such a specific configuration remains opaque for the user of the
+   *       created render activity
+   */
+  CalcStream
+  EngineService::configureCalculation ()
+  {
+    UNIMPLEMENTED ("represent *this as RenderEnvironmentClosure)");
+    RenderEnvironmentClosure* todo_fake(0);  ////KABOOOM
+    
+    return activateCalculation (*todo_fake); 
+  }
+  
+  
+  
   
   /* ===== Quality-of-Service ===== */
   
diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp
index 3c0c1e708..f40ea489c 100644
--- a/src/proc/engine/engine-service.hpp
+++ b/src/proc/engine/engine-service.hpp
@@ -150,7 +150,12 @@ namespace engine{
                           Quality serviceQuality =QoS_BACKGROUND);
       
       
+    private:
+      CalcStream activateCalculation (RenderEnvironmentClosure&);
+      
     protected:
+      virtual CalcStream configureCalculation ();
+      
       void activateTracing();
       void disableTracing(); ///< EX_FREE
       
diff --git a/src/proc/engine/worker/dummy-tick.hpp b/src/proc/engine/worker/dummy-tick.hpp
new file mode 100644
index 000000000..91dc5e868
--- /dev/null
+++ b/src/proc/engine/worker/dummy-tick.hpp
@@ -0,0 +1,126 @@
+/*
+  DUMMY-TICK.hpp  -  issuing timed callbacks
+
+  Copyright (C)         Lumiera.org
+    2009,               Joel Holdsworth ,
+                        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-tick.hpp
+ ** A timer service invoking a given callback periodically.
+ ** This is a rough preliminary implementation as of 1/2009. We use it to
+ ** drive the frame "creation" of a player dummy (the render engine is not 
+ ** ready yet). The intention is to use this service as part of a mock engine
+ ** setup, used to verify the construction of engine components. As an integration
+ ** test, we build a "dummy player", delivering some test data frames to the Gui.
+ ** 
+ ** @see proc::play::DummyPlayerService
+ **  
+ */
+
+
+#ifndef PROC_ENGINE_WORKER_DUMMY_TICK_H
+#define PROC_ENGINE_WORKER_DUMMY_TICK_H
+
+
+#include "lib/error.hpp"
+#include "backend/thread-wrapper.hpp"
+
+#include 
+#include 
+
+
+namespace proc {
+namespace node {
+
+  using std::tr1::function;
+  using std::tr1::bind;
+  
+  
+  
+  /************************************************************
+   * Tick generating service for a periodic callback,
+   * with adjustable frequency. Quick'n dirty implementation!
+   */
+  class DummyTick
+    : backend::ThreadJoinable
+    {
+      typedef function Tick;
+      volatile uint timespan_;
+      
+      /** poll interval for new settings in wait state */
+      static const uint POLL_TIMEOUT = 1000;
+      
+    public:
+      DummyTick (Tick callback)
+        : ThreadJoinable("Tick generator (dummy)"
+                        , bind (&DummyTick::timerLoop, this, callback)
+                        )
+        { 
+          INFO (proc, "TickService started.");
+        }
+      
+     ~DummyTick ()
+        {
+          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::node
+#endif
+
diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp
index 7c1e04f7c..d63428175 100644
--- a/tests/components/proc/play/output-slot-protocol-test.cpp
+++ b/tests/components/proc/play/output-slot-protocol-test.cpp
@@ -77,7 +77,7 @@ namespace test {
           DataSink sink2 = *++sinks;
           
           // within the frame-calculation "loop"
-          // we perform an data exchange cycle
+          // we perform a data exchange cycle
           int64_t frameNr = 123;
           BuffHandle buff00 = sink1.lockBufferFor (frameNr);
           BuffHandle buff10 = sink2.lockBufferFor (frameNr);
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index ab9ebafed..13f113e73 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1160,14 +1160,14 @@ __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
+* within the buffer table, we need a 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
@@ -3226,8 +3226,8 @@ 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:
+
+
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 a central entity to invoke nodes consecutively. Rather, the nodes themselves contain the detailed knowledge regarding prerequisites, so the calculation plan is worked out recursively. Yet still there are some prerequisite resources to be made available for any calculation to happen. So 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
@@ -3251,7 +3251,7 @@ 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
 * 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
+* nodes in general may require 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]]

From 2eb39704fce6451650b737b49ac0a6b72a42054a Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 23 Jan 2012 02:25:12 +0100
Subject: [PATCH 424/496] test-driven brainstorming: how to use the dummy
 playback?

this is an idea how to test a test setup :)
---
 src/proc/engine/engine-service-mock.cpp       |  32 ++----
 src/proc/engine/engine-service-mock.hpp       |  28 ++---
 src/proc/engine/engine-service.cpp            |   4 +-
 src/proc/engine/engine-service.hpp            |   5 +-
 tests/47playout.tests                         |   5 +
 .../proc/play/dummy-play-connection-test.cpp  | 105 ++++++++++++++++++
 6 files changed, 129 insertions(+), 50 deletions(-)
 create mode 100644 tests/components/proc/play/dummy-play-connection-test.cpp

diff --git a/src/proc/engine/engine-service-mock.cpp b/src/proc/engine/engine-service-mock.cpp
index 43c6e60ee..5cb7e8ed2 100644
--- a/src/proc/engine/engine-service-mock.cpp
+++ b/src/proc/engine/engine-service-mock.cpp
@@ -61,33 +61,15 @@ namespace engine{
   
   
   
-  /** core operation: activate the Lumiera Render Engine.
-   * Invoking this service effectively hooks up each channel
-   * of the given model exit point to deliver into the corresponding
-   * output sink on the given OutputConnection (which is assumed
-   * to be already allocated for active use by this connection).
-   * The generated calculation streams represent actively ongoing
-   * calculations within the engine, started right away, according
-   * to the given timing constraints and service quality.
+  /** special engine configuration for mock/testing operation.
    */
-  CalcStreams
-  EngineServiceMock::calculate(ModelPort mPort,
-                               Timings nominalTimings,
-                               OutputConnection& output,
-                               Quality serviceQuality)
+  RenderEnvironmentClosure&
+  EngineServiceMock::configureCalculation ()
   {
-    UNIMPLEMENTED ("MOCK IMPLEMENTATION: build a list of standard calculation streams");
-  }
-  
-  
-  
-  /** */
-  CalcStreams
-  EngineServiceMock::calculateBackground(ModelPort mPort,
-                                         Timings nominalTimings,
-                                         Quality serviceQuality)
-  {
-    UNIMPLEMENTED ("MOCK IMPLEMENTATION: build calculation streams for background rendering");
+    UNIMPLEMENTED ("represent *this as RenderEnvironmentClosure)");
+    RenderEnvironmentClosure* todo_fake(0);  ////KABOOOM
+    
+    return *todo_fake; 
   }
   
   
diff --git a/src/proc/engine/engine-service-mock.hpp b/src/proc/engine/engine-service-mock.hpp
index 21c290043..16990cab9 100644
--- a/src/proc/engine/engine-service-mock.hpp
+++ b/src/proc/engine/engine-service-mock.hpp
@@ -76,15 +76,12 @@ namespace engine{
   
   
   
-  /******************************************************
-   * 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.
+  /****************************************************************
+   * Variant of the render engine, reconfigured for mock operation.
+   * Especially, this setup leaves out most of the actual Lumiera
+   * engine's implementation facilities. There is no scheduler
+   * and no frame cache; rather we perform simple dependent
+   * calculations which might block.
    */
   class EngineServiceMock
     : public EngineService
@@ -95,21 +92,10 @@ namespace engine{
     public:
       
       EngineServiceMock();
-     ~EngineServiceMock() { }
-      
-      CalcStreams
-      calculate(ModelPort mPort,
-                Timings nominalTimings,
-                OutputConnection& output,
-                Quality serviceQuality = EngineService::QoS_DEFAULT);
-      
-      CalcStreams
-      calculateBackground(ModelPort mPort,
-                          Timings nominalTimings,
-                          Quality serviceQuality = EngineService::QoS_BACKGROUND);
       
       
     protected:
+      virtual RenderEnvironmentClosure& configureCalculation ();
     };
   
   
diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp
index 0c9cea551..bffada11e 100644
--- a/src/proc/engine/engine-service.cpp
+++ b/src/proc/engine/engine-service.cpp
@@ -112,13 +112,13 @@ namespace engine{
    *       such a specific configuration remains opaque for the user of the
    *       created render activity
    */
-  CalcStream
+  RenderEnvironmentClosure&
   EngineService::configureCalculation ()
   {
     UNIMPLEMENTED ("represent *this as RenderEnvironmentClosure)");
     RenderEnvironmentClosure* todo_fake(0);  ////KABOOOM
     
-    return activateCalculation (*todo_fake); 
+    return *todo_fake; 
   }
   
   
diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp
index f40ea489c..2de5ff56b 100644
--- a/src/proc/engine/engine-service.hpp
+++ b/src/proc/engine/engine-service.hpp
@@ -135,8 +135,9 @@ namespace engine{
       static lib::Singleton instance;
       
       
+      virtual ~EngineService() { }
+      
       EngineService();
-     ~EngineService() { }
       
       CalcStreams
       calculate(ModelPort mPort,
@@ -154,7 +155,7 @@ namespace engine{
       CalcStream activateCalculation (RenderEnvironmentClosure&);
       
     protected:
-      virtual CalcStream configureCalculation ();
+      virtual RenderEnvironmentClosure& configureCalculation ();
       
       void activateTracing();
       void disableTracing(); ///< EX_FREE
diff --git a/tests/47playout.tests b/tests/47playout.tests
index 0bdc2a9ad..0a6aa7875 100644
--- a/tests/47playout.tests
+++ b/tests/47playout.tests
@@ -2,6 +2,11 @@ TESTING "Component Test Suite: Player and Output" ./test-components --group=play
 
 
 
+PLANNED "Dummy Player setup" DummyPlayConnection_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 "proc/play/dummy-play-connection.hpp"
+//#include "proc/engine/buffhandle.hpp"
+//#include "proc/engine/testframe.hpp"
+#include "include/play-facade.h"
+#include "lib/time/control.hpp"
+
+
+
+namespace proc {
+namespace play {
+namespace test {
+  
+  namespace time = lib::time;
+  
+  using proc::engine::BuffHandle;
+//using proc::engine::test::testData;
+//using proc::engine::test::TestFrame;
+  
+  using lumiera::Play;
+  typedef Play::Controller Controller;
+  
+  typedef time::Control DurationControl;
+  
+  
+  /*******************************************************************
+   * @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 DummyPlayConnection_test : public Test
+    {
+      virtual void
+      run (Arg) 
+        {
+          verify_simulatedPlayback();
+        }
+      
+      
+      void
+      verify_simulatedPlayback()
+        {
+          DummyPlayConnection dummy;                //////////TODO: how to pre-configure the DummyPlayConnection
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #805
+          CHECK (!dummy.isWired());
+          
+          Controller ctrl = Play::facade().connect(dummy.port(), dummy.output());
+          CHECK ( dummy.isWired());
+          
+          DurationControl playDuration;
+          ctrl.controlDuration (playDuration);
+          
+          // configure the controller to playback only for a fixed time duration
+          playDuration (dummy.getPlannedTestDuration());
+          
+          CHECK (!ctrl.is_playing());
+          
+          ctrl.play(true);                  // hit the start button
+          CHECK (ctrl.is_playing());
+          
+          dummy.waitUntilDue();             // test helper: block waiting until planned test should be done
+          
+          CHECK (!ctrl.is_playing());       // should have returned to pause, since we've set a fixed playback duration
+          CHECK (dummy.isWired());
+          
+          ctrl.close();
+          CHECK (!dummy.isWired());
+          CHECK (dummy.gotCorrectOutput());
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #805
+        }
+    };
+  
+  
+  /** Register this test class... */
+  LAUNCHER (DummyPlayConnection_test, "unit player");
+  
+  
+  
+}}} // namespace proc::play::test

From 288b737718ef255aabacf7efaed8294d9d0ff92b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 27 Jan 2012 00:14:21 +0100
Subject: [PATCH 425/496] dummy playback: stub the required operations

---
 src/proc/mobject/output-mapping.hpp           | 16 +++---
 src/proc/play/dummy-play-connection.hpp       | 57 +++++++++++++++++++
 src/proc/play/play-service.cpp                |  8 +++
 .../proc/play/dummy-play-connection-test.cpp  |  9 +--
 4 files changed, 79 insertions(+), 11 deletions(-)

diff --git a/src/proc/mobject/output-mapping.hpp b/src/proc/mobject/output-mapping.hpp
index 7d40ffdc3..807ab5f50 100644
--- a/src/proc/mobject/output-mapping.hpp
+++ b/src/proc/mobject/output-mapping.hpp
@@ -26,9 +26,9 @@
  ** OutputMapping is a complement to the OutputDesignation handles
  ** used at various places in the high-level model. It is used when
  ** translating a given output spec into another connection target
- ** - when connecting an model port to a concrete external output
+ ** - when connecting a model port to a concrete external output
  ** - when connecting a timeline to a viewer element
- ** - for implementing the viewer input selection "switchbord"
+ ** - for implementing the viewer input selection "switchboard"
  ** - for translating output designation of virtual clips
  ** OutputMapping is to be used as value object, holding concrete
  ** connections and wiring. For each of the mentioned usage situations,
@@ -38,7 +38,7 @@
  ** This definition context is actually instantiated (as base class).
  ** The mapping table actually just stores an association of hash
  ** values, which typically are interpreted as asset::ID.
- ** But the actual mapping result is retrieved on each acces
+ ** But the actual mapping result is retrieved on each access
  ** by invoking a functor on the stored hash value,
  ** thus the final resolution is done \em late.
  ** 
@@ -104,10 +104,12 @@ namespace mobject {
   
   /**
    * OutputMapping is a facility to resolve output designations.
-   * For a given specification, resolution to the desired
-   * target specification may be derived. Here, the 
-   * type of the target specification is defined 
-   * through the type parameter.
+   * The session/model uses preliminary or partial output specifications,
+   * which are to be resolved to an actual system output while building
+   * and preparing a render network for operation (playback/rendering).
+   * For a given specification, resolution to the desired target spec
+   * may be derived by querying the OutputMapping. Here, the kind of
+   * the target specification is defined through the type parameter.
    * 
    * \par definition of specific mapping behaviour
    * 
diff --git a/src/proc/play/dummy-play-connection.hpp b/src/proc/play/dummy-play-connection.hpp
index 0f002cb1b..5f42cf473 100644
--- a/src/proc/play/dummy-play-connection.hpp
+++ b/src/proc/play/dummy-play-connection.hpp
@@ -36,6 +36,10 @@
 //#include "include/display-facade.h"
 //#include "common/instancehandle.hpp"
 //#include "lib/singleton-ref.hpp"
+#include "proc/play/output-manager.hpp"
+#include "proc/mobject/model-port.hpp"
+#include "lib/time/timequant.hpp"
+#include "lib/iter-source.hpp"
 //
 #include 
 //#include 
@@ -49,15 +53,68 @@ namespace play {
 //    using lumiera::Subsys;
 //    using lumiera::Display;
 //    using lumiera::DummyPlayer;
+  using lib::time::Duration;
   
+  typedef lib::IterSource::iterator ModelPorts;  
+  
+  
+  struct PlayTestFrames_Strategy
+    {
+      
+    };
   
   
   /********************************************************************
    */
+  template
   class DummyPlayConnection
     : boost::noncopyable
     {
       
+    public:
+      
+      ModelPorts
+      provide_testModelPorts()
+        {
+          UNIMPLEMENTED ("provide a set of test model ports");
+        }
+      
+      POutputManager
+      provide_testOutputSlot()
+        {
+          UNIMPLEMENTED ("provide a suitable output sink simulation");
+        }
+      
+      
+      /* === Test Support API === */
+      
+      bool
+      isWired()
+        {
+          UNIMPLEMENTED ("is this dummy in activated state?");
+        }
+      
+      Duration
+      getPlannedTestDuration()
+        {
+          UNIMPLEMENTED ("manage the a planned test duration");
+        }
+      
+      /** test helper: blocking wait during an output test.
+       *  The waiting time should be in accordance with the
+       *  \link #getPlannedTestduration planned value \endlink,
+       */
+      void
+      waitUntilDue()
+        {
+          UNIMPLEMENTED ("do a blocking wait, while an output test is performed in other threads");
+        }
+      
+      bool
+      gotCorrectOutput()
+        {
+          UNIMPLEMENTED ("verify proper operation by inspecting the provided test dummy components");
+        }
     };
   
   
diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp
index 0674fd934..6a32ed2c5 100644
--- a/src/proc/play/play-service.cpp
+++ b/src/proc/play/play-service.cpp
@@ -44,6 +44,14 @@ namespace lumiera {
   Play::~Play() { } // emit VTables here...
   
   
+  /** Storage for the lumiera::Play facade access point 
+   * @note PlayService uses a InterfaceFacadeLink member
+   *       for actually establishing the PlayService instance
+   *       as implementation of lumiera::Play
+   */
+  facade::Accessor Play::facade;
+  
+  
   
 }//(End) namespace lumiera
 
diff --git a/tests/components/proc/play/dummy-play-connection-test.cpp b/tests/components/proc/play/dummy-play-connection-test.cpp
index fc810d783..21d22625b 100644
--- a/tests/components/proc/play/dummy-play-connection-test.cpp
+++ b/tests/components/proc/play/dummy-play-connection-test.cpp
@@ -46,6 +46,8 @@ namespace test {
   
   typedef time::Control DurationControl;
   
+  typedef DummyPlayConnection DummyPlayer;
+  
   
   /*******************************************************************
    * @test verify the OutputSlot interface and base implementation
@@ -66,11 +68,11 @@ namespace test {
       void
       verify_simulatedPlayback()
         {
-          DummyPlayConnection dummy;                //////////TODO: how to pre-configure the DummyPlayConnection
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #805
+          DummyPlayer dummy;
           CHECK (!dummy.isWired());
           
-          Controller ctrl = Play::facade().connect(dummy.port(), dummy.output());
+          Controller ctrl = Play::facade().connect ( dummy.provide_testModelPorts()
+                                                   , dummy.provide_testOutputSlot());
           CHECK ( dummy.isWired());
           
           DurationControl playDuration;
@@ -92,7 +94,6 @@ namespace test {
           ctrl.close();
           CHECK (!dummy.isWired());
           CHECK (dummy.gotCorrectOutput());
-#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #805
         }
     };
   

From 3768791c76aceeee92ce80bf709fff9728c5d37b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 28 Jan 2012 23:12:42 +0100
Subject: [PATCH 426/496] considerations how to connect exit nodes to external
 outputs

---
 src/proc/mobject/session/fixture.hpp          |  5 ++
 src/proc/mobject/session/segment.hpp          |  4 ++
 src/proc/mobject/session/segmentation.hpp     |  4 ++
 src/proc/play/dummy-play-connection.hpp       | 52 ++++++++++++++++++-
 src/proc/play/render-configurator.cpp         | 41 +++++++--------
 .../proc/play/dummy-play-connection-test.cpp  | 18 +++----
 wiki/renderengine.html                        | 34 ++++++------
 7 files changed, 109 insertions(+), 49 deletions(-)

diff --git a/src/proc/mobject/session/fixture.hpp b/src/proc/mobject/session/fixture.hpp
index 201d5a425..0913a56ba 100644
--- a/src/proc/mobject/session/fixture.hpp
+++ b/src/proc/mobject/session/fixture.hpp
@@ -45,10 +45,15 @@ namespace session {
   
   
   
+  /**
+   * @todo 1/2012 Just a Placeholder. The real thing is not yet implemented.
+   * @see http://lumiera.org/wiki/renderengine.html#Fixture
+   */
   class Fixture
     : boost::noncopyable
     {
     protected:
+      /////////////////////////////////////////////////TODO: placeholder code
       list content_;
       boost::scoped_ptr partitioning_;
       
diff --git a/src/proc/mobject/session/segment.hpp b/src/proc/mobject/session/segment.hpp
index 900cb76b0..593c81d94 100644
--- a/src/proc/mobject/session/segment.hpp
+++ b/src/proc/mobject/session/segment.hpp
@@ -42,6 +42,9 @@ namespace session {
    * is partitioned such that each segment is structurally constant.
    * For each segment there is a RenderGraph (unit of the render engine) which
    * is able to render all ExitNodes for this segment.
+   * 
+   * @todo 1/2012 Just a Placeholder. The real thing is not yet implemented.
+   * @see http://lumiera.org/wiki/renderengine.html#Fixture
    */
   class Segment
     {
@@ -56,6 +59,7 @@ namespace session {
       // TODO: actually necessary??
       // TODO: ownership??
       
+      /////////////////////////////////////////////////TODO: placeholder code
     };
   
   
diff --git a/src/proc/mobject/session/segmentation.hpp b/src/proc/mobject/session/segmentation.hpp
index 75238b53d..08dac8c93 100644
--- a/src/proc/mobject/session/segmentation.hpp
+++ b/src/proc/mobject/session/segmentation.hpp
@@ -44,9 +44,13 @@ namespace session {
    * it is the general entry point for accessing the correct part of the engine
    * responsible for a given timeline time point.
    * @see SegmentationTool actually calculating the Segmentation
+   * 
+   * @todo 1/2012 Just a Placeholder. The real thing is not yet implemented.
+   * @see http://lumiera.org/wiki/renderengine.html#Fixture
    */
   class Segmentation
     {
+      /////////////////////////////////////////////////TODO: placeholder code
       
       /** segments of the engine in ordered sequence. */
       list segments_;
diff --git a/src/proc/play/dummy-play-connection.hpp b/src/proc/play/dummy-play-connection.hpp
index 5f42cf473..3dff88f75 100644
--- a/src/proc/play/dummy-play-connection.hpp
+++ b/src/proc/play/dummy-play-connection.hpp
@@ -21,8 +21,50 @@
 */
 
 /** @file dummy-play-connection.hpp
- ** to \em provide this service, not to access it.
- **
+ ** Dummy and test setup of playback and rendering, \em omitting most of the Lumiera engine.
+ ** Building this dummy configuration was driven by the need to test and verify the design
+ ** in the course of building the foundations of the render engine. The design of Lumiera's
+ ** engine is elaborate, and thus -- for a long time -- we have to live with a not-yet
+ ** operational engine. While, at the same time, we need to start integrating components
+ ** to see if and how the design works out. So, as a remedy, we create a fixture of
+ ** "coordinated placeholders". These can be used to stand-in for the real services still
+ ** to be written, allowing to invoke the high-level interfaces soon. And behind the scenes,
+ ** these placeholders are connected, allowing to produce specific test situations and then
+ ** verify the results after the test run.
+ ** 
+ ** \par Use cases
+ ** This dummy setup can be used in various circumstances
+ ** - for unit tests we want to produce artificial test media frames: each TestFrame is
+ **   produced with a reproducible pseudo-random sequence and can be verified to the last bit.
+ ** - for integration tests, we want to generate test media data, either to send them to a file,
+ **   or to a real system output
+ ** - the GUI needs a dummy engine for being able to implement at least some operational pieces
+ **   of functionality, instead of just creating windows, menus and icons.
+ ** - on the long run, the application will need some kind of test data generator for the users
+ **   to verify a more complicated wiring setup. Thus, the DummyPlayConnection is there to stay!
+ ** 
+ ** Because these are somewhat similar usage scenarios, where this and that part is to be exchanged
+ ** for some, we prefer a policy based design here: The DummyPlayConnection is templated
+ ** to use a \em strategy, filling in the variable parts.  
+ ** 
+ ** \par provided test services
+ ** By using different strategy template parameters, we create different flavours of the dummy;
+ ** each one counting as a separate setup (not related to each other, that is). The actual instance
+ ** then can just be default created; it should be placed into an scope enduring the whole usage
+ ** cycle. Repeated re-initialisation or re-loading is outside the intended usage scope here.
+ ** 
+ ** The core interface allows to retrieve dummy implementations of
+ ** - a session model exposing exit node(s)
+ ** - generator object(s) to live within this session model
+ ** - corresponding generator nodes to serve as implementation of the former
+ ** - a low-level render node network ready to use, relying on those generator nodes
+ ** - OutputSlot implementations to serve as pseudo- or demo output facilities
+ ** - an OutputManager exposing those output facilities.
+ ** 
+ ** The test support interface provides a test driver for performing a controlled 
+ ** playback or rendering for some time. Thus, a test routine may lock into a blocking wait,
+ ** to investigate results after the planned test sequence was performed. 
+ ** 
  ** @see lumiera::DummyPlayer
  ** @see gui::PlaybackController usage example 
  */
@@ -65,6 +107,12 @@ namespace play {
   
   
   /********************************************************************
+   * Framework for dummy playback and rendering.
+   * A DummyPlayConnection provides a coherent set of placeholders,
+   * allowing to start a data producing process while leaving out
+   * various parts of the real engine implementation. The specific
+   * mode of operation, suitable for various test scenarios, may be
+   * fine tuned by the strategy object defined as template parameter. 
    */
   template
   class DummyPlayConnection
diff --git a/src/proc/play/render-configurator.cpp b/src/proc/play/render-configurator.cpp
index ba378cae7..0edb8b070 100644
--- a/src/proc/play/render-configurator.cpp
+++ b/src/proc/play/render-configurator.cpp
@@ -51,27 +51,6 @@ namespace play {
   typedef EngineService::QoS_Definition RenderQuality;
   
   
- 
-  RenderConfigurator::~RenderConfigurator() { } // emit VTable here...
-  
-  
-  
-  /** Template Method: how to build an active render feed,
-   *  pulling from the given exit point of the model and
-   *  feeding the OutputSlot established appropriately
-   *  to deliver media data of suitable type */
-  Feed
-  RenderConfigurator::buildActiveFeed (ModelPort port)
-  {
-    OutputSlot& slot = getOutputFor (port);
-    return Feed (buildCalculationStreams (port,slot)); 
-  }
-  
-  
-  
-  
-  
-  
   namespace { // Implementation details...
     
 //    using std::tr1::bind;
@@ -110,8 +89,8 @@ namespace play {
             return activateEngine (port, nominalTimings, activeOutputConnection, renderQuality_);
           }
         
-      protected:
-        virtual engine::CalcStreams
+        
+        engine::CalcStreams
         activateEngine (ModelPort port, Timings timings, OutputSlot::Allocation& activeOutputConnection, RenderQuality quality)
           {
             return EngineService::instance().calculate (port, timings, activeOutputConnection, quality);
@@ -140,6 +119,22 @@ namespace play {
     
   } // (End) hidden service impl details
   
+    
+  
+ 
+  RenderConfigurator::~RenderConfigurator() { } // emit VTable here...
+  
+  
+  /** Template Method: how to build an active render feed,
+   *  pulling from the given exit point of the model and
+   *  feeding the OutputSlot established appropriately
+   *  to deliver media data of suitable type */
+  Feed
+  RenderConfigurator::buildActiveFeed (ModelPort port)
+  {
+    OutputSlot& slot = getOutputFor (port);
+    return Feed (buildCalculationStreams (port,slot)); 
+  }
   
   
   
diff --git a/tests/components/proc/play/dummy-play-connection-test.cpp b/tests/components/proc/play/dummy-play-connection-test.cpp
index 21d22625b..8f2350c11 100644
--- a/tests/components/proc/play/dummy-play-connection-test.cpp
+++ b/tests/components/proc/play/dummy-play-connection-test.cpp
@@ -42,7 +42,6 @@ namespace test {
 //using proc::engine::test::TestFrame;
   
   using lumiera::Play;
-  typedef Play::Controller Controller;
   
   typedef time::Control DurationControl;
   
@@ -71,27 +70,28 @@ namespace test {
           DummyPlayer dummy;
           CHECK (!dummy.isWired());
           
-          Controller ctrl = Play::facade().connect ( dummy.provide_testModelPorts()
-                                                   , dummy.provide_testOutputSlot());
+          Play::Controller player
+            = Play::facade().connect ( dummy.provide_testModelPorts()
+                                     , dummy.provide_testOutputSlot());
           CHECK ( dummy.isWired());
           
           DurationControl playDuration;
-          ctrl.controlDuration (playDuration);
+          player.controlDuration (playDuration);
           
           // configure the controller to playback only for a fixed time duration
           playDuration (dummy.getPlannedTestDuration());
           
-          CHECK (!ctrl.is_playing());
+          CHECK (!player.is_playing());
           
-          ctrl.play(true);                  // hit the start button
-          CHECK (ctrl.is_playing());
+          player.play(true);                // hit the start button
+          CHECK (player.is_playing());
           
           dummy.waitUntilDue();             // test helper: block waiting until planned test should be done
           
-          CHECK (!ctrl.is_playing());       // should have returned to pause, since we've set a fixed playback duration
+          CHECK (!player.is_playing());     // should have returned to pause, since we've set a fixed playback duration
           CHECK (dummy.isWired());
           
-          ctrl.close();
+          player.close();
           CHECK (!dummy.isWired());
           CHECK (dummy.gotCorrectOutput());
         }
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 13f113e73..e9fb7347b 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1967,7 +1967,7 @@ 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.
 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.
@@ -1993,12 +1993,12 @@ The fixture is like a grid, where one dimension is given by the [[model ports|Mo
 
 ;Segmentation
 :The segmentation partitiones the time axis of a single timeline into segments of constant (wiring) configuration
-:Together, the segments form a seamless sequence of time intervals. They contain a copy of each (explicit) placement of a visible object touching that time interval. Besides that, segments are the top level grouping device of the render engine node graph; they are always built and discarded at once.
+:Together, the segments form a seamless sequence of time intervals. They contain a copy of each (explicit) placement of a visible object touching that time interval. Besides that, segments are the top level grouping device of the render engine node graph; individual segments are immutable, they are built and discarded as a whole chunk.
 :Segments (and even a different Segmentation) may be //hot swapped// into an ongoing render.
 
 ;Exit Nodes
 :Each segment holds an ExitNode for each relevant ModelPort of the corresponding timeline.
-: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.//
+: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//&nbsp; in some segmehts.
 
@@ -3133,9 +3133,9 @@ Model ports are represented by small non-copyable descriptor objects with distin
-
+
Model ports are conceptual entities, denoting the points where output might possibly be produced &rarr; see [[definition|ModelPort]].
-But there is an actual representation, a collection of small descriptor objects managed by the Fixture and organised within the model port table datastructure. Because model ports are discovered during the build process, we need the ability to (re)build this table dynamically, finally swapping in the modified configuration with a transactional switch. Only the builder is allowed to perform such mutations, while for client code model ports are immutable.
+But there is an actual representation, a collection of small descriptor objects managed by the Fixture and organised within the model port table datastructure. Because model ports are discovered during the build process, we need the ability to (re)build this table dynamically, finally swapping in the modified configuration with a transactional switch. Only the builder is allowed to perform such mutations, while for client code the model ports are immutable.
 
 !supported operations
 * get the model port by ~Pipe-ID
@@ -4481,7 +4481,7 @@ The Controller is exposed to the client and acts as frontend handle, while the p
 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.
 
-
+
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
@@ -4489,6 +4489,10 @@ Right within the play process, there is a separation into two realms, relying on
 * managing existing play processes
 * convenience short-cuts for //performing//&nbsp; several kinds of high-level model objects
 
+!subject of performance
+Every play or render process will perfrom a part of the session. This part can be specified in varios ways, but in the end, every playback or render boils down to //performing some model ports.// While the individual model port as such is just an identifier (actually implemented as ''pipe-ID''), it serves as a common identifier used at various levels and tied into several related contexts. For one, by querying the [[Fixture]], the ModelPort leads to the actual ExitNode -- the stuff actually producing data when being pulled. Besides that, the OutputManager used for establishing the play process is able to resolve a real OutputSlot -- which, as a side effect, also yields the final data format and data implementation type to use for rendering or playback.
+
+
 !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
@@ -7748,7 +7752,7 @@ In case it's not already clear: we don't have "the" Render Engine, rat
 The &raquo;current setup&laquo; of the objects in the session is sort of a global state. Same holds true for the Controller, as the Engine can be at playback, it can run a background render or scrub single frames. But the whole complicated subsystem of the Builder and one given Render Engine configuration can be made ''stateless''. As a benefit of this we can run this subsystems multi-threaded without the need of any precautions (locking, synchronizing). Because all state information is just passed in as function parameters and lives in local variables on the stack, or is contained in the StateProxy which represents the given render //process// and is passed down as function parameter as well. (note: I use the term "stateless" in the usual, slightly relaxed manner; of course there are some configuration values contained in instance variables of the objects carrying out the calculations, but this values are considered to be constant over the course of the object usage).
 
-
+
within Lumiera's ~Proc-Layer, on the conceptual level there are two kinds of connections: data streams and control connections. The wiring deals with how to define and control these connections, how they are to be detected by the builder and finally implemented by links in the render engine.
 &rarr; see OutputManagement
 &rarr; see OutputDesignation
@@ -7757,24 +7761,24 @@ The &raquo;current setup&laquo; of the objects in the session is sort of
 
 
 !Stream connections
-A stream connection should result in media data traveling from a source to a sink. Here we have to distinguish between the high-level view and the situation in the render engine. At the session level and thus for the user, a //stream// is the elementary unit of "media content" flowing through the system. It can be described unambigously by having an uniform StreamType &mdash; this doesn't exclude the stream from being inherently structured, like containing several channels. The HighLevelModel can be interpreted as //creating a system of stream connections,// which, more specifically can be categorised into two kinds or types of connections -- the rigit and the flexible parts:
-* the [[Pipes|Pipe]] are rather rigid ''processing chains'', limited to using one specific StreamType. Pipes are build by attaching processing descriptors (Effect ~MObjects), where the order of attachment is given by the placement.
+A stream connection should result in media data travelling from a source to a sink. Here we have to distinguish between the high-level view and the situation in the render engine. At the session level and thus for the user, a //stream// is the elementary unit of "media content" flowing through the system. It can be described unambigously by having an uniform StreamType &mdash; this doesn't exclude the stream from being inherently structured, like containing several channels. The HighLevelModel can be interpreted as //creating a system of stream connections,// which, more specifically can be categorised into two kinds or types of connections -- the rigid and the flexible parts:
+* the [[Pipes|Pipe]] are rather rigid ''processing chains'', limited to using one specific StreamType. <br/>Pipes are built by attaching processing descriptors (Effect ~MObjects), where the order of attachment is given by the placement.
 * there are flexible interconnections or ''routing links'', including the ability to sum or overlay media streams, and the possibility of stream type conversions. They are controlled by the OutputDesignation ("wiring plug"), to be queried from the placement at the source side of the interconnection (i.e. at the exit point of a pipe)
-Please note, the high-level model comprises a blue print for constructing the render engine. There is no real data "flowing" through this model, thus any "wiring" may be considered conceptual. Within this context, any wiring specifications just express //the intention of getting things connected in a specific way.// Consider the example of a clip, which might find out (through query of his placement) that he's intended to produce output for some destination or  bus or subgroup called "XYZ"
+Please note, the ''High-level model'' comprises a blue print for constructing the render engine. There is no real data "flowing" through this model, thus any "wiring" may be considered conceptual. Within this context, any wiring specifications just express //the intention of getting things connected in a specific way.// Consider the example of a clip, which might find out (through query of his placement) that he's intended to produce output for some destination or  bus or subgroup called "Ambiance sound"
 
-The builder to the contrary considers matters locally. He's confined to a given set of objects handed in for processing, and during that processing will collect all [[plugs|WiringPlug]] and [[claims|WiringClaim]] encountered. While the former denote the desired //destination//&nbsp; of data emerging from a pipe, the wiring claim expresses the fact that a given object //claims to be some output destination.// Typically, each [[global pipe or bus|GlobalPipe]] raises such a claim. Both plugs and claims are processed on a "just in case they exist" base: When encountering a plug and //resolving//&nbsp; the corresponding claim, the builder drops off a WiringRequest accordingly. Note the additional resolution, which might be necessary due to virtual media and nested sequences (read: the output designation might need to be translated into another designation, using the media/channel mapping created by the virtual entity &rarr; see [[mapping of output designations|OutputMapping]])
+The ''Builder'' to the contrary considers matters locally. He's confined to a given set of objects handed in for processing, and during that processing will successively collect all the [[plugs|WiringPlug]] and [[claims|WiringClaim]] encountered. While the former denote the desired //destination//&nbsp; of data emerging from a pipe, the wiring claim expresses the fact that a given object //claims to be some output destination.// Typically, each [[global pipe or bus|GlobalPipe]] raises such a claim. Both plugs and claims are processed on a "just in case they exist" base: When encountering a plug and //resolving//&nbsp; the corresponding claim, the builder drops off a WiringRequest accordingly. Note the additional resolution, which might be necessary due to virtual media and nested sequences (read: the output designation might need to be translated into another designation, using the media/channel mapping created by the virtual entity &rarr; see [[mapping of output designations|OutputMapping]])
 
-The Processing os such a wiring request drives the actual connection step. It is conducted by the OperationPoint, provided by and executing within a BuilderMould and controlled by a [[processing pattern|ProcPatt]]. This rather flexible setup allows for wiring summation lines, include faders, scale and offset changes and various overlay modes. Thus the specific form of the connection wired in here depends on all the local circumstances visible at that point of operation:
+The Processing of such a wiring request drives the actual connection step. It is conducted by the OperationPoint, provided by and executing within a BuilderMould and controlled by a [[processing pattern|ProcPatt]]. This rather flexible setup allows for wiring summation lines, include faders, scale and offset changes and various overlay modes. Thus the specific form of the connection wired in here depends on all the local circumstances visible at that point of operation:
 * the mould is picked from a small number of alternatives, based on the general wiring situation.
 * the processing pattern is queried to fit the mould, the stream type and additional placement specifications ({{red{TODO 11/10: work out details}}})
 * the stream type system itself contributes in determining possible connections and conversions, introducing further processing patterns
 
-The final result, within the render engine, is a network of processing nodes. Each of this nodes holds a WiringDescriptor, created as a result of the wiring operation detailed above. This descriptor lists the predecessors, and (in somewhat encoded form) the other details necessary for the processing node to respond properly at the engine's calculation requests (read: those details are implementation bound and can be expeted to be made to fit)
+The final result, within the ''Render Engine'', is a network of processing nodes. Each of this nodes holds a WiringDescriptor, created as a result of the wiring operation detailed above. This descriptor lists the predecessors, and (in somewhat encoded form) the other details necessary for the processing node to respond properly at the engine's calculation requests (read: those details are implementation bound and can be expeted to be made to fit)
 
-On a more global level, this LowLevelModel within the engine exposes a number of [[exit nodes|ExitNode]], each corresponding to a ModelPort, thus being a possible source to be handled by the OutputManager, which is responsible for mapping and connecting nominal outputs (the model ports) to actual output sinks (external connections and viewer windows). A model port isn't necessarily an absolute endpoint of connected processing nodes &mdash; it may as well reside in the middle of the network, e.g. as a ProbePoint. Besides the core engine network, there is also an [[output network|OutputNetwork]], built and extended on demand to prepare generated data for the purpose of presentation. This ViewerPlayConnection might necesitate scaling or interpolating video for a viewer, adding overlays with control information produced by plugins, or rendering and downmixing multichannel sound. By employing this output network, the same techniques used to control wiring of the main path, can be extended to control this output preparation step. ({{red{WIP 11/20}}} some important details are to be settled here, like how to control semi-automatic adaptation steps. But that is partially true also for the main network: for example, we don't know where to locate and control the faders generated as a consequence of building a summation line)
+On a more global level, this LowLevelModel within the engine exposes a number of [[exit nodes|ExitNode]], each corresponding to a ModelPort, thus being a possible source to be handled by the OutputManager, which is responsible for mapping and connecting nominal outputs (the model ports) to actual output sinks (external connections and viewer windows). A model port isn't necessarily an absolute endpoint of connected processing nodes &mdash; it may as well reside in the middle of the network, e.g. as a ProbePoint. Besides the core engine network, there is also an [[output network|OutputNetwork]], built and extended on demand to prepare generated data for the purpose of presentation. This ViewerPlayConnection might necesitate scaling or interpolating video for a viewer, adding overlays with control information produced by plugins, or rendering and downmixing multichannel sound. By employing this output network, the same techniques used to control wiring of the main path, can be extended to control this output preparation step. ({{red{WIP 11/10}}} some important details are to be settled here, like how to control semi-automatic adaptation steps. But that is partially true also for the main network: for example, we don't know where to locate and control the faders generated as a consequence of building a summation line)
 
 !!!Participants and Requirements
-* the ~Pipe-ID needs to be something easily usable
+* the ~Pipe-ID is an universal key to denote connections, outputs and ports.
 * output designation is just a ~Pipe-ID, actually a thin wrapper to make the intention explicit
 * claims and plugs are to be implemented as LocatingPin
 * as decided elsewhere, we get a [[Bus-MObject|BusMO]] as an attachment point

From 9dae352731ee943182d22e2d3aa4650c798c40a6 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 1 Feb 2012 05:32:50 +0100
Subject: [PATCH 427/496] clarification regarding the Dispatcher

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

diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index e9fb7347b..125ee9314 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -2123,8 +2123,23 @@ 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 [[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
+
+
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.
+
+!defining the dispatcher interface
+The purpose of this interface is to support the planning of new jobs, for a given CalcStream. From time to time, a chunk of new RenderJob entries will be preapred for the [[Scheduler]]. Each job knows his frame number and the actual ProcNode to operate. So, to start planning jobs, we need to translate time &rarr; frame number &rarr; segment &rarr; real exit node.
+
+!!!Invocation situation
+* our //current starting point//&nbsp; is given as point-in-time closure
+* we want to address a single frame, offset by a given number of frames relative to the current position
+* &rArr; the dispatcher returns the //fundamental coordinates// of that frame, namely
+** the absolute nominal time
+** the absolute frame number
+** a concrete ExitNode to pull for this frame
+** focussed context information:
+*** the relevant [[segment|Segmentation]] responsible for producing this frame
+*** the corresponding JobTicket to use at this point
+
/***
@@ -4465,12 +4480,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|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 [[dispatching|Dispatcher]] the [[calculation stream|CalcStream]] and scheduling individual calculation jobs
+* the [[Backend]] is responsible for [[dispatching|FrameDispatcher]] the [[calculation stream|CalcStream]] and scheduling individual calculation jobs
 * the RenderEngine has the ability to carry out individual frame calculations
 * the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery
 
@@ -4481,18 +4496,18 @@ The Controller is exposed to the client and acts as frontend handle, while the p
 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.
 
-
+
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.
 
+!subject of performance
+Every play or render process will perfrom a part of the session. This part can be specified in varios ways, but in the end, every playback or render boils down to //performing some model ports.// While the individual model port as such is just an identifier (actually implemented as ''pipe-ID''), it serves as a common identifier used at various levels and tied into several related contexts. For one, by querying the [[Fixture]], the ModelPort leads to the actual ExitNode -- the stuff actually producing data when being pulled. Besides that, the OutputManager used for establishing the play process is able to resolve onto a real OutputSlot -- which, as a side effect, also yields the final data format and data implementation type to use for rendering or playback.
+
+
 !provided services
 * creating a PlayProcess
 * managing existing play processes
 * convenience short-cuts for //performing//&nbsp; several kinds of high-level model objects
 
-!subject of performance
-Every play or render process will perfrom a part of the session. This part can be specified in varios ways, but in the end, every playback or render boils down to //performing some model ports.// While the individual model port as such is just an identifier (actually implemented as ''pipe-ID''), it serves as a common identifier used at various levels and tied into several related contexts. For one, by querying the [[Fixture]], the ModelPort leads to the actual ExitNode -- the stuff actually producing data when being pulled. Besides that, the OutputManager used for establishing the play process is able to resolve a real OutputSlot -- which, as a side effect, also yields the final data format and data implementation type to use for rendering or playback.
-
-
 !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

From e581246f631d928617b4d881c94b241e867ec910 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 4 Feb 2012 00:52:28 +0100
Subject: [PATCH 428/496] forming some entities to support the dispatch step

---
 src/proc/engine/frame-coord.hpp | 73 +++++++++++++++++++++++++++++++++
 src/proc/engine/time-anchor.hpp | 72 ++++++++++++++++++++++++++++++++
 wiki/renderengine.html          | 42 +++++++++++--------
 3 files changed, 171 insertions(+), 16 deletions(-)
 create mode 100644 src/proc/engine/frame-coord.hpp
 create mode 100644 src/proc/engine/time-anchor.hpp

diff --git a/src/proc/engine/frame-coord.hpp b/src/proc/engine/frame-coord.hpp
new file mode 100644
index 000000000..6ff385790
--- /dev/null
+++ b/src/proc/engine/frame-coord.hpp
@@ -0,0 +1,73 @@
+/*
+  FRAME-COORD.hpp  -  unique distinct coordinates of a frame to be calculated
+
+  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.
+
+*/
+
+
+#ifndef PROC_ENGINE_FRAME_COORD_H
+#define PROC_ENGINE_FRAME_COORD_H
+
+#include "proc/common.hpp"
+//#include "proc/state.hpp"
+#include "lib/time/timevalue.hpp"
+#include "lib/time/timequant.hpp"
+
+
+
+namespace proc {
+namespace engine {
+  
+  using lib::time::TimeSpan;
+  using lib::time::FSecs;
+  using lib::time::Time;
+//  
+//  class ExitNode;
+  
+  /**
+   * effective coordinates of a frame to be calculated.
+   * Frame coordinates are produced as result of the Dispatcher call,
+   * thus forming the foundation of an actual ProcNode invocation
+   * A frame render job can be characterised by
+   * - the nominal (timeline) time of the frame
+   * - the corresponding frame-number
+   * - the real wall-clock time of expected delivery
+   * - timing constraints (e.g. latency to observe) //////////////TODO : not clear if we repeat this information here
+   * - the actual node to pull data from
+   * - the segment holding that node                //////////////TODO : is this information really required??
+   * 
+   * @todo 1/12 WIP-WIP-WIP defining the invocation sequence and render jobs
+   */
+  class FrameCoord
+    {
+      
+    public:
+      FrameCoord()
+        {
+          UNIMPLEMENTED ("anything regarding the Node Invocation");
+        }
+      
+      // using default copy operations
+      
+    };
+  
+  
+  
+}} // namespace proc::engine
+#endif
diff --git a/src/proc/engine/time-anchor.hpp b/src/proc/engine/time-anchor.hpp
new file mode 100644
index 000000000..4f8c62b16
--- /dev/null
+++ b/src/proc/engine/time-anchor.hpp
@@ -0,0 +1,72 @@
+/*
+  TIME-ANCHOR.hpp  -  current render evaluation time point closure
+
+  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.
+
+*/
+
+
+#ifndef PROC_ENGINE_TIME_ANCHOR_H
+#define PROC_ENGINE_TIME_ANCHOR_H
+
+#include "proc/common.hpp"
+//#include "proc/state.hpp"
+#include "lib/time/timevalue.hpp"
+
+
+
+namespace proc {
+namespace engine {
+  
+  using lib::time::TimeSpan;
+  using lib::time::FSecs;
+  using lib::time::Time;
+//  
+//  class ExitNode;
+  
+  /**
+   * The process of playback or rendering is a continued series of
+   * exploration and evaluation. The outline of what needs to be calculated
+   * is determined continuously, proceeding in chunks of evaluation.
+   * Each of these continued partial evaluations establishes a distinct
+   * anchor or breaking point in time: everything before this point
+   * is settled and planned thus far. Effectively, this time point
+   * acts as a evaluation closure, to be picked up on the
+   * next partial evaluation. More specifically, the TimeAnchor
+   * closure is the definitive binding between the abstract
+   * logical time of the session timeline, and the real
+   * wall-clock time forming the deadline for rendering.
+   * 
+   * @todo 1/12 WIP-WIP-WIP just emerging as a concept
+   */
+  class TimeAnchor
+    {
+      
+    public:
+      TimeAnchor()
+        {
+          UNIMPLEMENTED ("anything regarding the Engine backbone");
+        }
+      
+      // using default copy operations
+    };
+  
+  
+  
+}} // namespace proc::engine
+#endif
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 125ee9314..bfd35cd22 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1899,10 +1899,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 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")
+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 PipeID as used at the corresponding ModelPort and the corresponding [[global pipe|GlobalPipe]] ("5.1 Audio master bus")
 
@@ -2123,14 +2123,14 @@ 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 [[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.
 
 !defining the dispatcher interface
 The purpose of this interface is to support the planning of new jobs, for a given CalcStream. From time to time, a chunk of new RenderJob entries will be preapred for the [[Scheduler]]. Each job knows his frame number and the actual ProcNode to operate. So, to start planning jobs, we need to translate time &rarr; frame number &rarr; segment &rarr; real exit node.
 
 !!!Invocation situation
-* our //current starting point//&nbsp; is given as point-in-time closure
+* our //current starting point//&nbsp; is given as ''time anchor'' closure
 * we want to address a single frame, offset by a given number of frames relative to the current position
 * &rArr; the dispatcher returns the //fundamental coordinates// of that frame, namely
 ** the absolute nominal time
@@ -2139,7 +2139,9 @@ The purpose of this interface is to support the planning of new jobs, for a give
 ** focussed context information:
 *** the relevant [[segment|Segmentation]] responsible for producing this frame
 *** the corresponding JobTicket to use at this point
-
+ +!!!the ~TimeAnchor +In fact, the whole process of playback or rendering is a continued series of exploration and evaluation. The outline of what needs to be calculated is determined continuously, proceeding in chunks of evaluation. The evaluation structure of the render engine is quite similar to the //fork-join//-pattern, just with the addition of timing constraints. This leads to an invocation pattern, where a partial evaluation happens from time to time. Each of those evaluations establishes a breaking point in time: everything //before// this point is settled and planned thus far. So, this point is an ''anchor'' or closure to root the next partial evaluation. More specifically, this ~TimeAnchor closure is the definitive binding between the abstract logical time of the session timeline, and the real wall-clock time forming the deadline for render evaluation.
/***
@@ -2226,9 +2228,9 @@ For this Lumiera design, we could consider making GOP just another raw media dat
 &rarr;see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]]
 
-
+
Each [[Timeline]] has an associated set of global [[pipes|Pipe]] (global busses), similar to the subgroups of a sound mixing desk.
-In the typical standard configuration, there is (at least) a video master and a sound master pipe. Like any pipe, ingoing connections attach to the input side, attached effects form a chain, where the last node acts as exit node. The ~Pipe-ID of such a global bus can be used to route media streams, allowing the global pipe to act as a summation bus bar.
+In the typical standard configuration, there is (at least) a video master and a sound master pipe. Like any pipe, ingoing connections attach to the input side, attached effects form a chain, where the last node acts as exit node. The PipeID of such a global bus can be used to route media streams, allowing the global pipe to act as a summation bus bar.
 &rarr; discussion and design rationale of [[global pipes|GlobalPipeDesign]]
 
 !Properties and decisions
@@ -4198,12 +4200,12 @@ DAMAGE.
 
Facility guiding decisions regarding the strategy to employ for rendering or wiring up connections. The PathManager is querried through the OperationPoint, when executing the connection steps within the Build process.
-
+
Pipes play an central role within the Proc Layer, because for everything placed and handled within the session, the final goal is to get it transformed into data which can be retrieved at some pipe's exit port. Pipes are special facilities, rather like inventory, separate and not treated like all the other objects.
 We don't distinguish between "input" and "output" ports &mdash; rather, pipes are thought to be ''hooks for making connections to''. By following this line of thought, each pipe has an input side and an output side and is in itself something like a ''Bus'' or ''processing chain''. Other processing entities like effects and transitions can be placed (attached) at the pipe, resulting them to be appended to form this chain. Likewise, we can place [[wiring requests|WiringRequest]] to the pipe, meaning we want it connected so to send it's output to another destination pipe. The [[Builder]] may generate further wiring requests to fulfil the placement of other entities.
 Thus //Pipes are the basic building blocks// of the whole render network. We distinguish ''global available'' Pipes, which are like the sum groups of a mixing console, and the ''lokal pipe'' or [[source ports|ClipSourcePort]] of the individual clips, which exist only within the duration of the corresponding clip. The design //limits the possible kinds of pipes // to these two types &mdash; thus we can build local processing chains at clips and global processing chains at the global pipes of the session and that's all we can do. (because of the flexibility which comes with the concept of [[placements|Placement]], this is no real limitation)
 
-Pipes are denoted and addressed by ~Pipe-IDs, implemented as ID of a pipe asset. Besides explicitly named pipes, there are some generic placeholder ~IDs for a standard configured pipe of a given type. This is done to avoid creating a separate ~Pipe-ID for each and every clip to build.
+Pipes are denoted and addressed by [[pipe IDs|PipeID]], implemented as ID of a pipe asset. Besides explicitly named pipes, there are some generic placeholder ~IDs for a standard configured pipe of a given type. This is done to avoid creating a separate ~Pipe-ID for each and every clip to build.
 
 While pipes are rather rigid building blocks -- and especially are limited to a single StreamType without conversions, the interconnections or ''routing'' links to the contrary are more flexible. They are specified and controled through the use of an OutputDesignation, which, when fully resolved, should again yield a target ~Pipe-ID to connect. The mentioned resolution of an output designation typically involves an OutputMapping.
 
@@ -4229,6 +4231,14 @@ there is not much you can do directly with a pipe asset. It is an point of refer
 Pipes are integrated with the [[management of defaults|DefaultsManagement]]. For example, any pipe implicitly uses some [[processing pattern|ProcPatt]] &mdash; it may default to the empty pattern. This way, any kind of standard wiring might be applied to the pipes (e.g a fader for audio, similar to the classic mixing consoles). This //is // a global property of the pipe, but &mdash; contrary to the stream type &mdash; this pattern may be switched {{red{really? -- lots of open questions here as of 11/10}}}
 
+
+
A straight processing chain or ''Pipe'' is a core concept in Lumiera's HighLevelModel. Like most of the other building blocks in that model, pipes are represented in the asset view, which leads to having unique pipe-asset ~IDs for all pipes encountered within the model. Now, because of the special building pattern used within that model, which is comprised of pipes and flexible interconnections, the routing and wiring can be done in terms of pipe-~IDs, using them as a short representation of an OutputDesignation. As a consequence, by these relations, the pipe-~IDs are promoted to an universal key for wiring, routing and output targets, usable from the high-level objects down to the low level nodes within the engine.
+* pipe-~IDs are used to label the global busses
+* pipe-~IDs are used to denote output designations for routing
+* pipe-~IDs are attached to the real output possibilities, available when resolving such a designation
+* pipe-~IDs serve to denote the ExitNode corresponding to a global pipe within a single segment of the low-level model
+* pipe-~IDs consequently can be used to address a ModelPort, corresponding both to such a global bus and the corresponding exit nodes
+
A Placement represents a //relation:// it is always linked to a //Subject// (this being a [[Media Object|MObject]]) and has the meaning to //place// this Subject in some manner, either relatively to other Media Objects, by some Constraint or simply absolute at (time, output). The latter case is especially important for the build process and thus represented by a special [[Sub-Interface ExplicitPlacement|ExplicitPlacement]]. Besides this simple cases, Placements can also express more specific kinds of "locating" an object, like placing a sound source at a pan position or placing a video clip at a given layer (above or below another video clip)
 
@@ -7249,7 +7259,7 @@ Thus the possibly mutalble time entities get an {{{accept(time::Mutation&)}}
 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.
 
-
+
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.
@@ -7288,10 +7298,10 @@ Based on this time::Mutation design, we provide a specialised element for dealin
 :Quantisation also is the foundation of all kinds of formalised time specifications
 :actually even a frame count is some kind of (informal) timecode -- other timecodes employ a standardised format.
 ://every// presentation of time values and every persistent storage and exchange of such values is based on time codes.
-:but quantisation and time code aren't identical: a given quantised time value typically can be cast into multiple timecode formats.
+:yet quantisation and time code aren't identical: a given quantised time value typically can be cast into multiple timecode formats.
 
 !Patterns for handling quantised time
-When it comes to actually handling quantised time values, several patterns are conceivable for dealing with the quantisation operation and representing quantised data. As guideline for judging these patterns, the general properties of time quantisation, as detailed above, should be taken into account. Quantising a time value means both //discarding information,// while at the same time //adding information// pertaining the assumptions of the context.
+When it comes to actually handling quantised time values, several patterns are conceivable for dealing with the quantisation operation and representing quantised data. As guideline for judging these patterns, the general properties of time quantisation, as detailed above, should be taken into account. Quantising a time value means both //discarding information,// while at the same time //adding explicit information// pertaining the assumptions of the context.
 
 __casual handling__: this is rather an frequently encountered ''anti pattern''. When reading such code, most striking is the sense of general unawareness of the problem, which is then "discovered" on a per case base, which leads to numerous repetitions of the same basic undertakings, but done with individual treatment of each instance (not so much copy-n-paste). Typical code smells:
 * the rounding, modulo and subtract-base operations pertinent with scale handling are seemingly inserted as bugfix
@@ -7308,9 +7318,9 @@ __tagged values__: quantised values are explicitly created out of continuous val
 __delayed quantisation__: with this approach, the information loss is delayed as long as possible. Quantised time values are rather treated as promise for quantisation, while the actual time data remains unaltered. Additionally, they carry a tag, or even a direct link to the responsible quantiser instance. Effectively, these are specialised time values, instances of a sub-concept, able to stand-in for general time values, but exposing additional accessors to get a quantised value.
 
 !!!discussion
-For Lumiera, the static typing approach is of limited value -- it excels when values belonging to different scales are actually treated differently. There are such cases, but rather on the data handling level, e.g. sound samples are always handled block wise. But regarding time values, the unifying aspect is more important, which leads to prefering a dynamic (run time typed) approach, while //erasing// the special differences most of the time. Yet the dynamic and open nature of the Lumiera high-level model favours the delayed quantisation pattern; the same values may require different quantisation depending on the larger model context an object is encountered in. This solution might be to general and heavy weight at times though. Thus, for important special cases, the accessors should return tagged values, preferably even with differing static type. Time codes can be integrated this way, but most notably the ''frame numbers'' used for addressing throughout the backend, can be implemented as such specifically typed tagged values; the tag here denotes the quantiser and thus the underlying grid -- it should be implemented as hash-ID for smooth integration with code written in plain C.
+For Lumiera, the static typing approach is of limited value -- it excels when values belonging to different scales are actually treated differently. There are such cases, but rather on the data handling level, e.g. sound samples are always handled block wise. But regarding time values, the unifying aspect is more important, which leads to prefering a dynamic (run time typed) approach, while //erasing// the special differences most of the time. Yet the dynamic and open nature of the Lumiera high-level model favours the delayed quantisation pattern; the same values may require different quantisation depending on the larger model context an object is encountered in. This solution might be too general and heavy weight at times though. Thus, for important special cases, the accessors should return tagged values, preferably even with differing static type. Time codes can be integrated this way, but most notably the ''frame numbers'' used for addressing throughout the backend, can be implemented as such specifically typed tagged values; the tag here denotes the quantiser and thus the underlying grid -- it should be implemented as hash-ID for smooth integration with code written in plain C.
 
-At the level of individual timecode formats, we're lacking a common denominator; thus it is preferrable to work with different concrete timecode classes through //generic programming.// This way, each timecode format can expose operations specific only to the given format. Especially, different timecode formats expose different //component fields,// modelled by the generic ''Digxel'' concept. There is a common baseclass ~TCode though, which can be used for //type erasure.//
+At the level of individual timecode formats, we're lacking a common denominator; thus it is preferrable to work with different concrete timecode classes through //generic programming.// This way, each timecode format can expose operations specific only to the given format. Especially, different timecode formats expose different //component fields,// modelled by the generic ''Digxel'' concept. There is a common baseclass ~TCode though, which can be used as marker or for //type erasure.//
 &rarr; more on [[usage situations|TimeUsage]]
 &rarr; Timecode [[format and quantisation|TimecodeFormat]]
 &rarr; Quantiser [[implementation details|QuantiserImpl]]
@@ -7373,7 +7383,7 @@ The way time value and quantisation handling is designed in Lumiera creates a ty
 * how to design the relation of Timecode, Timecode formatting and Quantisation &rarr; [[more here|TimecodeFormat]]
 
-
+
The handling of [[Timecode]] is closely related to [[time representation and quantisation|TimeQuant]]. In fact, these two topics blend into one another. Time will be quantised into a //grid,// but this grid only makes sense when linked to a externally relevant meaning and representation, which is the Timecode. But a timecode value also denotes a specific point in time -- performing operations on a timecode is equivalent to manipulating a quantised time value.
 
 !Problem of dependencies
@@ -7390,7 +7400,7 @@ In this handling sequence, the ~QuTime with the embedded link to a ''quantiser''
 Timecode //is a value.// It has an explicit and distinct structure, and the component parts can be accessed as plain numeric values, or in a formatted string representation. To this end, these values need to be (re)-built. This operation -- building the value(s) -- is what effectively //materialises// the quantisation: At this point, the actual rounding and truncating operation is performed.
 
 But timecode values can also be //mutated.// In this respect they differ from a plain ~TimeValue, which is immutable like a number.
-* the individual components (digxels) can be assigned, causing rebuilding of the timecode
+* the individual components (digxels) can be assigned, causing the rebuilding of the timecode
 * the value as a whole can be incremented, decremented or be offset.
 
 And last but not least, it is possible to get a new ~TimeValue, reflecting the current (quantised) time of this timecode. This closes the circle of time handling and quantisation; actually this is the usual way to enter a new time value into the system, by starting out from an explicitly specified grid aligned timecode value.

From a4e33833679aacfe8921193bb2ccf47db4b24734 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 4 Feb 2012 22:20:21 +0100
Subject: [PATCH 429/496] turn Dispatcher into an interface

---
 src/proc/engine/dispatch-table.cpp            |  38 ++++++
 src/proc/engine/dispatch-table.hpp            |  63 ++++++++++
 src/proc/engine/dispatcher.cpp                |   3 +
 src/proc/engine/dispatcher.hpp                |  14 +--
 src/proc/engine/nodeoperation.hpp             |   2 +-
 src/proc/engine/nodewiring.cpp                |   4 +-
 src/proc/engine/procnode.hpp                  |  20 +++-
 .../proc/engine/dispatcher-interface-test.cpp | 109 ++++++++++++++++++
 wiki/renderengine.html                        |   8 +-
 9 files changed, 242 insertions(+), 19 deletions(-)
 create mode 100644 src/proc/engine/dispatch-table.cpp
 create mode 100644 src/proc/engine/dispatch-table.hpp
 create mode 100644 tests/components/proc/engine/dispatcher-interface-test.cpp

diff --git a/src/proc/engine/dispatch-table.cpp b/src/proc/engine/dispatch-table.cpp
new file mode 100644
index 000000000..7b5a5bb82
--- /dev/null
+++ b/src/proc/engine/dispatch-table.cpp
@@ -0,0 +1,38 @@
+/*
+  DispatchTable  -  implementation of frame job creation
+
+  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.
+
+* *****************************************************/
+
+
+#include "proc/engine/dispatch-table.hpp"
+//#include "lib/frameid.hpp"
+//#include "proc/state.hpp"
+
+
+
+namespace proc {
+namespace engine {
+  
+  /** */
+  
+  
+  
+  
+}} // namespace engine
diff --git a/src/proc/engine/dispatch-table.hpp b/src/proc/engine/dispatch-table.hpp
new file mode 100644
index 000000000..1c428542c
--- /dev/null
+++ b/src/proc/engine/dispatch-table.hpp
@@ -0,0 +1,63 @@
+/*
+  DISPATCH-TABLE.hpp  -  implementation of frame job creation
+
+  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.
+
+*/
+
+
+#ifndef PROC_ENGINE_DISPATCH_TABLE_H
+#define PROC_ENGINE_DISPATCH_TABLE_H
+
+#include "proc/common.hpp"
+#include "proc/engine/dispatcher.hpp"
+
+
+
+namespace proc {
+namespace engine {
+  
+  using lib::time::TimeSpan;
+  using lib::time::FSecs;
+  using lib::time::Time;
+//  
+//  class ExitNode;
+  
+  /**
+   * @todo 11/11 extremely fuzzy at the moment
+   */
+  class DispatchTable
+    : public Dispatcher
+    {
+    protected:
+      /** timerange covered by this RenderGraph */
+      TimeSpan segment_;
+      
+    public:
+      DispatchTable()
+        : segment_(Time::ZERO, FSecs(5))
+        {
+          UNIMPLEMENTED ("anything regarding the Engine backbone");
+        }
+      
+    };
+  
+  
+  
+}} // namespace proc::engine
+#endif
diff --git a/src/proc/engine/dispatcher.cpp b/src/proc/engine/dispatcher.cpp
index 40f5e3c53..ef148d4b5 100644
--- a/src/proc/engine/dispatcher.cpp
+++ b/src/proc/engine/dispatcher.cpp
@@ -29,6 +29,9 @@
 
 namespace proc {
 namespace engine {
+
+  Dispatcher::~Dispatcher() { }  // emit VTable here....
+  
   
   /** */
   
diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp
index d96c40e96..90265c5b4 100644
--- a/src/proc/engine/dispatcher.hpp
+++ b/src/proc/engine/dispatcher.hpp
@@ -28,6 +28,8 @@
 #include "proc/state.hpp"
 #include "lib/time/timevalue.hpp"
 
+#include 
+
 
 
 namespace proc {
@@ -43,17 +45,13 @@ namespace engine {
    * @todo 11/11 extremely fuzzy at the moment
    */
   class Dispatcher
+    : boost::noncopyable
     {
-    protected:
-      /** timerange covered by this RenderGraph */
-      TimeSpan segment_;
       
     public:
-      Dispatcher()
-        : segment_(Time::ZERO, FSecs(5))
-        {
-          UNIMPLEMENTED ("anything regarding the Engine backbone");
-        }
+      virtual ~Dispatcher();  ///< this is an interface
+      
+      
       
     };
   
diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp
index 7e48b5763..26031d989 100644
--- a/src/proc/engine/nodeoperation.hpp
+++ b/src/proc/engine/nodeoperation.hpp
@@ -106,7 +106,7 @@ namespace config {
       bool
       validateBuffers (Invocation& ivo)
         {
-          UNIMPLEMENTED ("Do a final, specifically tailored validation step on the buffers prior to invoking the procees function");
+          UNIMPLEMENTED ("Do a final, specifically tailored validation step on the buffers prior to invoking the process function");
         }
     };
   
diff --git a/src/proc/engine/nodewiring.cpp b/src/proc/engine/nodewiring.cpp
index 6ffa2b7be..47b4acab9 100644
--- a/src/proc/engine/nodewiring.cpp
+++ b/src/proc/engine/nodewiring.cpp
@@ -117,8 +117,8 @@ namespace engine {
    *  for the specific wiring situations) use the AllocationCluster
    *  of the current build process, we need to create a new instance
    *  for each newly built segment of the low-level model.
-   *  @note As this ctor creates a WiringFactoryImpl instance, 
-   *        compiling this invocation actually drives the necessary
+   *  @note This ctor creates a WiringFactoryImpl instance, thus 
+   *        compiling this definition actually drives the necessary
    *        template instantiations for all cases encountered while
    *        building the node network.
    */
diff --git a/src/proc/engine/procnode.hpp b/src/proc/engine/procnode.hpp
index 47d7a6d75..5e0e4167b 100644
--- a/src/proc/engine/procnode.hpp
+++ b/src/proc/engine/procnode.hpp
@@ -50,6 +50,7 @@
 #include "lib/ref-array.hpp"
 
 #include 
+#include 
 
 
 
@@ -67,10 +68,17 @@ namespace engine {
   /**
    * Interface: Description of the input and output ports,
    * processing function and predecessor nodes for a given ProcNode.
+   * 
+   * @todo the design of this part is messy in several respects.
+   *       Basically, its left-over from a first prototypical implementation from 2008
+   *       As of 1/2012, we're re-shaping that engine interface and invocation with a top-down approach,
+   *       starting from the player. Anyhow, you can expect the basic setup to remain as-is: there will
+   *       be a ProcNode and a WiringDescriptor, telling how it's connected to its predecessors, and
+   *       defining how the Node is supposed to operate
    */
   class WiringDescriptor
     {
-    public:
+    public: /* === public information record describing the node graph === */
       uint nrO;
       uint nrI;
       
@@ -97,6 +105,11 @@ namespace engine {
           nrI = in.size();
         }
       
+      
+      /* ==== strategy API for configuring the node operation ==== */
+      
+      friend class ProcNode;                               /////////////////////////////////TODO 1/12 : wouldn't it be better to extract that API into a distinct strategy?
+      
       /** the wiring-dependent part of the node operation.
        *  Includes the creation of a one-way state object on the stack
        *  holding the actual buffer pointers and issuing the recursive pull() calls
@@ -105,8 +118,6 @@ namespace engine {
       virtual BuffHandle
       callDown (State& currentProcess, uint requiredOutputNr)  const =0; 
       
-      friend class ProcNode;
-      
     };
   
   
@@ -120,9 +131,10 @@ namespace engine {
    *       several query/information functions. In that case, the ctor will become protected.
    *       The alternative would be to push down these information-retrieval part into a
    *       configurable element within WiringDescriptor, in which case we even might drop
-   *       ProcNode as a frontent entirely.
+   *       ProcNode as a frontend entirely.
    */
   class ProcNode
+    : boost::noncopyable
     {
       typedef mobject::Parameter Param;   //////TODO: just a placeholder for automation as of 6/2008
       vector params;
diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp
new file mode 100644
index 000000000..bb5bd9bff
--- /dev/null
+++ b/tests/components/proc/engine/dispatcher-interface-test.cpp
@@ -0,0 +1,109 @@
+/*
+  DispatcherInterface(Test)  -  document and verify dispatcher for frame job creation
+
+  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.
+
+* *****************************************************/
+
+
+#include "lib/test/run.hpp"
+#include "lib/error.hpp"
+
+//#include "proc/engine/procnode.hpp"
+#include "proc/engine/dispatcher.hpp"
+
+//#include 
+//#include 
+
+using test::Test;
+//using std::cout;
+//using std::rand;
+
+
+namespace proc {
+namespace engine{
+namespace test  {
+
+  namespace { // used internally
+    
+    
+    class MockDispatcherTable
+      : public Dispatcher
+      {
+        
+      public:
+      };
+    
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #890
+      
+  } // (End) internal defs
+  
+  
+  
+  /*******************************************************************
+   * @test document and verify the engine::Dispatcher interface, used
+   *       to translate a CalcStream into individual node jobs.
+   *       This test covers the definition of the interface itself,
+   *       together with the supporting types and the default
+   *       implementation of the basic operations.
+   *       It creates and uses  a mock Dispatcher implementation.
+   */
+  class DispatcherInterface_test : public Test
+    {
+      
+      virtual void
+      run (Arg) 
+        {
+           verify_basicDispatch();
+           verify_standardDispatcherUsage();
+        }
+      
+      
+      /** @test perform the basic dispatch step
+       * and verify the generated frame coordinates
+       */
+      void
+      verify_basicDispatch()
+        {
+          
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #890
+        }
+      
+      
+      /** @test the standard invocation sequence
+       * used within the engine for planning new jobs.
+       * The actual implementation is mocked.
+       */
+      void
+      verify_standardDispatcherUsage()
+        {
+          
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #890
+        }
+    };
+  
+  
+  /** Register this test class... */
+  LAUNCHER (DispatcherInterface_test, "unit engine");
+  
+  
+  
+}}} // namespace proc::engine::test
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index bfd35cd22..559293a3d 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1692,11 +1692,11 @@ Taken precisely, the "degree of constriction" yields only a partial or
 As we don't have a Prolog interpreter on board yet, we utilize a mock store with preconfigured answers. (see MockConfigQuery). As this preliminary solution is lacking the ability to create new objects, we need to resort to some trickery here (please look away). The overall logic is quite broken, not to say outright idiotic, because the system isn't capable to do any real resolution &mdash; if we ignore this fact, the rest of the algorithm can be implemented, tested and used right now.
 
-
+
For several components and properties there is an implicit default value or configuration; it is stored alongside with the session. The intention is that defaults never create an error, instead, they are to be extended silently on demand. Objects configured according to these defaults can be retrieved at the [[Session]] interface by a set of overloaded functions {{{Session::current->default(Query<TYPE> ("query string"))}}}, where the //query string // defines a capability query similar to what is employed for pipes, stream types, codecs etc. This query mechanism is implemented by [[configuration rules|ConfigRules]]
 
 !!!!what is denoted by {{{default}}}?
-{{{default(Obj)}}} is a predicate expressing that the object {{{Obj}}} can be considered the default setup under the given conditions. Using the //default// can be considered as a shortcut for actually finding a exact and unique solution. The latter would require to specify all sorts of detailed properties up to the point where only one single object can satisfy all conditions. On the other hand, leaving some properties unspecified would yield a set of solutions (and the user code issuing the query had to provide means for selecting one soltution from this set). Just falling back on the //default// means that the user code actually doesn't care for any additional properties (as long as the properties he //does// care for are satisfied). Nothing is said specifically on //how//&nbsp; this default gets configured; actually there can be rules //somewhere,// and, additionally, anything encountered once while asking for a default can be re-used as default under similar circumstances.
+{{{default(Obj)}}} is a predicate expressing that the object {{{Obj}}} can be considered the default setup under the given conditions. Using the //default// can be considered as a shortcut for actually finding an exact and unique solution. The latter would require to specify all sorts of detailed properties up to the point where only one single object can satisfy all conditions. On the other hand, leaving some properties unspecified would yield a set of solutions (and the user code issuing the query had to provide means for selecting one solution from this set). Just falling back on the //default// means that the user code actually doesn't care for any additional properties (as long as the properties he //does// care for are satisfied). Nothing is said specifically on //how//&nbsp; this default gets configured; actually there can be rules //somewhere,// and, additionally, anything encountered once while asking for a default can be re-used as default under similar circumstances.
 &rarr; [[implementing defaults|DefaultsImplementation]]
@@ -5776,7 +5776,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,...}}}).
@@ -5789,7 +5789,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
-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...
+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, data reduced ~MP3 versus full quality linear PCM...
 
From 2cb254365cc95faf2226f964662ba302d37f125a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 5 Feb 2012 00:25:22 +0100 Subject: [PATCH 430/496] some musing about timing constraints and quantisation --- src/lib/time/digxel.hpp | 2 +- src/lib/time/timecode.hpp | 2 +- src/proc/play/play-service.cpp | 2 +- tests/46engine.tests | 4 + tests/47playout.tests | 5 ++ .../proc/engine/dispatcher-interface-test.cpp | 22 +++++- .../proc/play/timing-constraints-test.cpp | 77 +++++++++++++++++++ wiki/renderengine.html | 18 ++++- 8 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 tests/components/proc/play/timing-constraints-test.cpp diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index d49e81af4..964985c3e 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -317,7 +317,7 @@ namespace time { typedef Digxel< int, digxel::SexaFormatter> SexaDigit; ///< for displaying time components (sexagesimal) typedef Digxel HexaDigit; ///< for displaying a hex byte typedef Digxel< int, digxel::HourFormatter> HourDigit; ///< for displaying hours in H:M.S - typedef Digxel CountVal; ///< for displaying a counter + typedef Digxel CountVal; ///< for displaying a counter ///////////TICKET #882 : outch! /** special Digxel to show a sign. diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index fafe52d9c..f81df7c7f 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -103,7 +103,7 @@ namespace time { FrameNr (QuTime const& quantisedTime); using TCode::operator string; - // CountVal implicitly convertible to long + // CountVal implicitly convertible to long ///////////TICKET #882 : outch! should be a 64bit type! }; diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index 6a32ed2c5..2c359c08e 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -178,7 +178,7 @@ namespace play { * * 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 + * through the given OutputManager. The goal is to configure a * 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 diff --git a/tests/46engine.tests b/tests/46engine.tests index b0de4f200..f5653aab3 100644 --- a/tests/46engine.tests +++ b/tests/46engine.tests @@ -39,6 +39,10 @@ PLANNED "Engine calculation streams" CalcStream_test < //#include @@ -38,7 +42,11 @@ using test::Test; namespace proc { namespace engine{ namespace test { - + + using lib::time::QuTime; + using lib::time::FrameRate; + using proc::play::Timings; + namespace { // used internally @@ -49,6 +57,8 @@ namespace test { public: }; + lib::Singleton mockDispatcher; + #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880 #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #890 @@ -81,9 +91,13 @@ namespace test { void verify_basicDispatch() { - + Dispatcher& dispatcher = mockDispatcher(); #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880 -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #890 + Timings timings (FrameRate::PAL); + uint startFrame(10); + + TimeAnchor refPoint = TimeAnchor::build (timings, startFrame); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880 } @@ -96,7 +110,7 @@ namespace test { { #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880 -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #890 +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880 } }; diff --git a/tests/components/proc/play/timing-constraints-test.cpp b/tests/components/proc/play/timing-constraints-test.cpp new file mode 100644 index 000000000..d11fdde2e --- /dev/null +++ b/tests/components/proc/play/timing-constraints-test.cpp @@ -0,0 +1,77 @@ +/* + TimingConstraints(Test) - verify timing constraint specifications + + 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. + +* *****************************************************/ + + +#include "lib/test/run.hpp" + +#include "proc/play/timings.hpp" +//#include "proc/engine/buffhandle.hpp" +//#include "proc/engine/testframe.hpp" +//#include "lib/time/control.hpp" + + + +namespace proc { +namespace play { +namespace test { + + namespace time = lib::time; + +//using proc::engine::BuffHandle; +//using proc::engine::test::testData; +//using proc::engine::test::TestFrame; + +//using lumiera::Play; + +//typedef time::Control DurationControl; +// +//typedef DummyPlayConnection DummyPlayer; + + + /******************************************************************* + * @test document and verify the definition and combining of + * timing constraints for playback and rendering. + */ + class TimingConstraints_test : public Test + { + virtual void + run (Arg) + { + define_basicTimingConstraints(); + } + + + void + define_basicTimingConstraints() + { +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #831 +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #831 + } + }; + + + /** Register this test class... */ + LAUNCHER (TimingConstraints_test, "unit player"); + + + +}}} // namespace proc::play::test diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 559293a3d..990184f0e 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3476,7 +3476,7 @@ Thus the mapping is a copyable value object, using an associative array. It may 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 (video in GUI window, video fullscreen, sound, Jack, rendering to file) can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3486,7 +3486,7 @@ 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. (in special cases, some 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)
 
 [>img[Outputslot implementation structures|uml/fig151685.png]]
-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 are 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. Based on this information, 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 are 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|Timings]] when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Based on this information, 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.
 
 !!!usage and implementation
 Clients retrieve just a reference to an output slot by asking a suitable OutputManager for an output possibility supporting a specific format. Usually, they just "claim" this slot by invoking {{{allocate()}}}, which behind the scenes causes building of the actual output connections and mechanisms. For each such connection -- corresponding to a single channel within the media format handled by this ~OutputSlot -- the client gets a smart-handle {{{DataSink}}}. The concrete ~OutputSlot implementation performs operations quite specific to the kind of output and external interface in question. All tese specific handling is embodied within the concrete connection implementation used by the concrete ~OutputSlot
@@ -7466,6 +7466,20 @@ The Timeline owns a list of global [[pipes (busses)|Pipe]] which are used to col
 Actually, what the GUI creates and uses is the //view// of a given timeline. This makes no difference to start with, as the view is modelled to be a sub-concept of "timeline" and thus can stand-in. All different views of the //same// timeline also share one single play control instance, i.e. they all have one single playhead position. Doing it this way should be the default, because it's the least confusing. Anyway, it's also possible to create multiple //independent timelines// &mdash; in an extreme case even so when referring to the same top-level sequence. This configuration gives the ability to play the same arrangement in parallel with multiple independent play controllers (and thus independent playhead positions)
 
 To complement this possibilities, I'd propose to give the //timeline view// the possibility to be re-linked to a sub-sequence. This way, it would stay connected to the main play control, but at the same time show a sub-sequence //in the way it will be treated as  embedded// within the top-level sequence. This would be the default operation mode when a meta-clip is opened (and showed in a separate tab with such a linked timeline view). The reason for this proposed handling is again to give the user the least surprising behaviour. Because, when &mdash; on the contrary &mdash; the sub-sequence would be opened as //separate timeline,// a different absolute time position and a different signal routing may result; doing such should be reserved for advanced use, e.g. when multiple editors cooperate on a single project and a sequence has to be prepared in isolation prior to being integrated in the global sequence (featuring the whole movie).
+
+
+
+
//Timing constraints of a render or playback process.//
+
+!design quest {{red{WIP 1/2012}}}
+The Timings record is used at various places at the engine interface level. Up to now mostly as placeholder for future work.
+
+* it is clear that a concrete frame grid needs to be part of the timings.
+* this defines time origin and framerate
+* additionally it's clear that the Timings record represents a //constraint// -- with the ability to specify additional constraining conditions
+* besides, it's clear that the Timings record is consulted to find out about latency
+* question: do we need a relation to wall-clock time?
+
 
From 157e3b6867e1df8c0d0bdb14f66baec816ca7004 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 6 Feb 2012 02:49:54 +0100 Subject: [PATCH 431/496] test-driven brainstorming: define default timings... --- src/proc/play/output-slot.hpp | 1 + src/proc/play/play-service.cpp | 2 +- src/proc/play/timings.cpp | 65 +++++++++++++++++++ src/proc/play/timings.hpp | 28 +++++++- .../proc/engine/engine-interface-test.cpp | 4 +- .../proc/play/timing-constraints-test.cpp | 11 +++- tests/lib/time/time-quantisation-test.cpp | 2 +- wiki/renderengine.html | 6 +- 8 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 src/proc/play/timings.cpp diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index 860232b1f..3b34a1fa1 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -83,6 +83,7 @@ namespace play { using proc::engine::BuffHandle; using proc::engine::BufferProvider; using lib::time::TimeValue; + using lib::time::Time; using boost::scoped_ptr; diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index 2c359c08e..580b0e6a4 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -187,7 +187,7 @@ namespace play { Play::Controller PlayService::connect (ModelPorts dataGenerators, POutputManager outputPossibilities) { - Timings playbackTimings; //////////////////////////////////////////////////////////////////////TICKET #875 + Timings playbackTimings (lib::time::FrameRate::PAL); //////////////////////////////////////////////////////TICKET #875 return pTable_->establishProcess( PlayProcess::initiate(dataGenerators, diff --git a/src/proc/play/timings.cpp b/src/proc/play/timings.cpp new file mode 100644 index 000000000..d5e1dd7b1 --- /dev/null +++ b/src/proc/play/timings.cpp @@ -0,0 +1,65 @@ +/* + Timings - timing specifications for a frame quantised data stream + + 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. + +* *****************************************************/ + + +#include "proc/play/timings.hpp" +#include "lib/time/formats.hpp" +#include "lib/time/timequant.hpp" + + + +namespace proc { +namespace play { + + + using lib::time::PQuant; + + namespace { // hidden local details of the service implementation.... + + inline PQuant + buildStandardGridForFramerate (FrameRate fps) + { + return PQuant (new lib::time::FixedFrameQuantiser (fps)); + } //////TODO maybe caching these quantisers? they are immutable and threadsafe + + + } // (End) hidden service impl details + + + /** Create a default initialised Timing constraint record. + * Using the standard optimistic settings for most values, + * no latency, no special requirements. The frame grid is + * rooted at the "natural" time origin; it is not related + * in any way to the current session. + * @remarks this ctor is intended rather for testing purposes! + * Usually, when creating a play/render process, + * the actual timings \em are related to the timeline + * and the latency/speed requirements of the output. + */ + Timings::Timings (FrameRate fps) + : grid_(buildStandardGridForFramerate(fps)) + , playbackUrgency (ASAP) + { } + + + +}} // namespace proc::play diff --git a/src/proc/play/timings.hpp b/src/proc/play/timings.hpp index d40e23a54..3ce908826 100644 --- a/src/proc/play/timings.hpp +++ b/src/proc/play/timings.hpp @@ -53,20 +53,30 @@ //#include //#include //#include -//#include +#include //#include +namespace lib { +namespace time{ + class Quantiser; +}} namespace proc { namespace play { - using lib::time::Time; + using lib::time::FrameRate; + using lib::time::TimeValue; //using std::string; //using std::vector; //using std::tr1::shared_ptr; //using boost::scoped_ptr; + enum PlaybackUrgency { + ASAP, + NICE, + TIMEBOUND + }; @@ -80,7 +90,21 @@ namespace play { */ class Timings { + typedef std::tr1::shared_ptr PQuant; + + PQuant grid_; + public: + PlaybackUrgency playbackUrgency; + + Timings (FrameRate fps); + + // default copy acceptable + + TimeValue getOrigin(); + + FrameRate getFrameRate(); + //////////////TODO further accessor functions here diff --git a/tests/components/proc/engine/engine-interface-test.cpp b/tests/components/proc/engine/engine-interface-test.cpp index 0345b0334..7f7b6ea59 100644 --- a/tests/components/proc/engine/engine-interface-test.cpp +++ b/tests/components/proc/engine/engine-interface-test.cpp @@ -31,6 +31,7 @@ #include "proc/play/diagnostic-output-slot.hpp" #include "proc/mobject/model-port.hpp" #include "proc/asset/pipe.hpp" +#include "lib/time/timevalue.hpp" //#include //#include @@ -50,6 +51,7 @@ namespace test { using mobject::ModelPort; using proc::play::OutputSlot; using proc::play::DiagnosticOutputSlot; + using lib::time::FrameRate; typedef asset::ID PID; typedef OutputSlot::Allocation Allocation; @@ -94,7 +96,7 @@ namespace test { OutputSlot& oSlot = DiagnosticOutputSlot::build(); Allocation& output = oSlot.allocate(); - Timings timings; /////////TODO + Timings timings (FrameRate::PAL); /////////TODO // Invoke test subject... CalcStreams calc = engine.calculate(port, timings, output); diff --git a/tests/components/proc/play/timing-constraints-test.cpp b/tests/components/proc/play/timing-constraints-test.cpp index d11fdde2e..16e8fbcc5 100644 --- a/tests/components/proc/play/timing-constraints-test.cpp +++ b/tests/components/proc/play/timing-constraints-test.cpp @@ -24,6 +24,7 @@ #include "lib/test/run.hpp" #include "proc/play/timings.hpp" +#include "lib/time/timevalue.hpp" //#include "proc/engine/buffhandle.hpp" //#include "proc/engine/testframe.hpp" //#include "lib/time/control.hpp" @@ -34,8 +35,9 @@ namespace proc { namespace play { namespace test { - namespace time = lib::time; - + using lib::time::Time; + using lib::time::FrameRate; + //using proc::engine::BuffHandle; //using proc::engine::test::testData; //using proc::engine::test::TestFrame; @@ -63,7 +65,12 @@ namespace test { void define_basicTimingConstraints() { + Timings timings (FrameRate::PAL); + #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #831 + CHECK (Time::ZERO == timings.getOrigin()); + CHECK (FrameRate::PAL == timings.getFrameRate()); + CHECK (ASAP == timings.playbackUrgency); #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #831 } }; diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp index 32b57a355..9cda288d3 100644 --- a/tests/lib/time/time-quantisation-test.cpp +++ b/tests/lib/time/time-quantisation-test.cpp @@ -154,7 +154,7 @@ namespace test{ checkGridBinding (TimeValue org) { // refer to a grid not yet defined - VERIFY_ERROR (UNKNOWN_GRID, QuTime wired(org, "special_funny_grid")); + VERIFY_ERROR (UNKNOWN_GRID, QuTime weird(org, "special_funny_grid")); TimeGrid::build("special_funny_grid", 1); // provide the grid's definition (1 frame per second) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 990184f0e..d47cf0f25 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -7468,7 +7468,7 @@ Actually, what the GUI creates and uses is the //view// of a given timeline. Thi To complement this possibilities, I'd propose to give the //timeline view// the possibility to be re-linked to a sub-sequence. This way, it would stay connected to the main play control, but at the same time show a sub-sequence //in the way it will be treated as embedded// within the top-level sequence. This would be the default operation mode when a meta-clip is opened (and showed in a separate tab with such a linked timeline view). The reason for this proposed handling is again to give the user the least surprising behaviour. Because, when &mdash; on the contrary &mdash; the sub-sequence would be opened as //separate timeline,// a different absolute time position and a different signal routing may result; doing such should be reserved for advanced use, e.g. when multiple editors cooperate on a single project and a sequence has to be prepared in isolation prior to being integrated in the global sequence (featuring the whole movie).
-
+
//Timing constraints of a render or playback process.//
 
 !design quest {{red{WIP 1/2012}}}
@@ -7480,7 +7480,9 @@ The Timings record is used at various places at the engine interface level. Up t
 * besides, it's clear that the Timings record is consulted to find out about latency
 * question: do we need a relation to wall-clock time?
 
-
+!where are the timings definied? +Timing constraint records are used at various places within the engine. Basically we need such a timings record for starting any kind of playback or render. +The effective timings are established when //allocating an OutputSlot// -- based on the timings defined for the ModelPort to //perform,// i.e. the global bus to render.
Tracks are just a structure used to organize the Media Objects within the Sequence. Tracks are associated allways to a specific Sequence and the Tracks of an Sequence form a //tree.// They can be considered to be an organizing grid, and besides that, they have no special meaning. They are grouping devices, not first-class entities. A track doesn't "have" a port or pipe or "is" a video track and the like; it can be configured to behave in such manner by using placements.

From 6772e94994963b6ae90da737ea22f674f1b2e6c1 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 9 Feb 2012 04:52:09 +0100
Subject: [PATCH 432/496] implement simple constant frame timings descriptor

---
 src/proc/play/timings.cpp                     | 43 ++++++++++++++++++-
 src/proc/play/timings.hpp                     | 18 +++++---
 .../proc/play/timing-constraints-test.cpp     | 11 +++--
 3 files changed, 63 insertions(+), 9 deletions(-)

diff --git a/src/proc/play/timings.cpp b/src/proc/play/timings.cpp
index d5e1dd7b1..956b5f72a 100644
--- a/src/proc/play/timings.cpp
+++ b/src/proc/play/timings.cpp
@@ -32,6 +32,7 @@ namespace play {
   
   
   using lib::time::PQuant;
+  using lib::time::Time;
   
   namespace { // hidden local details of the service implementation....
     
@@ -58,7 +59,47 @@ namespace play {
   Timings::Timings (FrameRate fps)
     : grid_(buildStandardGridForFramerate(fps))
     , playbackUrgency (ASAP)
-    { }
+    { 
+      ENSURE (grid_);
+    }
+  
+  
+  
+  TimeValue
+  Timings::getOrigin()  const
+  {
+    return grid_->timeOf(0);
+  }
+  
+  
+  Offset
+  Timings::getFrameOffsetAt (TimeValue refPoint)  const
+  {
+    return 1 * getFrameDurationAt (refPoint);      /////////////////TODO implement a speed factor here
+  }
+  
+  
+  Duration
+  Timings::getFrameDurationAt (TimeValue refPoint)  const
+  {
+    int64_t frameNr = grid_->gridPoint (refPoint);
+    return Offset (grid_->timeOf(frameNr), grid_->timeOf(frameNr+1));
+  }
+  
+  
+  /** @remarks the purpose of this function is to support scheduling
+   *           and frame handling even in case the frame rate isn't constant.
+   *           To indicate the case the frame rate is changing right now,
+   *           this function might returns Duration::NIL
+   *  @todo implement real support for variable frame rates      
+   */                                  ////////////////////////////////////////////////////////TICKET #236
+  Duration
+  Timings::constantFrameTimingsInterval (TimeValue startPoint)  const
+  {
+    return Duration (Time::ANYTIME);
+  }
+
+
 
   
 
diff --git a/src/proc/play/timings.hpp b/src/proc/play/timings.hpp
index 3ce908826..f0ac12112 100644
--- a/src/proc/play/timings.hpp
+++ b/src/proc/play/timings.hpp
@@ -59,6 +59,7 @@
 namespace lib {
 namespace time{
   class Quantiser;
+  typedef std::tr1::shared_ptr PQuant;
 }}
 
 namespace proc {
@@ -66,6 +67,8 @@ namespace play {
 
   using lib::time::FrameRate;
   using lib::time::TimeValue;
+  using lib::time::Duration;
+  using lib::time::Offset;
 //using std::string;
 
 //using std::vector;
@@ -90,9 +93,7 @@ namespace play {
    */
   class Timings
     {
-      typedef std::tr1::shared_ptr PQuant;
-      
-      PQuant grid_;
+      lib::time::PQuant grid_;
       
     public:
       PlaybackUrgency playbackUrgency;
@@ -101,9 +102,16 @@ namespace play {
       
       // default copy acceptable
       
-      TimeValue getOrigin();
+      TimeValue getOrigin()  const;
       
-      FrameRate getFrameRate();
+      Offset   getFrameOffsetAt   (TimeValue refPoint)  const;
+      Duration getFrameDurationAt (TimeValue refPoint)  const;
+      
+      /** the frame spacing and duration remains constant for some time...
+       * @param startPoint looking from that time point into future
+       * @return duration after this starting point, where it's safe
+       *         to assume unaltered frame dimensions */
+      Duration constantFrameTimingsInterval (TimeValue startPoint)  const;
       
       
       //////////////TODO further accessor functions here
diff --git a/tests/components/proc/play/timing-constraints-test.cpp b/tests/components/proc/play/timing-constraints-test.cpp
index 16e8fbcc5..f63b0114d 100644
--- a/tests/components/proc/play/timing-constraints-test.cpp
+++ b/tests/components/proc/play/timing-constraints-test.cpp
@@ -36,6 +36,7 @@ namespace play {
 namespace test {
   
   using lib::time::Time;
+  using lib::time::Duration;
   using lib::time::FrameRate;
 
 //using proc::engine::BuffHandle;
@@ -67,10 +68,14 @@ namespace test {
         {
           Timings timings (FrameRate::PAL);
           
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #831
-          CHECK (Time::ZERO == timings.getOrigin());
-          CHECK (FrameRate::PAL == timings.getFrameRate());
+          Time t(0,10);
+          Duration lengthPAL (1, FrameRate::PAL);
+          
+          CHECK (timings.getOrigin() == Time::ZERO);
+          CHECK (timings.getFrameDurationAt(t) == lengthPAL);
+          CHECK (timings.getFrameOffsetAt(t)   == lengthPAL);
           CHECK (ASAP == timings.playbackUrgency);
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #831
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #831
         }
     };

From 568fadd5265edb366e626cdaa04965ae98ff9652 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 9 Feb 2012 22:24:05 +0100
Subject: [PATCH 433/496] draft some steps of the dispatch operation

---
 src/proc/engine/dispatcher.hpp                |  6 +++++-
 src/proc/engine/frame-coord.hpp               | 15 ++++++++++++--
 src/proc/engine/time-anchor.hpp               | 20 +++++++++++++++----
 .../proc/engine/dispatcher-interface-test.cpp | 15 +++++++++++++-
 4 files changed, 48 insertions(+), 8 deletions(-)

diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp
index 90265c5b4..c828ea260 100644
--- a/src/proc/engine/dispatcher.hpp
+++ b/src/proc/engine/dispatcher.hpp
@@ -25,7 +25,9 @@
 #define PROC_ENGINE_DISPATCHER_H
 
 #include "proc/common.hpp"
-#include "proc/state.hpp"
+//#include "proc/state.hpp"
+#include "proc/engine/time-anchor.hpp"
+#include "proc/engine/frame-coord.hpp"
 #include "lib/time/timevalue.hpp"
 
 #include 
@@ -52,6 +54,8 @@ namespace engine {
       virtual ~Dispatcher();  ///< this is an interface
       
       
+      virtual FrameCoord locateFrameNext (uint frameCountOffset)   =0;
+      
       
     };
   
diff --git a/src/proc/engine/frame-coord.hpp b/src/proc/engine/frame-coord.hpp
index 6ff385790..950874927 100644
--- a/src/proc/engine/frame-coord.hpp
+++ b/src/proc/engine/frame-coord.hpp
@@ -34,8 +34,9 @@
 namespace proc {
 namespace engine {
   
-  using lib::time::TimeSpan;
-  using lib::time::FSecs;
+//using lib::time::TimeSpan;
+  using lib::time::Duration;
+//using lib::time::FSecs;
   using lib::time::Time;
 //  
 //  class ExitNode;
@@ -58,6 +59,9 @@ namespace engine {
     {
       
     public:
+      Time absoluteNominalTime;
+      int64_t absoluteFrameNumber;
+      
       FrameCoord()
         {
           UNIMPLEMENTED ("anything regarding the Node Invocation");
@@ -65,6 +69,13 @@ namespace engine {
       
       // using default copy operations
       
+      
+      Duration remainingRealTime()
+        {
+          UNIMPLEMENTED ("deterine the real wall clock time amount left until deadline");
+        }
+
+      
     };
   
   
diff --git a/src/proc/engine/time-anchor.hpp b/src/proc/engine/time-anchor.hpp
index 4f8c62b16..ad6736252 100644
--- a/src/proc/engine/time-anchor.hpp
+++ b/src/proc/engine/time-anchor.hpp
@@ -27,15 +27,16 @@
 #include "proc/common.hpp"
 //#include "proc/state.hpp"
 #include "lib/time/timevalue.hpp"
+#include "proc/play/timings.hpp"
 
 
 
 namespace proc {
 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;
   
@@ -57,13 +58,24 @@ namespace engine {
   class TimeAnchor
     {
       
-    public:
       TimeAnchor()
         {
           UNIMPLEMENTED ("anything regarding the Engine backbone");
         }
       
+    public:
       // using default copy operations
+      
+      static TimeAnchor
+      build (play::Timings timings, uint64_t startFrame)
+        {
+          UNIMPLEMENTED ("representation of the Time Anchor closure");
+        }
+      
+      operator lib::time::TimeValue()  const
+        {
+          UNIMPLEMENTED ("representation of the Time Anchor closure");
+        }
     };
   
   
diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp
index 6a05f27e0..b30a0293f 100644
--- a/tests/components/proc/engine/dispatcher-interface-test.cpp
+++ b/tests/components/proc/engine/dispatcher-interface-test.cpp
@@ -45,6 +45,7 @@ namespace test  {
   
   using lib::time::QuTime;
   using lib::time::FrameRate;
+  using lib::time::Duration;
   using proc::play::Timings;
   
   namespace { // used internally
@@ -54,6 +55,12 @@ namespace test  {
       : public Dispatcher
       {
         
+        FrameCoord
+        locateFrameNext (uint frameCountOffset)
+          {
+            UNIMPLEMENTED ("dummy implementation of the core dispatch operation");
+          }
+
       public:
       };
     
@@ -92,11 +99,17 @@ namespace test  {
       verify_basicDispatch()
         {
           Dispatcher& dispatcher = mockDispatcher();
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
           Timings timings (FrameRate::PAL);
           uint startFrame(10);
           
           TimeAnchor refPoint = TimeAnchor::build (timings, startFrame);
+          CHECK (refPoint == Time::ZERO + Duration(10, FrameRate::PAL));
+          
+          FrameCoord coordinates = dispatcher.locateFrameNext (15);
+          CHECK (coordinates.absoluteNominalTime == Time(0,1));
+          CHECK (coordinates.absoluteFrameNumber == 25);
+          CHECK (coordinates.remainingRealTime() >= Time(FSecs(24,25)));
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
         }
       

From 23ac29028c650384e52978d041eca3663e684878 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 12 Feb 2012 01:19:37 +0100
Subject: [PATCH 434/496] design draft: job tickets and planning...

---
 wiki/renderengine.html | 61 ++++++++++++++++++++++--------------------
 1 file changed, 32 insertions(+), 29 deletions(-)

diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index d47cf0f25..45636fa78 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1376,7 +1376,7 @@ at the lowest level within the builder there is the step of building a //connect
 * by default, a timeline is outfitted with one video and one sound master bus
 
-
+
Calculation stream is an organisational unit used at the interface level of the Lumiera engine.
 Representing a //stream of calculations,// delivering generated data within //timing constraints,// it is used
 *by the [[play process(es)|PlayProcess]] to define and control properties of the output generation
@@ -1390,9 +1390,11 @@ Any really interesting calculation stream needs to be retrieved from the EngineF
 !purpose
 When a calculation stream is retrieved from the EngineFaçade it is already registered and attached there and represents an ongoing activity. Under the hood, several further collaborators will hold a copy of that calculation stream descriptor. While, as such, a CalcStream has no explicit state, at any time it //represents a current state.// In case the running time span of that stream is limited, it becomes superseded automatically, just by the passing of time.
 
-Each calculation stream refers a relevant [[frame dispatcher table|FrameDispatcher]]. Thus, for the engine (interface level), the calculation stream allows to produce the individual [[render jobs|RenderJob]] to enqueue with the [[Scheduler]]. This translation step is what links and relates nominal time with running wall clock time, thereby obeying the [[timing constraints|Timings]] given while initially defining the calculation stream.
+Each calculation stream refers a relevant [[frame dispatcher table|FrameDispatcher]]. Thus, for the engine (interface level), the calculation stream allows to produce the individual [[render jobs|RenderJob]] to enqueue with the [[Scheduler]]. This translation step is what links and relates nominal time with running wall clock time, thereby obeying the [[timing constraints|Timings]] given when initially defining the calculation stream.
 
 Additionally, each calculation stream knows how to access a //render environment closure,// allowing to re-schedule and re-adjust the setup of this stream. Basically, this closure is comprised of several functors (callbacks), which could be invoked to perform management tasks later on. Amongst others, this allows the calculation stream to redefine, supersede or "cancel itself", without the need to access a central registration table at the engine interface level.
+
+&rarr; NodeOperationProtocol
 
@@ -2773,6 +2775,14 @@ From experiences with other middle scale projects, I prefer having the test code [img[Example: Interfaces/Namespaces of the ~Session-Subsystems|uml/fig130053.png]]
+
+
The actual media data is rendered by [[individually scheduled render jobs|RenderJob]]. When preparing such jobs, in order to [[dispatch|FrameDispatch]] the individual frames to be calculated to make a given [[stream of calculations|CalcStream]] happen, a node planning phase is performed to find out
+* what channel(s) to pull
+* what prerequisites to prepare
+* what parameters to provide
+The result of this planning phase is the JobTicket, a complete execution plan.
+This planning is uniform for each segment and treated for all channels together, resulting in a nested tree structure of sub job tickets, allocated and stored alongside with the processing nodes and wiring descriptors forming the segment's data and descriptor network. Job tickets are //higher order functions:// entering a concrete frame number and channel into a given job ticket will produce an actual job descriptor, which in itself is again a function, to be invoked through the scheduler when it's time to trigger the actual calculations.
+
A major //business interface// &mdash; used by the layers for interfacing to each other; also to be invoked externally by scripts.
 &rarr; [[overfiew and technical details|LayerSeparationInterfaces]]
@@ -3182,7 +3192,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")
@@ -3193,34 +3203,27 @@ __Note__: nothing within the PlacementIndex requires the root object to be of a
 [>img[Outline of the Build Process|uml/fig131333.png]]
 Basically, each [[media asset|MediaAsset]] is considered to be a compound of several elementary media (tracks), possibly of various different media kinds. Adding support for placeholders (''proxy clips'') at some point in future will add still more complexity (because then there will be even dependencies between some of these elementary media). To handle, edit and render compound media, we need to impose some structural limitations. But anyhow, we try to configure as much as possible already at the "asset level" and make the rest of the proc layer behave just according to the configuration given with each asset.
 
-So, when creating a clip out of such a compound media asset, the clip has to be a compound of elementary clips mirroring the given media asset's structure. Besides, it should be possible to //detach// and //attach// elementary clips from a compound clip. On the other hand, the [[Fixture]] created from the current state of the session is explicit to a great extent. So, in the Fixture we deal only with elementary clips placed to absolute positions, and thus the builder will see only simple non-compound clips and translate them into the corresponding source reading nodes.
-
-!Handling
+!!Handling within the Model
 * from a Media asset, we can get a [[Processing Pattern (ProcPatt)|ProcPatt]] describing how to build a render pipeline for this media
 * we can create a Clip (MObject) from each Media, which will be linked back to the media asset internally.
-* moreover, creating a Clip will create and register a Clip asset as well, and this Clip asset will be tied to the original Clip and will show up in some special Category
-* media can be compound and the created Clips will mirror this compound structure
-* we distinguish elementay (non-compound) Clips from compound clips by concrete subtype. {{red{really?? doesn't seem so much like a good idea to me anymore 1/10}}} The builder can only handle elementary clips, because he needs to build a separate pipeline for every output channel. So the work of splitting common effect stacks for clips with several channels needs to be done when calculating the [[Fixture]] for the current session. The Builder expects to be able to build the render nodes corresponding to each entity found in the Fixture one by one.
+* media can be compound internally, but will be handled as a single unit as long as possible. Even for compound media, we get a single clip.
+* within the assets, we get a distinct ChannelConfig asset to represent the structural assembly of compound media based on elementary parts.
+* sometimes its necessary to split and join the individual channels in processing, for technical reasons. Clearly the goal is to hide this kind of accidental complexity and treat it as an implementation detail. At HighLevelModel scope, conceptually there is just one "stream" for each distinct kind of media, and thus there is only one [[Pipe]]. But note, compound media can be structured beyond having several channels. The typical clip is a compound of image and sound. While still handled as one unit, this separation can't be concealed entirely; some effects can be applied to sound or image solely, and routing needs to separate sound and image at least when reaching the [[global pipes|GlobalPipe]] within the timeline. 
 * the Builder gets at the ProcPatt (descriptor) of the underlying media for each clip and uses this description as a template to build the render pipeline. That is, the ProcPatt specifies the codec asset and maybe some additional effect assets (deinterlace, scale) necessary for feeding media data corresponding to this clip/media into the render nodes network.
 
-!{{red{Reviewed 3/2010}}}
-While the general approach and reasoning remains valid, a lot of the details looks dated meanwhile.
-* it is //not//&nbsp; true that the builder can be limited to the handling single processing chains. Some effects ineed need to be fed with mutliple channels &mdash; most notably panners and compressors.
-* consequently there is the need for an internal representation of the media StreamType.
-* thus we can assume the presence of some kind of //type system//
-* which transforms the individual "stream" into a entirely conceptual entity within the HighLevelModel
-* moreover it is clear that the channel configuration needs to be flexible, and not automatically bound to the existence of a single media with that configuration.
-* and last but not least, we handle nested sequences as virtual clips with virtual media.
+!!Handling within the engine
+While initially it seems intuitive just to break down everything into elementary stream processing within the engine, unfortunately a more in-depth analysis reveals that this approach isn't viable. There are some crucial kinds of media processing, which absolutely require having //all channels available at the actual processing step.// Notable examples being sound panning, reverb and compressors. Same holds true for processing of 3D (stereoscopic) images. In some cases we can get away with replicating identical processor nodes over the multiple channels and applying the same parameters to all of them. The decision which approach to take here is a tricky one, and requires much in-depth knowledge about the material to be processed -- typically the quality and the image or sound fidelity depends on these kind of minute distinctions. Many -- otherwise fine -- existing software falls short on this domain. Making such fine points accessible through [[processing rules|Rules]] is one of the core goals of the Lumiera project.
+
+As an immediate consequence of not being able to reduce processing to elementary stream processing, we need to introduce a [[media type system|StreamType]], allowing us to reason and translate between the conceptual unit at the model level, the compound of individual processing chains in the builder and scheduling level, and the still necessary individual render jobs to produce the actual data for each channel stream. Moreover it is clear that the channel configuration needs to be flexible, and not automatically bound to the existence of a single media with that configuration. And last but not least, through this approach, we also enable handling of nested sequences as virtual clips with virtual (multichannel) media.
+
 &rArr; conclusions
-* 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
+* either the ~ClipMO referres directly to a ChannelConfig asset &mdash; or in case of the VirtualClip a BindingMO takes this role.
 * 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
-
-
+* this "multichannel-of-same-kind" capability carries over to all entities within the build process, including ModelPort entries and even the OutputSlot elements
+* in playback / rendering, within each "Feed" (e.g. for image and sound) we get [[calculation streams|CalcStream]] matching the individual channels
+* thus, only as late as when allocating / "opening" an OutputSlot for actual rendering, we get multiple handles for plain single channels.
+* the PlayProcess serves to join both views, providing a single PlayController front-end, while [[dispatching|FrameDispatcher]] to single channel processing.
 
@@ -4490,13 +4493,13 @@ 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|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 [[dispatching|FrameDispatcher]] the [[calculation stream|CalcStream]] and scheduling individual calculation jobs
-* the RenderEngine has the ability to carry out individual frame calculations
+* the [[engine backbone|RenderBackbone]] is responsible for [[dispatching|FrameDispatcher]] the [[calculation stream|CalcStream]] and preparing individual calculation jobs
+* the [[Scheduler]] at the [[engine core|RenderEngine]] has the ability to trigger individual frame calculation carry out individual [[frame calculation jobs|RenderJob]].
 * the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery
 
 [>img[Anatomy of a Play Process|uml/fig144005.png]]
@@ -5132,8 +5135,8 @@ 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.
+
+
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 out over two layers (Backend and Proc-Layer): The [[Builder]] creates a network of [[render nodes|ProcNode]], the [[Scheduler]] triggers individual [[calculation jobs|RenderJob]], which in turn pull data from the render nodes, thereby relying on the [[Backend's services|Backend]] for data access and using plug-ins for the actual media calculations.
 &rarr; OverviewRenderEngine
 &rarr; EngineFaçade 
 
From e9dbb3bdb14a2d6277e09b1e88bde5bb9843b184 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 13 Feb 2012 00:37:57 +0100 Subject: [PATCH 435/496] stubs for some important components of play/engine (JobTicket...) also touches the question how to represent the job descriptor datastructure. @Cehteh: I've just pasted in your preliminary data struct definitinons from the relevant mailing list discussions. --- src/proc/engine/dispatcher.cpp | 5 + src/proc/engine/dispatcher.hpp | 3 + src/proc/engine/job-ticket.hpp | 85 +++++++++++ src/proc/engine/job.hpp | 132 ++++++++++++++++++ src/proc/engine/time-anchor.hpp | 2 +- src/proc/play/dummy-play-connection.hpp | 2 +- .../proc/engine/dispatcher-interface-test.cpp | 35 ++++- .../builder/model-port-registry-test.cpp | 2 +- wiki/renderengine.html | 18 ++- 9 files changed, 275 insertions(+), 9 deletions(-) create mode 100644 src/proc/engine/job-ticket.hpp create mode 100644 src/proc/engine/job.hpp diff --git a/src/proc/engine/dispatcher.cpp b/src/proc/engine/dispatcher.cpp index ef148d4b5..ff44d1f57 100644 --- a/src/proc/engine/dispatcher.cpp +++ b/src/proc/engine/dispatcher.cpp @@ -34,6 +34,11 @@ namespace engine { /** */ + JobTicket& + Dispatcher::accessJobTicket (FrameCoord const& frameID, mobject::ModelPort const& port) + { + UNIMPLEMENTED ("figure out and create the actual JobTicket, for the current segment and the given port"); + } diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp index c828ea260..ee255c384 100644 --- a/src/proc/engine/dispatcher.hpp +++ b/src/proc/engine/dispatcher.hpp @@ -26,8 +26,10 @@ #include "proc/common.hpp" //#include "proc/state.hpp" +#include "proc/mobject/model-port.hpp" #include "proc/engine/time-anchor.hpp" #include "proc/engine/frame-coord.hpp" +#include "proc/engine/job-ticket.hpp" #include "lib/time/timevalue.hpp" #include @@ -56,6 +58,7 @@ namespace engine { virtual FrameCoord locateFrameNext (uint frameCountOffset) =0; + JobTicket& accessJobTicket (FrameCoord const&, mobject::ModelPort const&); }; diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp new file mode 100644 index 000000000..61c03fedb --- /dev/null +++ b/src/proc/engine/job-ticket.hpp @@ -0,0 +1,85 @@ +/* + JOB-TICKET.hpp - execution plan for render jobs + + 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. + +*/ + + +#ifndef PROC_ENGINE_JOB_TICKET_H +#define PROC_ENGINE_JOB_TICKET_H + +#include "proc/common.hpp" +//#include "proc/state.hpp" +//#include "lib/time/timevalue.hpp" +//#include "lib/time/timequant.hpp" + +#include + + +namespace proc { +namespace engine { + +//using lib::time::TimeSpan; +//using lib::time::Duration; +//using lib::time::FSecs; +//using lib::time::Time; +// +//class ExitNode; + + /** + * execution plan for pulling a specific exit node. + * Usable as blue print for generating actual render jobs. + * Job tickets are created on demand, specialised for each segment + * of the low-level model, and for each individual feed (corresponding + * to a single model port). Once created, they are final for this segment, + * stored together with the other descriptor objects (ProcNode and WiringDescriptor) + * and finally discarded in bulk, in case that segment of the low-level model becomes + * obsolete and is replaced by a newly built new version of. + * + * Job tickets are created by a classical recursive descent call on the exit node, + * which figures out everything to be done for generating data from this node. + * To turn a JobTicket into an actual job, we need the additional information + * regarding the precise frame number (=nominal time) and the channel number + * to calculate (in case the actual feed is multichannel, which is the default). + * + * @todo 1/12 WIP-WIP-WIP defining the invocation sequence and render jobs + */ + class JobTicket + : boost::noncopyable + { + + public: + + JobTicket() + { + UNIMPLEMENTED ("job representation, planning and scheduling"); + } + + + bool + isValid() const + { + UNIMPLEMENTED ("validity self check"); + } + }; + + + +}} // namespace proc::engine +#endif diff --git a/src/proc/engine/job.hpp b/src/proc/engine/job.hpp new file mode 100644 index 000000000..97abde69b --- /dev/null +++ b/src/proc/engine/job.hpp @@ -0,0 +1,132 @@ +/* + JOB.hpp - render job closure + + 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. + +*/ + + +#ifndef PROC_ENGINE_JOB_H +#define PROC_ENGINE_JOB_H + + +#include + + +typedef void* LList; ////////////////////////////////////TODO + + +enum JobState + { + DONE, ///< already done, nothing to do + RUNNING, ///< job is currently running + WAITING, ///< waits for some prerequisite resource + REJECTED, ///< sorry, can't do that Dave + EXPIRED, ///< deadline expired + ABORTED ///< got aborted + }; + + +struct lumiera_jobDescriptor_struct + { + gavl_time_t when; + + /* == Job prerequisites == */ + LList waiting; + LList failed; + LList completed; + + JobState jobstate; + + void (*jobfn)(); + void (*failfn)(); + }; +typedef struct lumiera_JobDescriptor_struct lumiera_jobDescriptor; +typedef lumiera_jobDescriptor* LumieraJobDescriptor; + + + + + +#ifdef __cplusplus /* ============== C++ Interface ================= */ + +#include "proc/common.hpp" +//#include "proc/state.hpp" +//#include "lib/time/timevalue.hpp" +//#include "lib/time/timequant.hpp" + +#include + + +namespace proc { +namespace engine { + +//using lib::time::TimeSpan; +//using lib::time::Duration; +//using lib::time::FSecs; +//using lib::time::Time; +// +//class ExitNode; + + /** + * Frame rendering task, represented as closure. + * This functor encodes all information necessary actually to + * trigger and invoke the rendering operation. It will be embedded + * into a job descriptor and then enqueued with the scheduler for + * invocation just in time. + * + * @todo 1/12 WIP-WIP-WIP defining the invocation sequence and render jobs + */ + class Job + { + + public: + + Job() + { + UNIMPLEMENTED ("job representation, planning and scheduling"); + } + + // using standard copy operations + + + bool + isValid() const + { + UNIMPLEMENTED ("validity self check"); + } + }; + + + +}} // namespace proc::engine + + + + +extern "C" { +#endif /* =========================== CL Interface ===================== */ + + +////////////////////////////////////TODO define a C binding for use by the scheduler + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/proc/engine/time-anchor.hpp b/src/proc/engine/time-anchor.hpp index ad6736252..3144db1f2 100644 --- a/src/proc/engine/time-anchor.hpp +++ b/src/proc/engine/time-anchor.hpp @@ -67,7 +67,7 @@ namespace engine { // using default copy operations static TimeAnchor - build (play::Timings timings, uint64_t startFrame) + build (play::Timings timings, uint64_t startFrame, uint channel) { UNIMPLEMENTED ("representation of the Time Anchor closure"); } diff --git a/src/proc/play/dummy-play-connection.hpp b/src/proc/play/dummy-play-connection.hpp index 3dff88f75..5cefdbbd6 100644 --- a/src/proc/play/dummy-play-connection.hpp +++ b/src/proc/play/dummy-play-connection.hpp @@ -97,7 +97,7 @@ namespace play { // using lumiera::DummyPlayer; using lib::time::Duration; - typedef lib::IterSource::iterator ModelPorts; + typedef lib::IterSource::iterator ModelPorts; struct PlayTestFrames_Strategy diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp index b30a0293f..8b528c699 100644 --- a/tests/components/proc/engine/dispatcher-interface-test.cpp +++ b/tests/components/proc/engine/dispatcher-interface-test.cpp @@ -25,6 +25,8 @@ #include "lib/error.hpp" //#include "proc/engine/procnode.hpp" +#include "proc/play/dummy-play-connection.hpp" +#include "proc/mobject/model-port.hpp" #include "proc/engine/dispatcher.hpp" #include "proc/play/timings.hpp" #include "lib/time/timevalue.hpp" @@ -46,15 +48,26 @@ namespace test { using lib::time::QuTime; using lib::time::FrameRate; using lib::time::Duration; - using proc::play::Timings; + using mobject::ModelPort; + using play::Timings; namespace { // used internally + using play::PlayTestFrames_Strategy; + using play::ModelPorts; + + typedef play::DummyPlayConnection DummyPlaybackSetup; + class MockDispatcherTable : public Dispatcher { + DummyPlaybackSetup dummySetup_; + + + /* == mock Dispatcher implementation == */ + FrameCoord locateFrameNext (uint frameCountOffset) { @@ -62,13 +75,26 @@ namespace test { } public: + + ModelPort + provideMockModelPort() + { + ModelPorts mockModelPorts = dummySetup_.provide_testModelPorts(); + return *mockModelPorts; // using just the first dummy port + } }; lib::Singleton mockDispatcher; #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880 #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #890 + ModelPort + getTestPort() + { + return mockDispatcher().provideMockModelPort(); + } + } // (End) internal defs @@ -100,15 +126,20 @@ namespace test { { Dispatcher& dispatcher = mockDispatcher(); Timings timings (FrameRate::PAL); + ModelPort modelPort (getTestPort()); uint startFrame(10); + uint channel(0); - TimeAnchor refPoint = TimeAnchor::build (timings, startFrame); + TimeAnchor refPoint = TimeAnchor::build (timings, startFrame, channel); CHECK (refPoint == Time::ZERO + Duration(10, FrameRate::PAL)); FrameCoord coordinates = dispatcher.locateFrameNext (15); CHECK (coordinates.absoluteNominalTime == Time(0,1)); CHECK (coordinates.absoluteFrameNumber == 25); CHECK (coordinates.remainingRealTime() >= Time(FSecs(24,25))); + + JobTicket& executionPlan = dispatcher.accessJobTicket (coordinates, modelPort); + CHECK (executionPlan.isValid()); #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880 #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880 } diff --git a/tests/components/proc/mobject/builder/model-port-registry-test.cpp b/tests/components/proc/mobject/builder/model-port-registry-test.cpp index a5ebb39e5..d37ae5653 100644 --- a/tests/components/proc/mobject/builder/model-port-registry-test.cpp +++ b/tests/components/proc/mobject/builder/model-port-registry-test.cpp @@ -258,7 +258,7 @@ namespace test { CHECK (pwc); registry.rollback(); - CHECK ( registry.contains (pipeA)); // no effect to the officialy visible state + CHECK ( registry.contains (pipeA)); // no effect to the officially visible state CHECK (!registry.contains (pipeB)); CHECK ( registry.contains (pipeWC)); diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 45636fa78..9c17b5802 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1969,6 +1969,12 @@ 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 grouping device within an ongoing [[playback or render process|PlayProcess]].
+Any feed corresponds to a specific ModelPort, which in turn typically corresponds to a given GlobalPipe.
+When starting playback or render, a play process (with a PlayController front-end for client code) is established to coordinate the processing. This ongoing data production might encompass multiple media streams, i.e. multiple feeds pulled from several model ports and delivered into several [[output slots|OutputSlot]]. Each feed in turn might carry structured MultichannelMedia, and is thus further structured into individual [[streams of calculation|CalcStream]]. Since the latter are //stateless descriptors,// while the player and play process obviously is stateful, it's the feed's role to mediate between a state-based (procedural) and a stateless (functional and parallelised) organisation model -- ensuring a seamless data feed even during modification of the playback parameters.
+
+
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.//
@@ -2125,7 +2131,7 @@ 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 [[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.
 
 !defining the dispatcher interface
@@ -2143,7 +2149,11 @@ The purpose of this interface is to support the planning of new jobs, for a give
 *** the corresponding JobTicket to use at this point
 
 !!!the ~TimeAnchor
-In fact, the whole process of playback or rendering is a continued series of exploration and evaluation. The outline of what needs to be calculated is determined continuously, proceeding in chunks of evaluation. The evaluation structure of the render engine is quite similar to the //fork-join//-pattern, just with the addition of timing constraints. This leads to an invocation pattern, where a partial evaluation happens from time to time. Each of those evaluations establishes a breaking point in time: everything //before// this point is settled and planned thus far. So, this point is an ''anchor'' or closure to root the next partial evaluation. More specifically, this ~TimeAnchor closure is the definitive binding between the abstract logical time of the session timeline, and the real wall-clock time forming the deadline for render evaluation.
+In fact, the whole process of playback or rendering is a continued series of exploration and evaluation. The outline of what needs to be calculated is determined continuously, proceeding in chunks of evaluation. The evaluation structure of the render engine is quite similar to the //fork-join//-pattern, just with the addition of timing constraints. This leads to an invocation pattern, where a partial evaluation happens from time to time. Each of those evaluations establishes a breaking point in time: everything //before// this point is settled and planned thus far. So, this point is an ''anchor'' or closure to root the next partial evaluation. More specifically, this ~TimeAnchor closure is the definitive binding between the abstract logical time of the session timeline, and the real wall-clock time forming the deadline for render evaluation. + +!!!producing actual jobs +The JobTicket is created on demand, specialised for a single [[segment|Segmentation]] of the timeline and a single [[feed|Feed]] of data frames to be pulled from a ModelPort. Thus, in turn, this means that all frames and all channels within that realm will rely on the same job ticket -- which is a //higher order function,// a function producing another function: when provided with the actual channel number and the specific frame coordinates, the job ticket produces a [[concrete job definition|RenderJob]], which itself is a function to be invoked by the [[scheduler|Scheduler]] just in time. +
/***
@@ -4493,7 +4503,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.
 
@@ -4504,7 +4514,7 @@ The play process is an conceptual entity linking together several activities in
 
 [>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.
+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|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 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 875342fa40119f546780bf8bb168b2d68ec248e1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 17 Feb 2012 01:54:51 +0100 Subject: [PATCH 436/496] initial draft for the job representation this draft is based on - Cehteh's draft for the scheduler - my plannings about segmentation and JobTicket it defines "Job" as a closure which can be invoked from plain-C, using the information in the job descriptor datastructure --- src/proc/engine/job.cpp | 90 +++++++++++++++++++++++++++++++++++++++++ src/proc/engine/job.hpp | 58 +++++++++++++++++++++++--- wiki/renderengine.html | 32 +++++++++++++-- 3 files changed, 170 insertions(+), 10 deletions(-) create mode 100644 src/proc/engine/job.cpp diff --git a/src/proc/engine/job.cpp b/src/proc/engine/job.cpp new file mode 100644 index 000000000..9cb97c088 --- /dev/null +++ b/src/proc/engine/job.cpp @@ -0,0 +1,90 @@ +/* + Job - render job closure + + 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. + +* *****************************************************/ + + +#include "proc/engine/job.hpp" +#include "proc/engine/job-ticket.hpp" + + +namespace proc { +namespace engine { + + namespace { // Details... + + + } // (END) Details... + + +// using mobject::Placement; +// using mobject::session::Effect; + + + /** @todo WIP-WIP 2/12 + */ + void + Job::triggerJob (lumiera_jobParameter param) const + { + UNIMPLEMENTED ("how to access the JobTicket and build the RenderInvocation"); + } + + + void + Job::signalFailure (lumiera_jobParameter) const + { + UNIMPLEMENTED ("how to organise job failure and abortion"); + } + + + +}} // namespace proc::engine + +namespace { + using proc::engine::Job; + + inline Job& + forwardInvocation (LumieraJobClosure jobFunctor) + { + Job* job = static_cast (jobFunctor); + + REQUIRE (job); + REQUIRE (job->isValid()); + return *job; + } +} + + +extern "C" { /* ==== implementation C interface for job invocation ======= */ + +void +lumiera_job_invoke (LumieraJobClosure jobFunctor, lumiera_jobParameter param) +{ + forwardInvocation(jobFunctor).triggerJob (param); +} + +void +lumiera_job_failure (LumieraJobClosure jobFunctor, lumiera_jobParameter param) +{ + forwardInvocation(jobFunctor).signalFailure (param); +} + + +} diff --git a/src/proc/engine/job.hpp b/src/proc/engine/job.hpp index 97abde69b..31b563889 100644 --- a/src/proc/engine/job.hpp +++ b/src/proc/engine/job.hpp @@ -29,6 +29,7 @@ typedef void* LList; ////////////////////////////////////TODO +typedef uint64_t InvocationInstanceID; /////////////////TODO enum JobState @@ -42,6 +43,36 @@ enum JobState }; +/** + * closure representing the execution context of a job. + * The information reachable through this closure is specific + * for this kind of job, but static and typically shared among + * all jobs for a given feed and segment of the timeline + */ +struct lumiera_jobClosure { /* placeholder */ }; +typedef struct lumiera_jobClosure* LumieraJobClosure; + + +/** + * invocation parameter for the individual + * frame calculation job. Embedded into the job descriptor + * and passed to #lumiera_job_invoke when triggering + */ +struct lumiera_jobParameter_struct + { + InvocationInstanceID invoKey; + gavl_time_t nominalTime; + //////////////////////////////////////////////////////////////TODO: place a parameter value here, or make the instanceID globally unique? + }; +typedef struct lumiera_jobParameter_struct lumiera_jobParameter; +typedef lumiera_jobParameter* LumieraJobParameter; + + +/** + * descriptor record used by the scheduler to organise job invocation. + * The invocation parameter and job closure necessary to invoke this + * job as a function is embedded into this descriptor. + */ struct lumiera_jobDescriptor_struct { gavl_time_t when; @@ -53,10 +84,10 @@ struct lumiera_jobDescriptor_struct JobState jobstate; - void (*jobfn)(); - void (*failfn)(); + LumieraJobClosure jobClosure; + lumiera_jobParameter parameter; }; -typedef struct lumiera_JobDescriptor_struct lumiera_jobDescriptor; +typedef struct lumiera_jobDescriptor_struct lumiera_jobDescriptor; typedef lumiera_jobDescriptor* LumieraJobDescriptor; @@ -70,7 +101,6 @@ typedef lumiera_jobDescriptor* LumieraJobDescriptor; //#include "lib/time/timevalue.hpp" //#include "lib/time/timequant.hpp" -#include namespace proc { @@ -85,7 +115,7 @@ namespace engine { /** * Frame rendering task, represented as closure. - * This functor encodes all information necessary actually to + * This functor encodes all information necessary to actually * trigger and invoke the rendering operation. It will be embedded * into a job descriptor and then enqueued with the scheduler for * invocation just in time. @@ -93,9 +123,11 @@ namespace engine { * @todo 1/12 WIP-WIP-WIP defining the invocation sequence and render jobs */ class Job + : public lumiera_jobClosure { public: + //////////////////////////////TODO: value semantics or turn this into an interface? Job() { @@ -105,6 +137,10 @@ namespace engine { // using standard copy operations + void triggerJob (lumiera_jobParameter) const; + void signalFailure (lumiera_jobParameter) const; + + bool isValid() const { @@ -123,7 +159,17 @@ extern "C" { #endif /* =========================== CL Interface ===================== */ -////////////////////////////////////TODO define a C binding for use by the scheduler +/** trigger execution of a specific job, + * assuming availability of all prerequisites */ +void lumiera_job_invoke (LumieraJobClosure, lumiera_jobParameter); + +/** signal inability to invoke this job + * @todo decide what and how to communicate details of the failure + * @remarks the purpose of this function is to allow for reliable checkpoints + * within the network of dependent jobs invocations, even after + * missing deadlines or aborting a sequence of jobs */ +void lumiera_job_failure (LumieraJobClosure, lumiera_jobParameter); + #ifdef __cplusplus diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 9c17b5802..7a076ebfb 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -2785,13 +2785,13 @@ From experiences with other middle scale projects, I prefer having the test code [img[Example: Interfaces/Namespaces of the ~Session-Subsystems|uml/fig130053.png]]
-
-
The actual media data is rendered by [[individually scheduled render jobs|RenderJob]]. When preparing such jobs, in order to [[dispatch|FrameDispatch]] the individual frames to be calculated to make a given [[stream of calculations|CalcStream]] happen, a node planning phase is performed to find out
+
+
The actual media data is rendered by [[individually scheduled render jobs|RenderJob]]. When preparing such jobs, in order to [[dispatch|FrameDispatcher]] the individual frames calculations required to make a given [[stream of calculations|CalcStream]] happen, a node planning phase is performed. The goal is to find out
 * what channel(s) to pull
 * what prerequisites to prepare
 * what parameters to provide
-The result of this planning phase is the JobTicket, a complete execution plan.
-This planning is uniform for each segment and treated for all channels together, resulting in a nested tree structure of sub job tickets, allocated and stored alongside with the processing nodes and wiring descriptors forming the segment's data and descriptor network. Job tickets are //higher order functions:// entering a concrete frame number and channel into a given job ticket will produce an actual job descriptor, which in itself is again a function, to be invoked through the scheduler when it's time to trigger the actual calculations.
+The result of this planning phase is the {{{JobTicket}}}, a complete ''execution plan''. +This planning is uniform for each segment and treated for all channels together, resulting in a nested tree structure of sub job tickets, allocated and stored alongside with the processing nodes and wiring descriptors to form the segment's data and descriptor network. Job tickets are //higher order functions:// entering a concrete frame number and channel into a given job ticket will produce an actual job descriptor, which in itself is again a function, to be invoked through the scheduler when it's time to trigger the actual calculations.
A major //business interface// &mdash; used by the layers for interfacing to each other; also to be invoked externally by scripts.
@@ -5198,6 +5198,30 @@ We //do employ//&nbsp; some virtual calls for the buffer management in order
 @@clear(right):display(block):@@
 
+
+
An unit of operation, to be [[scheduled|Scheduler]] for calculating media frame data just in time.
+Within each CalcStream, render jobs are produced by the associated FrameDispatcher, based on the corresponding JobTicket used as blue print (execution plan).
+
+!Anatomy of a render job
+Basically, each render job is a //closure// -- hiding all the prepared, extended execution context and allowing the scheduler to trigger the job as a simple function.
+When activated, by virtue of this closure, the actual ''node invocation'' is constructed, which is a private and safe execution environment for the actual calculations. This (mostly stack based) environment embodies a StateProxy, acting as communication hub for accessing anything possibly stateful within the larger scope of the currently ongoing render process. The node invocation sequence is what actually implements the ''pulling of data'': on exit, all cacluated data is expected to be available in the output buffers. Typically (but not necessarily) each node embodies a ''calculation function'', holding the actual data processing algorithm.
+
+!{{red{open questions 2/12}}}
+* what are the job's actual parameters?
+* how is prerequisite data passed?  &rarr; maybe by an //invocation key?//
+
+!Input and closure
+each job gets only the bare minimum information required to trigger the execution: the really variable part of the node's invocation. The job uses this pieces of information to re-activate a pre-calculated closure, representing a wider scope of environment information. Yet the key point is for this wider scope information to be //quasi static.// It is shared by a whole [[segment|Segmentation]] of the timeline in question, and it will be used and re-used, possibly concurrently. From the render job's point of view, the engine framework just ensures the availability and accessibility of all this wider scope information.
+
+Prerequisite data for the media calculations can be considered just part of that static environment, as far as the node is concerned. Actually, this prerequisite data is dropped off by other nodes, and the engine framework and the builder ensure the availability of this data just in time.
+
+!observations
+* the job's scope represents a strictly local view
+* the job doesn't need to know about its output
+* the job doesn't need to know anything about the frame grid or frame number
+* all it needs to know is the ''effective nominal time'' and an ''invocation instance ID''
+
+
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}}}

From db68577b4aac0dd11eb20dbe06fd4d60439e68bf Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 18 Feb 2012 00:49:53 +0100
Subject: [PATCH 437/496] clarify relation of Job, JobTicket and channel number

---
 src/proc/engine/dispatcher.cpp                    |  2 +-
 src/proc/engine/dispatcher.hpp                    |  3 +--
 src/proc/engine/frame-coord.hpp                   |  6 ++++++
 src/proc/engine/job-ticket.hpp                    |  4 +++-
 src/proc/engine/job.hpp                           | 10 ++++++++--
 src/proc/engine/time-anchor.hpp                   |  4 +++-
 .../proc/engine/dispatcher-interface-test.cpp     | 15 ++++++++++++---
 7 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/src/proc/engine/dispatcher.cpp b/src/proc/engine/dispatcher.cpp
index ff44d1f57..10307282b 100644
--- a/src/proc/engine/dispatcher.cpp
+++ b/src/proc/engine/dispatcher.cpp
@@ -35,7 +35,7 @@ namespace engine {
   
   /** */
   JobTicket&
-  Dispatcher::accessJobTicket (FrameCoord const& frameID, mobject::ModelPort const& port)
+  Dispatcher::accessJobTicket (FrameCoord const& frameID)
   {
     UNIMPLEMENTED ("figure out and create the actual JobTicket, for the current segment and the given port");
   }
diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp
index ee255c384..3f06bb94b 100644
--- a/src/proc/engine/dispatcher.hpp
+++ b/src/proc/engine/dispatcher.hpp
@@ -26,7 +26,6 @@
 
 #include "proc/common.hpp"
 //#include "proc/state.hpp"
-#include "proc/mobject/model-port.hpp"
 #include "proc/engine/time-anchor.hpp"
 #include "proc/engine/frame-coord.hpp"
 #include "proc/engine/job-ticket.hpp"
@@ -58,7 +57,7 @@ namespace engine {
       
       virtual FrameCoord locateFrameNext (uint frameCountOffset)   =0;
       
-      JobTicket& accessJobTicket (FrameCoord const&, mobject::ModelPort const&);
+      JobTicket& accessJobTicket (FrameCoord const&);
       
     };
   
diff --git a/src/proc/engine/frame-coord.hpp b/src/proc/engine/frame-coord.hpp
index 950874927..11b751bb1 100644
--- a/src/proc/engine/frame-coord.hpp
+++ b/src/proc/engine/frame-coord.hpp
@@ -26,6 +26,7 @@
 
 #include "proc/common.hpp"
 //#include "proc/state.hpp"
+#include "proc/mobject/model-port.hpp"
 #include "lib/time/timevalue.hpp"
 #include "lib/time/timequant.hpp"
 
@@ -34,6 +35,7 @@
 namespace proc {
 namespace engine {
   
+  using mobject::ModelPort;
 //using lib::time::TimeSpan;
   using lib::time::Duration;
 //using lib::time::FSecs;
@@ -62,6 +64,10 @@ namespace engine {
       Time absoluteNominalTime;
       int64_t absoluteFrameNumber;
       
+      ModelPort modelPort;
+      uint      channelNr;
+      
+      
       FrameCoord()
         {
           UNIMPLEMENTED ("anything regarding the Node Invocation");
diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index 61c03fedb..7dc2980f9 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -56,7 +56,9 @@ namespace engine {
    * which figures out everything to be done for generating data from this node.
    * To turn a JobTicket into an actual job, we need the additional information
    * regarding the precise frame number (=nominal time) and the channel number
-   * to calculate (in case the actual feed is multichannel, which is the default). 
+   * to calculate (in case the actual feed is multichannel, which is the default).
+   * This way, the JobTicket acts as higher order function: a function
+   * generating on invocation another, specific function (= the job).
    * 
    * @todo 1/12 WIP-WIP-WIP defining the invocation sequence and render jobs
    */
diff --git a/src/proc/engine/job.hpp b/src/proc/engine/job.hpp
index 31b563889..592931ab5 100644
--- a/src/proc/engine/job.hpp
+++ b/src/proc/engine/job.hpp
@@ -117,8 +117,14 @@ namespace engine {
    * Frame rendering task, represented as closure.
    * This functor encodes all information necessary to actually
    * trigger and invoke the rendering operation. It will be embedded
-   * into a job descriptor and then enqueued with the scheduler for
-   * invocation just in time.
+   * by reference into a job descriptor and then enqueued with the scheduler
+   * for invocation just in time. The job interface exposes everything necessary
+   * to plan, handle, schedule and abort jobs. The implementation refers to the
+   * concrete "execution plan" encoded into the corresponding engine::JobTicket.
+   * The latter is embedded into the storage for segment of the low-level model
+   * and thus is shared for all frames and channels within this part of the
+   * timeline. Thus, the lumiera_jobParameter struct contains the "moving parts"
+   * changing for each individual job.
    * 
    * @todo 1/12 WIP-WIP-WIP defining the invocation sequence and render jobs
    */
diff --git a/src/proc/engine/time-anchor.hpp b/src/proc/engine/time-anchor.hpp
index 3144db1f2..f65f7fa30 100644
--- a/src/proc/engine/time-anchor.hpp
+++ b/src/proc/engine/time-anchor.hpp
@@ -26,6 +26,7 @@
 
 #include "proc/common.hpp"
 //#include "proc/state.hpp"
+#include "proc/mobject/model-port.hpp"
 #include "lib/time/timevalue.hpp"
 #include "proc/play/timings.hpp"
 
@@ -34,6 +35,7 @@
 namespace proc {
 namespace engine {
   
+  using mobject::ModelPort;
 //  using lib::time::TimeSpan;
 //  using lib::time::FSecs;
 //  using lib::time::Time;
@@ -67,7 +69,7 @@ namespace engine {
       // using default copy operations
       
       static TimeAnchor
-      build (play::Timings timings, uint64_t startFrame, uint channel)
+      build (play::Timings timings, uint64_t startFrame, ModelPort modelPort, uint channel)
         {
           UNIMPLEMENTED ("representation of the Time Anchor closure");
         }
diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp
index 8b528c699..96d4f6383 100644
--- a/tests/components/proc/engine/dispatcher-interface-test.cpp
+++ b/tests/components/proc/engine/dispatcher-interface-test.cpp
@@ -119,7 +119,7 @@ namespace test  {
       
       
       /** @test perform the basic dispatch step
-       * and verify the generated frame coordinates
+       *  and verify the generated frame coordinates
        */
       void
       verify_basicDispatch()
@@ -130,17 +130,26 @@ namespace test  {
           uint startFrame(10);
           uint channel(0);
           
-          TimeAnchor refPoint = TimeAnchor::build (timings, startFrame, channel);
+          TimeAnchor refPoint = TimeAnchor::build (timings, startFrame, modelPort, channel);
           CHECK (refPoint == Time::ZERO + Duration(10, FrameRate::PAL));
           
           FrameCoord coordinates = dispatcher.locateFrameNext (15);
           CHECK (coordinates.absoluteNominalTime == Time(0,1));
           CHECK (coordinates.absoluteFrameNumber == 25);
           CHECK (coordinates.remainingRealTime() >= Time(FSecs(24,25)));
+          CHECK (coordinates.modelPort == modelPort);
+          CHECK (coordinates.channelNr == channel);
           
-          JobTicket& executionPlan = dispatcher.accessJobTicket (coordinates, modelPort);
+          JobTicket& executionPlan = dispatcher.accessJobTicket (coordinates);
           CHECK (executionPlan.isValid());
+          
 #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
+          JobTicket::JobsPlanning jobs = executionPlan.createJobsFor (coordinates);
+          CHECK (!isnil (jobs));
+          
+          Job headJob = *jobs;
+          CHECK (headJob.getNominalTime() == coordinates.absoluteNominalTime);
+          CHECK (0 < headJob.getInvocationInstanceID());
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
         }
       

From 365f54d59ef02f007c0cd8884e08b08a194928d0 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 18 Feb 2012 19:42:06 +0100
Subject: [PATCH 438/496] Proc-Layer TiddlyWiki entrance pages: some minor
 updates

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

diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 7a076ebfb..e4ebe4522 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1664,8 +1664,15 @@ This is an very important external Interface, because it links together all thre
 * completely hide the Session object behind a ''~PImpl'' smart pointer, so the session object can be switched when reloading.
 * the [[Fixture]] acts as isolation layer, and all objects refered from the Fixture are refcounting smart pointers. So, even when the session gets switched, the old objects remain valid as long as needed.
-
-
A ''frame of data'' is the central low-level abstraction when dealing with media data and media processing.
+
+
A ''frame of data'' is the central low-level abstraction when dealing with media data and media processing.
+
+Deliberately we avoid relying on any special knowledge regarding such data frames, beyond the fact
+* that a frame resides within a ''memory buffer'' &rarr; [[buffer provider abstraction|BufferProvider]]
+* that the frame has a ''frame number'', which can be related to a ''time span'' &rarr; [[time quantisation framework|TimeQuant]]
+* that the frame belongs to an abstract media stream, which can be described through our &rarr; [[stream type system|StreamType]]
+* and that some external ''media handling library'' knows to deal with the data in this frame
+
[[ProcLayer and Engine]]
@@ -1701,13 +1708,13 @@ As we don't have a Prolog interpreter on board yet, we utilize a mock store with
 {{{default(Obj)}}} is a predicate expressing that the object {{{Obj}}} can be considered the default setup under the given conditions. Using the //default// can be considered as a shortcut for actually finding an exact and unique solution. The latter would require to specify all sorts of detailed properties up to the point where only one single object can satisfy all conditions. On the other hand, leaving some properties unspecified would yield a set of solutions (and the user code issuing the query had to provide means for selecting one solution from this set). Just falling back on the //default// means that the user code actually doesn't care for any additional properties (as long as the properties he //does// care for are satisfied). Nothing is said specifically on //how//&nbsp; this default gets configured; actually there can be rules //somewhere,// and, additionally, anything encountered once while asking for a default can be re-used as default under similar circumstances.
 &rarr; [[implementing defaults|DefaultsImplementation]]
-
+
Along the way of working out various [[implementation details|ImplementationDetails]], decisions need to be made on how to understand the different facilities and entities and how to tackle some of the problems. This page is mainly a collection of keywords, summaries and links to further the discussion. And the various decisions should allways be read as proposals to solve some problem at hand...
 
 ''Everything is an object'' &mdash; yes of course, that's a //no-brainer,// todays. Rather, important is to note what is not "an object", meaning it can't be arranged arbitrarily
 * we have one and only one global [[Session]] which directly contains a collection of multiple [[Timelines|Timeline]] and is associated with a globally managed collection of [[assets|Asset]]. We don't utilise scoped variables here (no "mandantisation"); if  a media has been //opened,// it is just plain //globally known//&nbsp; as asset.
 * the [[knowledge base|ConfigRules]] is just available globally. Obviously, the session gets a chance to install rules into this knowledge base, but we don't stress ownership here.
-* we have a [[Fixture]] which acts as isolation layer towards the render engine and is (re)built automatically.
+* we create an unique [[Fixture]] which acts as isolation layer towards the render engine and is (re)built automatically.
 The high-level view of the tangible entities within the session is unified into a ''single tree'' -- with the notable exception of [[external outputs|OutputManagement]] and the [[assets|Asset]], which are understood as representing a //bookkeeping view// and kept separate from the //things to be manipulated// (MObjects).
 
 We ''separate'' processing (rendering) and configuration (building). The [[Builder]] creates a network of [[render nodes|ProcNode]], to be processed by //pulling data // from some [[Pipe]]
@@ -1715,14 +1722,14 @@ We ''separate'' processing (rendering) and configuration (building). The [[Build
 ''Objects are [[placed|Placement]] rather'' than assembled, connected, wired, attached. This is more of a rule-based approach and gives us one central metaphor and abstraction, allowing us to treat everything in an uniform manner. You can place it as you like, and the builder tries to make sense out of it, silently disabling what doesn't make sense.
 An [[Sequence]] is just a collection of configured and placed objects (and has no additional, fixed structure). [[Tracks|Track]] form a mere organisational grid, they are grouping devices not first-class entities (a track doesn't "have" a pipe or "is" a video track and the like; it can be configured to behave in such manner by using placements though). [[Pipes|Pipe]] are hooks for making connections and are the only facility to build processing chains. We have global pipes, and each clip is built around a lokal [[source port|ClipSourcePort]] &mdash; and that's all. No special "media viewer" and "arranger", no special role for media sources, no commitment to some fixed media stream types (video and audio). All of this is sort of pushed down to be configuration, represented as asset of some kind. For example, we have [[processing pattern|ProcPatt]] assets to represent the way of building the source network for reading from some media file (including codecs treated like effect plugin nodes)
 
-Actual ''media data and handling'' is abstracted rigorously. Media is conceived as being stream-like data of distinct StreamType. When it comes to more low-level media handling, we build on the DataFrame abstraction. Media processing isn't the focus of Lumiera; we organise the processing but otherwise ''rely on media handling libraries.'' In a similar vein, multiplicity is understood as type variation. Consequently, we don't build an audio and video "section" and we don't even have audio tracks and video tracks. Lumiera uses tracks and clips, and clips build on media, but we're able to deal with multichannel mixed-typed media.
+Actual ''media data and handling'' is abstracted rigorously. Media is conceived as being stream-like data of distinct StreamType. When it comes to more low-level media handling, we build on the DataFrame abstraction. Media processing isn't the focus of Lumiera; we organise the processing but otherwise ''rely on media handling libraries.'' In a similar vein, multiplicity is understood as type variation. Consequently, we don't build an audio and video "section" and we don't even have audio tracks and video tracks. Lumiera uses tracks and clips, and clips build on media, but we're able to deal with [[multichannel|MultichannelMedia]] mixed-typed media natively.
 
-Lumiera is not a connection manager, it is not an audio-visual real time instrument, and it doesn't aim at running presentations. It's an ''environment for assembling and building up'' something (an edit, a session, a piece of media work). This decision is visible at various levels and contexts, like a reserved attitude towards hardware acceleration (it //will// be supported, but reliable proxy editing has a higher priority), or the decision, not to incorporate system level ports and connections directly into the session model (they are mapped to [[output designations|OutputDesignation]] rather)
+Lumiera is not a connection manager, it is not an audio-visual real time performance instrument, and it doesn't aim at running presentations. It's an ''environment for assembling and building up'' something (an edit, a session, a piece of media work). This decision is visible at various levels and contexts, like a reserved attitude towards hardware acceleration (it //will// be supported, but reliable proxy editing has a higher priority), or the decision, not to incorporate system level ports and connections directly into the session model (they are mapped to [[output designations|OutputDesignation]] rather)
 
 ''State'' is rigorously ''externalized'' and operations are to be ''scheduled'', to simplify locking and error handling. State is either treated similar to media stream data (as addressable and cacheable data frame), or is represented as "parameter" to be served by some [[parameter provider|ParamProvider]]. Consequently, [[Automation]] is just another kind of parameter, i.e. a function &mdash; how this function is calculated is an encapsulated implementation detail (we don't have "bezier automation", and then maybe a "linear automation", a "mask automation" and yet another way to handle transitions)
 
 Deliberately there is an limitaion on the flexibility of what can be added to the system via Plugins. We allow configuration and parametrisation to be extended and we allow processing and data handling to be extended, but we disallow extensions to the fundamental structure of the system by plugins. They may provide new implementations for already known subsystems, but they can't introduce new subsystems not envisioned in the general design of the application.
-* thus fixed assortments include: the possbile kinds of MObject and [[Asset]], the possible automation parameter data types, the supported kinds of plugin systems
+* these fixed assortments include: the possbile kinds of MObject and [[Asset]], the possible automation parameter data types, the supported kinds of plugin systems
 * while plugins may extend: the supported kinds of media, the [[media handling libraries|MediaImplLib]] used to deal with those media, the session storage backends, the behaviour of placments
 
@@ -2379,7 +2386,7 @@ Besides routing to a global pipe, wiring plugs can also connect to the source po Finally, this example shows an ''automation'' data set controlling some parameter of an effect contained in one of the global pipes. From the effect's POV, the automation is simply a ParamProvider, i.e a function yielding a scalar value over time. The automation data set may be implemented as a bézier curve, or by a mathematical function (e.g. sine or fractal pseudo random) or by some captured and interpolated data values. Interestingly, in this example the automation data set has been placed relatively to the meta clip (albeit on another track), thus it will follow and adjust when the latter is moved.
-
+
This wiki page is the entry point to detail notes covering some technical decisions, details and problems encountered in the course of the implementation of the Lumiera Renderengine, the Builder and the related parts.
 
 * [[Packages, Interfaces and Namespaces|InterfaceNamespaces]]
@@ -2412,7 +2419,7 @@ Finally, this example shows an ''automation'' data set controlling some paramete
 * detail considerations regarding [[time and time quantisation|TimeQuant]]
 * [[Timecode]] -- especially the link of [[TC formats and quantisation|TimecodeFormat]]
 * designing how to [[build|BuildFixture]] the [[Fixture]] (...{{red{WIP}}}...)
-
+* from [[play process|PlayProcess]] to [[frame dispatching|FrameDispatcher]] and [[node invocation|NodeInvocation]]
 
@@ -4653,8 +4660,8 @@ Besides, they provide an __inward interface__ for the [[ProcNode]]s, enabling th
-
-
The Render Engine is the part of the application doing the actual video calculations. Utilising system level services and retrieving raw audio and video data through [[Lumiera's Backend|backend.html]], its operations are guided by the objects and parameters edited by the user in [[the session|Session]]. The middle layer of the Lumiera architecture, known as the Proc-Layer, spans the area between these two exteremes, providing the the (abstract) edit operations available to the user, the representation of [["editable things"|MObjects]] and the translation of those into structures and facilities allowing to [[drive the rendering|Rendering]].
+
+
The Render Engine is the part of the application doing the actual video calculations. Relying on system level services and retrieving raw audio and video data through [[Lumiera's Backend|Backend]], its operations are guided by the objects and parameters edited by the user in [[the session|Session]]. The middle layer of the Lumiera architecture, known as the Proc-Layer, spans the area between these two exteremes, providing the the (abstract) edit operations available to the user, the representation of [["editable things"|MObjects]] and the translation of those into structures and facilities allowing to [[drive the rendering|Rendering]].
 
 !About this wiki page
 |background-color:#E3F3F1;width:96ex;padding:2ex; This TiddlyWiki is the central location for design, planning and documentation of the Lumiera Proc-Layer. Some parts are used as //extended brain// &mdash; collecting ideas, considerations and conclusions &mdash; while other tiddlers contain the decisions and document the planned or implemented facilities. The intention is to move over the more mature parts into the emerging technical documentation section on the [[Lumiera website|http://www.lumiera.org]] eventually. <br/><br/>Besides cross-references, content is largely organised through [[Tags|TabTags]], most notably <br/><<tag overview>> &middot; <<tag def>> &middot; <<tag decision>> &middot; <<tag Concepts>> <br/> <<tag Model>> &middot; <<tag SessionLogic>> &middot; <<tag GuiIntegration>> &middot; <<tag Builder>> &middot; <<tag Rendering>> &middot; <<tag Player>> &middot; <<tag Rules>> &middot; <<tag Types>> |

From 1e54b5d3e675203fce13f56407545878c5e4111e Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 18 Feb 2012 19:42:20 +0100
Subject: [PATCH 439/496] stubbing some job functions

---
 src/proc/engine/job-ticket.hpp                 | 10 ++++++++++
 src/proc/engine/job.hpp                        | 18 +++++++++++++++---
 .../proc/engine/dispatcher-interface-test.cpp  |  2 +-
 3 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index 7dc2980f9..838d073ad 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -26,8 +26,11 @@
 
 #include "proc/common.hpp"
 //#include "proc/state.hpp"
+#include "proc/engine/job.hpp"
+#include "proc/engine/frame-coord.hpp"
 //#include "lib/time/timevalue.hpp"
 //#include "lib/time/timequant.hpp"
+#include "lib/iter-source.hpp"
 
 #include 
 
@@ -67,6 +70,7 @@ namespace engine {
     {
       
     public:
+       typedef lib::IterSource::iterator  JobsPlanning;
       
       JobTicket()
         {
@@ -74,6 +78,12 @@ namespace engine {
         }
       
       
+      JobsPlanning
+      createJobsFor (FrameCoord coordinates)
+        {
+          UNIMPLEMENTED ("job planning and generation")
+        }
+      
       bool
       isValid()  const
         {
diff --git a/src/proc/engine/job.hpp b/src/proc/engine/job.hpp
index 592931ab5..711dad1a7 100644
--- a/src/proc/engine/job.hpp
+++ b/src/proc/engine/job.hpp
@@ -62,7 +62,7 @@ struct lumiera_jobParameter_struct
   {
     InvocationInstanceID invoKey;
     gavl_time_t nominalTime;
-    //////////////////////////////////////////////////////////////TODO: place a parameter value here, or make the instanceID globally unique?
+    //////////////////////////////////////////////////////////////TODO: place an additional parameter value here, or make the instanceID globally unique?
   };
 typedef struct lumiera_jobParameter_struct lumiera_jobParameter;
 typedef lumiera_jobParameter* LumieraJobParameter;
@@ -98,7 +98,7 @@ typedef lumiera_jobDescriptor* LumieraJobDescriptor;
 
 #include "proc/common.hpp"
 //#include "proc/state.hpp"
-//#include "lib/time/timevalue.hpp"
+#include "lib/time/timevalue.hpp"
 //#include "lib/time/timequant.hpp"
 
 
@@ -109,7 +109,7 @@ namespace engine {
 //using lib::time::TimeSpan;
 //using lib::time::Duration;
 //using lib::time::FSecs;
-//using lib::time::Time;
+using lib::time::Time;
 //  
 //class ExitNode;
   
@@ -147,6 +147,18 @@ namespace engine {
       void signalFailure (lumiera_jobParameter)  const;
       
       
+      Time
+      getNominalTime()  const
+        {
+          UNIMPLEMENTED ("job representation, planning and scheduling");
+        }
+      
+      InvocationInstanceID
+      getInvocationInstanceID()  const
+        {
+          UNIMPLEMENTED ("job representation, planning and scheduling");
+        }
+      
       bool
       isValid()  const
         {
diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp
index 96d4f6383..758ad4d7e 100644
--- a/tests/components/proc/engine/dispatcher-interface-test.cpp
+++ b/tests/components/proc/engine/dispatcher-interface-test.cpp
@@ -143,13 +143,13 @@ namespace test  {
           JobTicket& executionPlan = dispatcher.accessJobTicket (coordinates);
           CHECK (executionPlan.isValid());
           
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
           JobTicket::JobsPlanning jobs = executionPlan.createJobsFor (coordinates);
           CHECK (!isnil (jobs));
           
           Job headJob = *jobs;
           CHECK (headJob.getNominalTime() == coordinates.absoluteNominalTime);
           CHECK (0 < headJob.getInvocationInstanceID());
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
         }
       

From 0b25c2e08d469a10e514aa2112961bdfbc35786a Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 19 Feb 2012 00:41:45 +0100
Subject: [PATCH 440/496] Fix: missing sanity check in ScopedCollection

funny enough this possible memory corruption
didn't happen in the unit test, because my
compiler optimised the additional int field
of class SubDummy, making it the same size
of the baseclass. Now matters should be safe.
---
 src/lib/scoped-collection.hpp        | 10 ++++++----
 tests/lib/scoped-collection-test.cpp |  4 ++--
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp
index 0ff2d37ef..729c88495 100644
--- a/src/lib/scoped-collection.hpp
+++ b/src/lib/scoped-collection.hpp
@@ -132,12 +132,14 @@ namespace lib {
           
           
           
-#define TYPE_SANITY_CHECK \
-              BOOST_STATIC_ASSERT ((boost::is_base_of::value || boost::is_same::value))
+#define TYPE_AND_STORAGE_SANITY_CHECK \
+              BOOST_STATIC_ASSERT (((boost::is_same::value   \
+                                   ||boost::is_base_of::value)\
+                                   && sizeof(TY) <= siz))
           
           /** Abbreviation for placement new */
 #define EMBEDDED_ELEMENT_CTOR(_CTOR_CALL_)    \
-              TYPE_SANITY_CHECK;               \
+              TYPE_AND_STORAGE_SANITY_CHECK;   \
               return *new(&buf_) _CTOR_CALL_;   \
           
           
@@ -205,8 +207,8 @@ namespace lib {
             {
               EMBEDDED_ELEMENT_CTOR ( TY(a1,a2,a3,a4,a5) )
             }
+#undef TYPE_AND_STORAGE_SANITY_CHECK
 #undef EMBEDDED_ELEMENT_CTOR
-#undef TYPE_SANITY_CHECK
         };
       
       
diff --git a/tests/lib/scoped-collection-test.cpp b/tests/lib/scoped-collection-test.cpp
index f112e79da..c2408d7dc 100644
--- a/tests/lib/scoped-collection-test.cpp
+++ b/tests/lib/scoped-collection-test.cpp
@@ -78,7 +78,7 @@ namespace test{
     inline uint
     sum (uint n)
     {
-      return n*(n+1) / 2; 
+      return n*(n+1) / 2;
     }
       
   }//(End) subversive test data
@@ -89,7 +89,7 @@ namespace test{
   using util::isnil;
   using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
   
-  typedef ScopedCollection CollD;
+  typedef ScopedCollection CollD;
   
   
   /********************************************************************

From f8f011bb44b4495ccfe17661fa37989f7dd62466 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 19 Feb 2012 00:43:35 +0100
Subject: [PATCH 441/496] rework Job representation

make class Job a real subclass of the
job definition struct and turn the
JobClosure into a trampoline
---
 src/proc/engine/job-ticket.hpp                | 23 ++++++-
 src/proc/engine/job.cpp                       | 60 ++++++++++++-----
 src/proc/engine/job.hpp                       | 67 +++++++++++--------
 .../proc/engine/dispatcher-interface-test.cpp |  2 +
 4 files changed, 107 insertions(+), 45 deletions(-)

diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index 838d073ad..8fb4034ff 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -45,6 +45,27 @@ namespace engine {
 //  
 //class ExitNode;
   
+  /** 
+   * a job closure represents the execution context of a job.
+   * This allows us to enqueue simple job-"functions" with the scheduler.
+   * By virtue of the JobClosure-pointer, embedded into #lumiera_jobDefinition,
+   * the invocation of such a job may re-gain the full context, including the
+   * actual ProcNode to pull and further specifics, like the media channel.
+   */ 
+  class JobClosure
+    : public lumiera_jobClosure
+    {
+    public:
+      
+      bool
+      verify (Time nominalJobTime)  const
+        {
+          UNIMPLEMENTED ("access the underlying JobTicket and verify the given job time is within the relevant timeline segment");
+          return false;
+        }
+    };
+  
+  
   /**
    * execution plan for pulling a specific exit node.
    * Usable as blue print for generating actual render jobs.
@@ -81,7 +102,7 @@ namespace engine {
       JobsPlanning
       createJobsFor (FrameCoord coordinates)
         {
-          UNIMPLEMENTED ("job planning and generation")
+          UNIMPLEMENTED ("job planning and generation");
         }
       
       bool
diff --git a/src/proc/engine/job.cpp b/src/proc/engine/job.cpp
index 9cb97c088..0af1b192f 100644
--- a/src/proc/engine/job.cpp
+++ b/src/proc/engine/job.cpp
@@ -21,6 +21,18 @@
 * *****************************************************/
 
 
+/** @file job.cpp 
+ ** Implementation of render job invocation.
+ ** Within this translation unit, the actual invocation of a frame rendering
+ ** job takes place, after reconstruction of the job's execution environment (closure).
+ ** 
+ ** @see JobTicket
+ ** @see ProcNode
+ ** @see nodeinvocation.hpp
+ **
+ */
+
+
 #include "proc/engine/job.hpp"
 #include "proc/engine/job-ticket.hpp"
 
@@ -29,7 +41,14 @@ namespace proc {
 namespace engine {
   
   namespace { // Details...
-
+    
+    inline JobClosure&
+    myClosure (const Job * const self)
+    {
+      ASSERT (self);
+      ASSERT (self->jobClosure);
+      return *static_cast (self->jobClosure);
+    }
     
   } // (END) Details...
   
@@ -41,18 +60,31 @@ namespace engine {
   /** @todo WIP-WIP 2/12  
    */
   void
-  Job::triggerJob (lumiera_jobParameter param)  const
+  Job::triggerJob ()  const
   {
     UNIMPLEMENTED ("how to access the JobTicket and build the RenderInvocation");
   }
   
   
   void
-  Job::signalFailure (lumiera_jobParameter)  const
+  Job::signalFailure ()  const
   {
     UNIMPLEMENTED ("how to organise job failure and abortion");
   }
-
+  
+  
+  /** Render Job self verification.
+   *  performs a parameter consistency check
+   *  including a call-back to the defining JobTicket
+   */
+  bool
+  Job::isValid()  const
+  {
+    return this->jobClosure
+        && this->parameter.invoKey > 0
+        && myClosure(this).verify (getNominalTime());
+         ;
+  }
   
   
 }} // namespace proc::engine
@@ -61,30 +93,28 @@ namespace {
   using proc::engine::Job;
     
   inline Job& 
-  forwardInvocation (LumieraJobClosure jobFunctor)
+  forwardInvocation (lumiera_jobDefinition& jobDef)
   {
-    Job* job = static_cast (jobFunctor);
+    Job& job = static_cast (jobDef);
     
-    REQUIRE (job);
-    REQUIRE (job->isValid());
-    return *job;
+    REQUIRE (job.isValid());
+    return job;
   }
 }
 
 
+
 extern "C" { /* ==== implementation C interface for job invocation ======= */
   
 void
-lumiera_job_invoke  (LumieraJobClosure jobFunctor, lumiera_jobParameter param)
+lumiera_job_invoke  (lumiera_jobDefinition jobDef)
 {
-  forwardInvocation(jobFunctor).triggerJob (param);
+  forwardInvocation(jobDef).triggerJob();
 }
 
 void
-lumiera_job_failure (LumieraJobClosure jobFunctor, lumiera_jobParameter param)
+lumiera_job_failure (lumiera_jobDefinition jobDef)
 {
-  forwardInvocation(jobFunctor).signalFailure (param);
+  forwardInvocation(jobDef).signalFailure();
 }
-  
-  
 }
diff --git a/src/proc/engine/job.hpp b/src/proc/engine/job.hpp
index 711dad1a7..c184b77e4 100644
--- a/src/proc/engine/job.hpp
+++ b/src/proc/engine/job.hpp
@@ -68,24 +68,37 @@ typedef struct lumiera_jobParameter_struct lumiera_jobParameter;
 typedef lumiera_jobParameter* LumieraJobParameter;
 
 
+/** complete definition of an individual job */
+struct lumiera_jobDefinition_struct
+  {
+    LumieraJobClosure jobClosure;
+    lumiera_jobParameter parameter;
+  };
+typedef struct lumiera_jobDefinition_struct lumiera_jobDefinition;
+
+
 /**
  * descriptor record used by the scheduler to organise job invocation.
- * The invocation parameter and job closure necessary to invoke this
- * job as a function is embedded into this descriptor.
+ * The actual job's definition, i.e. the invocation parameter and
+ * the closure necessary to invoke the job as a function
+ * is embedded (by value) into this descriptor.
+ * 
+ * @note while this descriptor as such is self-contained,
+ *       the referred LumieraJobClosure needs to be allocated
+ *       and managed separately. Indeed, this closure happens
+ *       to live within the segment data, as part of the JobTicket.
  */
 struct lumiera_jobDescriptor_struct
   {
     gavl_time_t when;
+    JobState jobState;
+    
+    lumiera_jobDefinition jobDefinition;
     
     /* == Job prerequisites == */
     LList waiting;
     LList failed;
     LList completed;
-  
-    JobState jobstate;
-  
-    LumieraJobClosure jobClosure;
-    lumiera_jobParameter parameter;
   };
 typedef struct lumiera_jobDescriptor_struct lumiera_jobDescriptor;
 typedef lumiera_jobDescriptor* LumieraJobDescriptor;
@@ -109,61 +122,57 @@ namespace engine {
 //using lib::time::TimeSpan;
 //using lib::time::Duration;
 //using lib::time::FSecs;
+using lib::time::TimeValue;
 using lib::time::Time;
 //  
 //class ExitNode;
   
   /**
    * Frame rendering task, represented as closure.
-   * This functor encodes all information necessary to actually
-   * trigger and invoke the rendering operation. It will be embedded
-   * by reference into a job descriptor and then enqueued with the scheduler
+   * This functor encodes all information necessary to trigger
+   * and invoke the actual rendering operation. It will be embedded
+   * by value into a job descriptor and then enqueued with the scheduler
    * for invocation just in time. The job interface exposes everything necessary
    * to plan, handle, schedule and abort jobs. The implementation refers to the
    * concrete "execution plan" encoded into the corresponding engine::JobTicket.
-   * The latter is embedded into the storage for segment of the low-level model
-   * and thus is shared for all frames and channels within this part of the
-   * timeline. Thus, the lumiera_jobParameter struct contains the "moving parts"
-   * changing for each individual job.
+   * The latter is embedded into the storage for one segment of the low-level model
+   * and thus is shared for all frames and channels within this part of the timeline.
+   * Thus, the lumiera_jobParameter struct contains the "moving parts"
+   * different for each \em individual job.
    * 
-   * @todo 1/12 WIP-WIP-WIP defining the invocation sequence and render jobs
+   * @todo 2/12 WIP-WIP-WIP defining the invocation sequence and render jobs
    */
   class Job
-    : public lumiera_jobClosure
+    : public lumiera_jobDefinition
     {
       
     public:
-      //////////////////////////////TODO: value semantics or turn this into an interface?
       
       Job()
         {
-          UNIMPLEMENTED ("job representation, planning and scheduling");
+          UNIMPLEMENTED ("job creation, planning and scheduling");
         }
       
       // using standard copy operations
       
       
-      void triggerJob (lumiera_jobParameter)  const;
-      void signalFailure (lumiera_jobParameter)  const;
+      void triggerJob()    const;
+      void signalFailure() const;
       
       
       Time
       getNominalTime()  const
         {
-          UNIMPLEMENTED ("job representation, planning and scheduling");
+          return Time (TimeValue(parameter.nominalTime));
         }
       
       InvocationInstanceID
       getInvocationInstanceID()  const
         {
-          UNIMPLEMENTED ("job representation, planning and scheduling");
+          return this->parameter.invoKey;
         }
       
-      bool
-      isValid()  const
-        {
-          UNIMPLEMENTED ("validity self check");
-        }
+      bool isValid()  const;
     };
   
   
@@ -179,14 +188,14 @@ extern "C" {
 
 /** trigger execution of a specific job,
  *  assuming availability of all prerequisites */
-void lumiera_job_invoke  (LumieraJobClosure, lumiera_jobParameter);
+void lumiera_job_invoke  (lumiera_jobDefinition);
 
 /** signal inability to invoke this job
  * @todo decide what and how to communicate details of the failure
  * @remarks the purpose of this function is to allow for reliable checkpoints
  *          within the network of dependent jobs invocations, even after
  *          missing deadlines or aborting a sequence of jobs */
-void lumiera_job_failure (LumieraJobClosure, lumiera_jobParameter);
+void lumiera_job_failure (lumiera_jobDefinition);
 
 
 
diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp
index 758ad4d7e..be2c4bf91 100644
--- a/tests/components/proc/engine/dispatcher-interface-test.cpp
+++ b/tests/components/proc/engine/dispatcher-interface-test.cpp
@@ -32,11 +32,13 @@
 #include "lib/time/timevalue.hpp"
 #include "lib/time/timequant.hpp"
 #include "lib/singleton.hpp"
+#include "lib/util.hpp"
 
 //#include 
 //#include 
 
 using test::Test;
+using util::isnil;
 //using std::cout;
 //using std::rand;
 

From 393a4478615d0e378a79c9b9d2d550b95e8a196f Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 20 Feb 2012 01:13:53 +0100
Subject: [PATCH 442/496] test setup: simulated build proecss results, mock
 model port

---
 src/proc/play/dummy-play-connection.hpp | 109 +++++++++++++++++++++++-
 1 file changed, 107 insertions(+), 2 deletions(-)

diff --git a/src/proc/play/dummy-play-connection.hpp b/src/proc/play/dummy-play-connection.hpp
index 5cefdbbd6..37681ce0f 100644
--- a/src/proc/play/dummy-play-connection.hpp
+++ b/src/proc/play/dummy-play-connection.hpp
@@ -81,11 +81,20 @@
 #include "proc/play/output-manager.hpp"
 #include "proc/mobject/model-port.hpp"
 #include "lib/time/timequant.hpp"
+//#include "lib/scoped-collection.hpp"
+#include "proc/mobject/builder/model-port-registry.hpp"
+#include "proc/asset/timeline.hpp"
+#include "proc/asset/pipe.hpp"
+#include "lib/query.hpp"
+#include "lib/util.hpp"
+//#include "lib/symbol.hpp"
+
 #include "lib/iter-source.hpp"
 //
 #include 
 //#include 
 //#include 
+#include 
 
 
 namespace proc {
@@ -97,7 +106,7 @@ namespace play {
 //    using lumiera::DummyPlayer;
   using lib::time::Duration;
   
-  typedef lib::IterSource::iterator ModelPorts;
+  typedef lib::IterSource::iterator ModelPorts;
   
   
   struct PlayTestFrames_Strategy
@@ -106,6 +115,101 @@ namespace play {
     };
   
   
+  namespace { // simulated builder environment
+    
+    using asset::Pipe;
+    using asset::PPipe;
+    using asset::Struct;
+    using asset::Timeline;
+    using asset::PTimeline;
+    using mobject::ModelPort;
+    using mobject::builder::ModelPortRegistry;
+    using lumiera::Query;
+    using util::contains;
+//  using lib::ScopedCollection;
+//  using lib::Literal;
+    using lib::eachEntry;
+  
+    typedef asset::ID PID;
+    typedef asset::ID TID;
+      
+//  typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor;
+    
+    
+    inline PID
+    getPipe (string id)
+    {
+      return Pipe::query("id("+id+")");
+    }
+    
+    inline TID
+    getTimeline (string id)
+    {
+      return asset::Struct::retrieve (Query ("id("+id+")"))->getID();
+    }
+    
+    const uint NUMBER_OF_PORTS = 2;
+    const string namePortA("bus-A");
+    const string namePortB("bus-B");
+    
+    /** 
+     * helper for dummy render engine: 
+     * Simulate the result of a build process,
+     * without actually running the builder.
+     * Produces some mock pipes, model ports etc.
+     */
+    struct SimulatedBuilderContext
+      {
+        ModelPortRegistry registry_;
+        ModelPortRegistry* existingRegistry_;
+        
+        std::vector modelPorts_;
+        
+        /** setup */
+        SimulatedBuilderContext()
+          : registry_()
+          , existingRegistry_(ModelPortRegistry::setActiveInstance (registry_))
+          {
+            performMockBuild();
+          }
+        
+        /** tear-down */
+       ~SimulatedBuilderContext()
+          {
+            if (existingRegistry_)
+              ModelPortRegistry::setActiveInstance (*existingRegistry_);
+            else
+              ModelPortRegistry::shutdown();
+          }
+        
+        void
+        performMockBuild()
+          {
+            PID pipeA        = getPipe (namePortA);
+            PID pipeB        = getPipe (namePortB);
+            TID someTimeline = getTimeline ("dummy_Timeline");
+            
+            // start out with defining some new model ports......
+            registry_.definePort (pipeA, someTimeline);
+            registry_.definePort (pipeB, someTimeline);
+            
+            registry_.commit();
+            
+            // now "bus-A" and "bus-B" are known as model ports
+            modelPorts_.push_back (ModelPort(pipeA));
+            modelPorts_.push_back (ModelPort(pipeB));
+          }
+        
+        
+        ModelPorts
+        getAllModelPorts()
+          {
+            return eachEntry (modelPorts_.begin(), modelPorts_.end());
+          }
+      };
+  }
+
+  
   /********************************************************************
    * Framework for dummy playback and rendering.
    * A DummyPlayConnection provides a coherent set of placeholders,
@@ -118,13 +222,14 @@ namespace play {
   class DummyPlayConnection
     : boost::noncopyable
     {
+      SimulatedBuilderContext mockBuilder_;
       
     public:
       
       ModelPorts
       provide_testModelPorts()
         {
-          UNIMPLEMENTED ("provide a set of test model ports");
+          return mockBuilder_.getAllModelPorts();
         }
       
       POutputManager

From ee1450a81ad7d885a774382d1dbfe6d022a579f9 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 24 Feb 2012 00:29:59 +0100
Subject: [PATCH 443/496] rectify frame dispatch invocation

---
 src/proc/engine/dispatcher.hpp                      | 13 ++++++++++++-
 src/proc/engine/time-anchor.hpp                     |  9 +++++----
 .../proc/engine/dispatcher-interface-test.cpp       |  7 ++++---
 3 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp
index 3f06bb94b..6f9d0af8d 100644
--- a/src/proc/engine/dispatcher.hpp
+++ b/src/proc/engine/dispatcher.hpp
@@ -26,6 +26,7 @@
 
 #include "proc/common.hpp"
 //#include "proc/state.hpp"
+#include "proc/mobject/model-port.hpp"
 #include "proc/engine/time-anchor.hpp"
 #include "proc/engine/frame-coord.hpp"
 #include "proc/engine/job-ticket.hpp"
@@ -38,6 +39,7 @@
 namespace proc {
 namespace engine {
   
+  using mobject::ModelPort;
   using lib::time::TimeSpan;
   using lib::time::FSecs;
   using lib::time::Time;
@@ -50,12 +52,21 @@ namespace engine {
   class Dispatcher
     : boost::noncopyable
     {
+      struct CoordBuilder
+        {
+          Dispatcher& dispatcher_;
+          ModelPort modelPort_;
+          uint channel_;
+          
+          FrameCoord relativeFrameLocation (TimeAnchor refPoint, uint frameCountOffset);    
+        };
       
     public:
       virtual ~Dispatcher();  ///< this is an interface
       
+      CoordBuilder onCalcStream (ModelPort modelPort, uint channel);
       
-      virtual FrameCoord locateFrameNext (uint frameCountOffset)   =0;
+      virtual FrameCoord locateFrameNext (uint frameCountOffset, TimeAnchor refPoint)   =0;
       
       JobTicket& accessJobTicket (FrameCoord const&);
       
diff --git a/src/proc/engine/time-anchor.hpp b/src/proc/engine/time-anchor.hpp
index f65f7fa30..40b7105e5 100644
--- a/src/proc/engine/time-anchor.hpp
+++ b/src/proc/engine/time-anchor.hpp
@@ -26,7 +26,6 @@
 
 #include "proc/common.hpp"
 //#include "proc/state.hpp"
-#include "proc/mobject/model-port.hpp"
 #include "lib/time/timevalue.hpp"
 #include "proc/play/timings.hpp"
 
@@ -35,10 +34,9 @@
 namespace proc {
 namespace engine {
   
-  using mobject::ModelPort;
 //  using lib::time::TimeSpan;
 //  using lib::time::FSecs;
-//  using lib::time::Time;
+  using lib::time::Time;
 //  
 //  class ExitNode;
   
@@ -59,6 +57,9 @@ namespace engine {
    */
   class TimeAnchor
     {
+      ///////////////////////////TODO need some kind of scale or grid here
+      uint64_t anchorPoint_;
+      Time relatedRealTime_;
       
       TimeAnchor()
         {
@@ -69,7 +70,7 @@ namespace engine {
       // using default copy operations
       
       static TimeAnchor
-      build (play::Timings timings, uint64_t startFrame, ModelPort modelPort, uint channel)
+      build (play::Timings timings, uint64_t startFrame)
         {
           UNIMPLEMENTED ("representation of the Time Anchor closure");
         }
diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp
index be2c4bf91..005ebbb06 100644
--- a/tests/components/proc/engine/dispatcher-interface-test.cpp
+++ b/tests/components/proc/engine/dispatcher-interface-test.cpp
@@ -71,7 +71,7 @@ namespace test  {
         /* == mock Dispatcher implementation == */
         
         FrameCoord
-        locateFrameNext (uint frameCountOffset)
+        locateFrameNext (uint frameCountOffset, TimeAnchor refPoint)
           {
             UNIMPLEMENTED ("dummy implementation of the core dispatch operation");
           }
@@ -132,10 +132,11 @@ namespace test  {
           uint startFrame(10);
           uint channel(0);
           
-          TimeAnchor refPoint = TimeAnchor::build (timings, startFrame, modelPort, channel);
+          TimeAnchor refPoint = TimeAnchor::build (timings, startFrame);
           CHECK (refPoint == Time::ZERO + Duration(10, FrameRate::PAL));
           
-          FrameCoord coordinates = dispatcher.locateFrameNext (15);
+          FrameCoord coordinates = dispatcher.onCalcStream (modelPort,channel)
+                                             .relativeFrameLocation (refPoint, 15);
           CHECK (coordinates.absoluteNominalTime == Time(0,1));
           CHECK (coordinates.absoluteFrameNumber == 25);
           CHECK (coordinates.remainingRealTime() >= Time(FSecs(24,25)));

From aa01813f52acf43c7fb1f8442d879991fe486a11 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 25 Feb 2012 00:27:50 +0100
Subject: [PATCH 444/496] TimeAnchor: draft calculating the start delay for
 rendering

---
 src/proc/engine/time-anchor.hpp | 48 ++++++++++++++++++++++++++++++---
 src/proc/play/timings.hpp       |  1 +
 2 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/src/proc/engine/time-anchor.hpp b/src/proc/engine/time-anchor.hpp
index 40b7105e5..8905ad7a9 100644
--- a/src/proc/engine/time-anchor.hpp
+++ b/src/proc/engine/time-anchor.hpp
@@ -29,12 +29,15 @@
 #include "lib/time/timevalue.hpp"
 #include "proc/play/timings.hpp"
 
+#include 
+
 
 
 namespace proc {
 namespace engine {
   
 //  using lib::time::TimeSpan;
+  using lib::time::Offset;
 //  using lib::time::FSecs;
   using lib::time::Time;
 //  
@@ -54,27 +57,64 @@ namespace engine {
    * wall-clock time forming the deadline for rendering.
    * 
    * @todo 1/12 WIP-WIP-WIP just emerging as a concept
+   *          /////////////////////////////////////////////////////////////////TODO: WIP needs to act as proxy for the grid, using the Timings
    */
   class TimeAnchor
     {
-      ///////////////////////////TODO need some kind of scale or grid here
+      play::Timings timings_;
       uint64_t anchorPoint_;
       Time relatedRealTime_;
       
-      TimeAnchor()
+      Time
+      expectedTimeofArival (play::Timings timings, Offset engineLatency)
         {
-          UNIMPLEMENTED ("anything regarding the Engine backbone");
+          TimeVar deadline;
+          switch (timings.playbackUrgency)
+            {
+            case play::ASAP:
+            case play::NICE:
+              deadline = RealClock::now() + engineLatency;
+              break;
+            
+            case play::TIMEBOUND:
+              deadline = timings.getTimeDue() - engineLatency; 
+              break;
+            }
+          return deadline - timings.getOutputLatency();
         }
       
+      
+      TimeAnchor (play::Timings timings, Offset engineLatency, uint64_t startFrame)
+        : timings_(timings)
+        , anchorPoint(startFrame)
+        , relatedRealTime_(expectedTimeofArival(timings, engineLatency))
+        { }
+      
     public:
       // using default copy operations
       
+      
+      /** create a TimeAnchor for playback/rendering start
+       *  at the given startFrame. Since no information is given
+       *  regarding the reaction latency required to get the engine
+       *  to deliver at a given time, this "engine latency" is guessed
+       *  to be 1/3 of the frame duration.
+       * @note using this function in case of "background" rendering
+       *       doesn't make much sense; you should indeed retrieve the
+       *       start delay from internals of the engine in this case.
+       */
       static TimeAnchor
       build (play::Timings timings, uint64_t startFrame)
         {
-          UNIMPLEMENTED ("representation of the Time Anchor closure");
+          const boost::rational DEFAULT_LATENCY_FACTOR (1,3);
+          Offset startDelay = timings.getFrameDurationAt(startFrame) * DEFAULT_LATENCY_FACTOR;
+          
+          return TimeAnchor (timings,startDelay,startFrame);
         }
       
+      //////////////////////////////////////////////////////////////////////////////////////////TODO: second builder function, relying on Engine timings
+      
+      
       operator lib::time::TimeValue()  const
         {
           UNIMPLEMENTED ("representation of the Time Anchor closure");
diff --git a/src/proc/play/timings.hpp b/src/proc/play/timings.hpp
index f0ac12112..085fc267e 100644
--- a/src/proc/play/timings.hpp
+++ b/src/proc/play/timings.hpp
@@ -90,6 +90,7 @@ namespace play {
    * @note copyable value class
    * 
    * @todo write type comment
+   *          /////////////////////////////////////////////////////////////////TODO: WIP seems to become some kind of abstracted Grid representation!
    */
   class Timings
     {

From 882bcf07aef4be40dacdbd8e63f26d72ff19d74e Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 2 Mar 2012 01:33:57 +0100
Subject: [PATCH 445/496] define stubs for accessing the wall clock time
 (->Backend)

---
 src/backend/real-clock.cpp      |  72 ++++++++++++++++++++++
 src/backend/real-clock.hpp      | 103 ++++++++++++++++++++++++++++++++
 src/proc/engine/time-anchor.hpp |  14 +++--
 src/proc/play/timings.cpp       |  33 ++++++++--
 src/proc/play/timings.hpp       |  17 ++++--
 5 files changed, 224 insertions(+), 15 deletions(-)
 create mode 100644 src/backend/real-clock.cpp
 create mode 100644 src/backend/real-clock.hpp

diff --git a/src/backend/real-clock.cpp b/src/backend/real-clock.cpp
new file mode 100644
index 000000000..b651520f0
--- /dev/null
+++ b/src/backend/real-clock.cpp
@@ -0,0 +1,72 @@
+/*
+  Timings  -  timing specifications for a frame quantised data stream
+
+  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.
+
+* *****************************************************/
+
+
+#include "backend/real-clock.hpp"
+#include "lib/time/timequant.hpp"
+
+
+
+namespace backend {
+  
+  
+//  using lib::time::PQuant;
+//  using lib::time::Time;
+  
+  namespace { // hidden local details of the service implementation....
+    
+    
+  } // (End) hidden service impl details
+  
+  
+  /** storage for the singleton accessor, holding
+   *  an instance of the RealClock service */
+  lib::Singleton RealClock::_clock;
+  
+  
+  /** Initialise a service to retrieve system time with sufficient precision 
+   */
+  RealClock::RealClock ()
+    { 
+      UNIMPLEMENTED ("system clock service");
+    }
+  
+  RealClock::~RealClock ()
+    { 
+      UNIMPLEMENTED ("disconnect the system clock service");
+    }
+  
+  
+     
+  TimeValue
+  RealClock::readSystemTime()
+  {
+    UNIMPLEMENTED ("access the system clock");
+    gavl_time_t ticksSince1970 = 1261440000000000L;
+    
+    ENSURE (ticksSince1970 == Time::limited (ticksSince1970));
+    return TimeValue::buildRaw_(ticksSince1970);  // bypassing the limit check
+  }
+  
+  
+  
+} // namespace backend
diff --git a/src/backend/real-clock.hpp b/src/backend/real-clock.hpp
new file mode 100644
index 000000000..3ce89342d
--- /dev/null
+++ b/src/backend/real-clock.hpp
@@ -0,0 +1,103 @@
+/*
+  REAL-CLOCK.hpp  -  convenience front-end to access the system clock
+
+  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.
+
+*/
+
+/** @file real-clock.hpp
+ ** Front-end for simplified access to the current wall clock time.
+ ** The implementation relies on Lumiera backend functions to access the
+ ** system clock with a sufficient level of precision. The result is
+ ** delivered in lumiera's internal \link lib::time::Time time format \endlink
+ **   
+ ** @todo WIP-WIP-WIP 3/2012
+ ** @todo this might be a good candidate also to provide some kind of
+ **       translation service, i.e. a grid to anchor a logical time value
+ **       with actual running wall clock time
+ ** 
+ ** @see lib/time/timevalue.hpp
+ */
+
+
+#ifndef BACKEND_REAL_CLOCK_H
+#define BACKEND_REAL_CLOCK_H
+
+
+#include "lib/error.hpp"
+//#include "lib/handle.hpp"
+#include "lib/time/timevalue.hpp"
+#include "lib/singleton.hpp"
+//#include "proc/engine/buffer-provider.hpp"
+//#include "lib/iter-source.hpp"
+//#include "lib/sync.hpp"
+
+//#include 
+//#include 
+//#include 
+//#include 
+//#include 
+
+//namespace lib {
+//namespace time{
+//  class Quantiser;
+//  typedef std::tr1::shared_ptr PQuant;
+//}}
+
+namespace backend {
+
+  using lib::time::Time;
+  using lib::time::TimeValue;
+//using std::string;
+
+//using std::vector;
+//using std::tr1::shared_ptr;
+//using boost::scoped_ptr;
+  
+  
+  /**
+   * Convenience frontend to access the current wall clock time
+   */
+  class RealClock
+    {
+      
+     ~RealClock ();
+      RealClock ();
+      
+      friend class lib::singleton::StaticCreate;
+      
+      
+      static lib::Singleton _clock;
+      
+    public:
+      
+      static Time
+      now()
+        {
+          return Time(_clock().readSystemTime());
+        }
+      
+      
+    private:
+      TimeValue readSystemTime();
+    };
+  
+  
+  
+} // namespace backend
+#endif
diff --git a/src/proc/engine/time-anchor.hpp b/src/proc/engine/time-anchor.hpp
index 8905ad7a9..d5812adf2 100644
--- a/src/proc/engine/time-anchor.hpp
+++ b/src/proc/engine/time-anchor.hpp
@@ -26,6 +26,7 @@
 
 #include "proc/common.hpp"
 //#include "proc/state.hpp"
+#include "backend/real-clock.hpp"
 #include "lib/time/timevalue.hpp"
 #include "proc/play/timings.hpp"
 
@@ -36,11 +37,12 @@
 namespace proc {
 namespace engine {
   
+  using backend::RealClock;
 //  using lib::time::TimeSpan;
   using lib::time::Offset;
 //  using lib::time::FSecs;
+  using lib::time::TimeVar;
   using lib::time::Time;
-//  
 //  class ExitNode;
   
   /**
@@ -62,7 +64,7 @@ namespace engine {
   class TimeAnchor
     {
       play::Timings timings_;
-      uint64_t anchorPoint_;
+      int64_t anchorPoint_;
       Time relatedRealTime_;
       
       Time
@@ -80,13 +82,13 @@ namespace engine {
               deadline = timings.getTimeDue() - engineLatency; 
               break;
             }
-          return deadline - timings.getOutputLatency();
+          return deadline - timings.outputLatency;
         }
       
       
-      TimeAnchor (play::Timings timings, Offset engineLatency, uint64_t startFrame)
+      TimeAnchor (play::Timings timings, Offset engineLatency, int64_t startFrame)
         : timings_(timings)
-        , anchorPoint(startFrame)
+        , anchorPoint_(startFrame)
         , relatedRealTime_(expectedTimeofArival(timings, engineLatency))
         { }
       
@@ -104,7 +106,7 @@ namespace engine {
        *       start delay from internals of the engine in this case.
        */
       static TimeAnchor
-      build (play::Timings timings, uint64_t startFrame)
+      build (play::Timings timings, int64_t startFrame)
         {
           const boost::rational DEFAULT_LATENCY_FACTOR (1,3);
           Offset startDelay = timings.getFrameDurationAt(startFrame) * DEFAULT_LATENCY_FACTOR;
diff --git a/src/proc/play/timings.cpp b/src/proc/play/timings.cpp
index 956b5f72a..564f65d45 100644
--- a/src/proc/play/timings.cpp
+++ b/src/proc/play/timings.cpp
@@ -83,8 +83,16 @@ namespace play {
   Timings::getFrameDurationAt (TimeValue refPoint)  const
   {
     int64_t frameNr = grid_->gridPoint (refPoint);
+    return getFrameDurationAt(frameNr);
+  }
+  
+  
+  Duration
+  Timings::getFrameDurationAt (int64_t refFrameNr)  const
+  {
     return Offset (grid_->timeOf(frameNr), grid_->timeOf(frameNr+1));
   }
+
   
   
   /** @remarks the purpose of this function is to support scheduling
@@ -98,9 +106,26 @@ namespace play {
   {
     return Duration (Time::ANYTIME);
   }
-
-
-
   
-
+  
+  Time
+  Timings::getTimeDue()  const
+  {
+    if (TIMEBOUND == playbackUrgency)
+      {
+        UNIMPLEMENTED ("scheduled delivery spec");
+      }
+    else
+      return Time::NEVER;
+  }
+  
+  
+  Timings
+  Timings::constrainedBy (Timings additionalConditions)
+  {
+    UNIMPLEMENTED ("how to combine timing constraints");
+  }
+  
+  
+  
 }} // namespace proc::play
diff --git a/src/proc/play/timings.hpp b/src/proc/play/timings.hpp
index 085fc267e..634ece475 100644
--- a/src/proc/play/timings.hpp
+++ b/src/proc/play/timings.hpp
@@ -69,6 +69,7 @@ namespace play {
   using lib::time::TimeValue;
   using lib::time::Duration;
   using lib::time::Offset;
+  using lib::time::Time;
 //using std::string;
 
 //using std::vector;
@@ -98,6 +99,7 @@ namespace play {
       
     public:
       PlaybackUrgency playbackUrgency;
+      Duration outputLatency;
       
       Timings (FrameRate fps);
       
@@ -107,6 +109,7 @@ namespace play {
       
       Offset   getFrameOffsetAt   (TimeValue refPoint)  const;
       Duration getFrameDurationAt (TimeValue refPoint)  const;
+      Duration getFrameDurationAt (int64_t refFrameNr)  const;
       
       /** the frame spacing and duration remains constant for some time...
        * @param startPoint looking from that time point into future
@@ -114,14 +117,18 @@ namespace play {
        *         to assume unaltered frame dimensions */
       Duration constantFrameTimingsInterval (TimeValue startPoint)  const;
       
+      /** for scheduled time of delivery, which is signalled
+       *  by \code playbackUrgency == TIMEBOUND \endcode
+       * @return wall clock time to expect delivery of data corresponding
+       *         to \link #getOrigin time axis origin \endlink
+       * @note for other playback urgencies \c Time::NEVER
+       */
+      Time getTimeDue()  const;
       
       //////////////TODO further accessor functions here
       
-      Timings
-      constrainedBy (Timings additionalConditions)
-        {
-          UNIMPLEMENTED ("how to combine timing constraints");
-        }
+      Timings constrainedBy (Timings additionalConditions);
+      
     };
   
   

From 22322dfec4e1f0728e01186ef982a2e11400da47 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 4 Mar 2012 01:34:18 +0100
Subject: [PATCH 446/496] refactor the division/quantisation helpers

...no need to keep them in util.hpp, as they
are used rather occasionally, while util.hpp
is used pervasively.
---
 src/lib/time.cpp                  |   2 +-
 src/lib/time/mutation.cpp         |   1 +
 src/lib/time/timecode.cpp         |   1 +
 src/lib/util-quant.hpp            | 119 ++++++++++++++++++++++++++++++
 src/lib/util.hpp                  |  88 +---------------------
 tests/lib/util-floordiv-test.cpp  |   1 +
 tests/lib/util-floorwrap-test.cpp |   3 +-
 7 files changed, 127 insertions(+), 88 deletions(-)
 create mode 100644 src/lib/util-quant.hpp

diff --git a/src/lib/time.cpp b/src/lib/time.cpp
index ff9815f95..d68b883cb 100644
--- a/src/lib/time.cpp
+++ b/src/lib/time.cpp
@@ -22,7 +22,7 @@
 
 #include "lib/time.h"
 #include "lib/error.hpp"
-#include "lib/util.hpp"
+#include "lib/util-quant.hpp"
 
 extern "C" {
 #include "lib/tmpbuf.h"
diff --git a/src/lib/time/mutation.cpp b/src/lib/time/mutation.cpp
index a6fd96e50..718af5244 100644
--- a/src/lib/time/mutation.cpp
+++ b/src/lib/time/mutation.cpp
@@ -27,6 +27,7 @@
 #include "lib/time/timecode.hpp"
 #include "lib/time/mutation.hpp"
 //#include "lib/time.h"
+#include "lib/util-quant.hpp"
 #include "lib/util.hpp"
 
 //#include 
diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp
index 857179461..53faef575 100644
--- a/src/lib/time/timecode.cpp
+++ b/src/lib/time/timecode.cpp
@@ -27,6 +27,7 @@
 #include "lib/time/formats.hpp"
 #include "lib/time.h"
 #include "lib/util.hpp"
+#include "lib/util-quant.hpp"
 
 #include 
 #include 
diff --git a/src/lib/util-quant.hpp b/src/lib/util-quant.hpp
new file mode 100644
index 000000000..361edace7
--- /dev/null
+++ b/src/lib/util-quant.hpp
@@ -0,0 +1,119 @@
+/*
+  UTIL-QUANT.hpp  -  helper functions to deal with division and quantisation
+
+  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 LIB_UTIL_QUANT_H
+#define LIB_UTIL_QUANT_H
+
+#include 
+
+
+
+namespace util {
+  
+  
+  /** helper to treat int or long division uniformly */
+  template
+  struct IDiv
+    {
+      I quot;
+      I rem;
+      
+      IDiv (I num, I den)
+        : quot(num/den)
+        , rem(num - quot*den)
+        { }
+    };
+  
+  template<>
+  struct IDiv
+    : div_t
+    {
+      IDiv (int num, int den)
+        : div_t(div (num,den))
+        { }
+    };
+  
+  template<>
+  struct IDiv
+    : ldiv_t
+    {
+      IDiv (long num, long den)
+        : ldiv_t(ldiv (num,den))
+        { }
+    };
+  
+  template<>
+  struct IDiv
+    : lldiv_t
+    {
+      IDiv (long long num, long long den)
+        : lldiv_t(lldiv (num,den))
+        { }
+    };
+  
+  
+  /** floor function for integer arithmetics.
+   *  Unlike the built-in integer division, this function
+   *  always rounds towards the next \em smaller integer,
+   *  even for negative numbers.
+   * @warning floor on doubles performs way better
+   * @see UtilFloordiv_test
+   */
+  template
+  inline I
+  floordiv (I num, I den)
+  {
+    if (0 < (num^den))
+      return num/den;
+    else
+      { // truncate similar to floor()
+        IDiv res(num,den);
+        return (res.rem)? res.quot-1   // negative results truncated towards next smaller int
+                        : res.quot;   //..unless the division result not truncated at all
+      }
+  }
+  
+  /** scale wrapping operation.
+   *  Quantises the numerator value into the scale given by the denominator.
+   *  Unlike the built-in integer division, this function always rounds towards
+   *  the next \em smaller integer and also relates the remainder (=modulo) to
+   *  this next lower scale grid point.
+   * @return quotient and remainder packed into a struct
+   * @see UtilFloorwarp_test
+   */
+  template
+  inline IDiv
+  floorwrap (I num, I den)
+  {
+    IDiv res(num,den);
+    if (0 > (num^den) && res.rem)
+      {  // negative results
+        //  wrapped similar to floor()
+        --res.quot;
+        res.rem = den - (-res.rem);
+      }
+    return res;
+  }
+  
+} // namespace util
+#endif /*UTIL_QUANT_H*/
diff --git a/src/lib/util.hpp b/src/lib/util.hpp
index 037881883..a629de195 100644
--- a/src/lib/util.hpp
+++ b/src/lib/util.hpp
@@ -21,8 +21,8 @@
 */
 
 
-#ifndef UTIL_HPP_
-#define UTIL_HPP_
+#ifndef LIB_UTIL_H
+#define LIB_UTIL_H
 
 #include 
 #include 
@@ -57,90 +57,6 @@ namespace util {
   }
   
   
-  /** helper to treat int or long division uniformly */
-  template
-  struct IDiv
-    {
-      I quot;
-      I rem;
-      
-      IDiv (I num, I den)
-        : quot(num/den)
-        , rem(num - quot*den)
-        { }
-    };
-  
-  template<>
-  struct IDiv
-    : div_t
-    {
-      IDiv (int num, int den)
-        : div_t(div (num,den))
-        { }
-    };
-  
-  template<>
-  struct IDiv
-    : ldiv_t
-    {
-      IDiv (long num, long den)
-        : ldiv_t(ldiv (num,den))
-        { }
-    };
-  
-  template<>
-  struct IDiv
-    : lldiv_t
-    {
-      IDiv (long long num, long long den)
-        : lldiv_t(lldiv (num,den))
-        { }
-    };
-  
-  
-  /** floor function for integer arithmetics.
-   *  Unlike the built-in integer division, this function
-   *  always rounds towards the next \em smaller integer,
-   *  even for negative numbers.
-   * @warning floor on doubles performs way better
-   * @see UtilFloordiv_test
-   */
-  template
-  inline I
-  floordiv (I num, I den)
-  {
-    if (0 < (num^den))
-      return num/den;
-    else
-      { // truncate similar to floor()
-        IDiv res(num,den);
-        return (res.rem)? res.quot-1   // negative results truncated towards next smaller int
-                        : res.quot;   //..unless the division result not truncated at all
-      }
-  }
-  
-  /** scale wrapping operation.
-   *  Quantises the numerator value into the scale given by the denominator.
-   *  Unlike the built-in integer division, this function always rounds towards
-   *  the next \em smaller integer and also relates the remainder (=modulo) to
-   *  this next lower scale grid point.
-   * @return quotient and remainder packed into a struct
-   * @see UtilFloorwarp_test
-   */
-  template
-  inline IDiv
-  floorwrap (I num, I den)
-  {
-    IDiv res(num,den);
-    if (0 > (num^den) && res.rem)
-      {  // negative results
-        //  wrapped similar to floor()
-        --res.quot;
-        res.rem = den - (-res.rem);
-      }
-    return res;
-  }
-  
   
   /*  ======== generic empty check =========  */
   
diff --git a/tests/lib/util-floordiv-test.cpp b/tests/lib/util-floordiv-test.cpp
index 63c14a1a6..256498cef 100644
--- a/tests/lib/util-floordiv-test.cpp
+++ b/tests/lib/util-floordiv-test.cpp
@@ -22,6 +22,7 @@
 
 
 #include "lib/test/run.hpp"
+#include "lib/util-quant.hpp"
 #include "lib/util.hpp"
 
 #include 
diff --git a/tests/lib/util-floorwrap-test.cpp b/tests/lib/util-floorwrap-test.cpp
index c42f37593..7e66cc10b 100644
--- a/tests/lib/util-floorwrap-test.cpp
+++ b/tests/lib/util-floorwrap-test.cpp
@@ -23,7 +23,7 @@
 
 #include "lib/test/run.hpp"
 #include "lib/test/test-helper.hpp"
-#include "lib/util.hpp"
+#include "lib/util-quant.hpp"
 
 #include 
 #include 
@@ -36,6 +36,7 @@ using std::endl;
 using boost::format;
 using boost::lexical_cast;
 using lib::test::showType;
+using util::floorwrap;
 
 
 namespace util {

From 9aec2a98068a38dca9b09c45116f9cc662bf19b1 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 17 Mar 2012 00:49:58 +0100
Subject: [PATCH 447/496] allow for fractional scaling of time durations

implemented as extension to the linear combinations.
I decided to use the same "always floor" rule
as employed for time quantisation. Moreover,
we don't support floating point, only rationals
---
 src/lib/time/lumitime.cpp          | 12 ++++++++++++
 src/lib/time/timevalue.hpp         | 23 ++++++++++++++++-------
 tests/lib/time/time-value-test.cpp | 23 +++++++++++++++++++++++
 3 files changed, 51 insertions(+), 7 deletions(-)

diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp
index 82de03645..03218ebb7 100644
--- a/src/lib/time/lumitime.cpp
+++ b/src/lib/time/lumitime.cpp
@@ -23,12 +23,14 @@
 
 #include "lib/time/timevalue.hpp"
 #include "lib/time.h"
+#include "lib/util-quant.hpp"
 
 #include 
 #include 
 #include 
 
 using std::string;
+using util::floordiv;
 
 
 namespace lib {
@@ -122,6 +124,16 @@ namespace time {
   }
   
   
+  Offset
+  operator* (boost::rational factor, Offset const& o)
+  {
+    boost::rational distance (_raw(o));
+    distance *= factor;
+    gavl_time_t microTicks = floordiv (distance.numerator(), distance.denominator());
+    return Offset(TimeValue(microTicks));
+  }
+  
+  
   /** 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)
diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp
index 4b429b0ea..b01f295c8 100644
--- a/src/lib/time/timevalue.hpp
+++ b/src/lib/time/timevalue.hpp
@@ -223,6 +223,13 @@ namespace time {
     return Offset(distance);
   }
   
+  template
+  inline Offset
+  operator* (Offset const& distance, INT factor)
+  {
+    return factor*distance;
+  }
+  
   inline Offset
   operator* (int factor, Offset const& o)
   {
@@ -231,12 +238,12 @@ namespace time {
     return Offset(distance);
   }
   
-  inline Offset
-  operator* (Offset const& distance, int factor)
-  {
-    return factor*distance;
-  }
+  /** stretch offset by a possibly fractional factor */
+  Offset
+  operator* (boost::rational factor, Offset const& o);
   
+  
+  /** flip offset direction */
   inline Offset
   Offset::operator- ()  const
   {
@@ -365,14 +372,16 @@ namespace time {
     return Offset(base) + Offset(toAdd);
   }
   
+  template
   inline Offset
-  operator* (int factor, Duration const& dur)
+  operator* (INT factor, Duration const& dur)
   {
     return factor * Offset(dur);
   }
   
+  template
   inline Offset
-  operator* (Duration const& dur, int factor)
+  operator* (Duration const& dur, INT factor)
   {
     return factor*dur;
   }
diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp
index 27656bd44..59d71b7c2 100644
--- a/tests/lib/time/time-value-test.cpp
+++ b/tests/lib/time/time-value-test.cpp
@@ -76,6 +76,7 @@ namespace test{
           buildTimeSpan (ref);
           compareTimeSpan (Time(ref));
           relateTimeIntervals (ref);
+          verify_fractionalOffset();
         } 
       
       
@@ -286,6 +287,28 @@ namespace test{
         }
       
       
+      void
+      verify_fractionalOffset ()
+        {
+          typedef boost::rational Frac;
+          
+          Duration three (TimeValue(3));       // three micro seconds
+          
+          Offset o1 = Frac(1,2) * three;
+          CHECK (o1 > Time::ZERO);
+          CHECK (o1 == TimeValue(1));          // bias towards the next lower micro grid position
+          
+          Offset o2 = -Frac(1,2) * three;
+          CHECK (o2 < Time::ZERO);
+          CHECK (o2 == TimeValue(-2));
+          
+          CHECK (three * Frac(1,2) * 2 != three);
+          CHECK (three *(Frac(1,2) * 2) == three);
+          // integral arithmetics is precise,
+          // but not necessarily exact!
+        }
+      
+      
       void
       buildTimeSpan (TimeValue org)
         {

From 7941865d5d2744282a04fb9a7d085e835343a732 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 19 Mar 2012 01:03:14 +0100
Subject: [PATCH 448/496] implement anchor against current system time

using CLOCK_REALTIME for now
---
 SConstruct                      |  2 +-
 src/backend/real-clock.cpp      | 40 +++++++++------------------------
 src/backend/real-clock.hpp      | 17 +++++---------
 src/proc/engine/dispatcher.cpp  | 16 +++++++++++++
 src/proc/engine/time-anchor.hpp |  4 +++-
 src/proc/play/timings.cpp       |  3 ++-
 6 files changed, 38 insertions(+), 44 deletions(-)

diff --git a/SConstruct b/SConstruct
index 8fe12e998..16fda2d2f 100644
--- a/SConstruct
+++ b/SConstruct
@@ -70,7 +70,7 @@ env.Clean ('build', [ 'src/pre.gch' ])
 # pick up the targets defined by the sub SConscripts
 Import('lumiera plugins tools gui testsuite doxydoc')
 
-build = env.Alias('build', lumiera + plugins + tools +gui)
+build = env.Alias('build', lumiera + plugins + tools + gui)
 env.Default('build')
 # SCons default target
 
diff --git a/src/backend/real-clock.cpp b/src/backend/real-clock.cpp
index b651520f0..fc5fa5520 100644
--- a/src/backend/real-clock.cpp
+++ b/src/backend/real-clock.cpp
@@ -22,46 +22,26 @@
 
 
 #include "backend/real-clock.hpp"
-#include "lib/time/timequant.hpp"
 
+#include 
 
 
 namespace backend {
   
-  
-//  using lib::time::PQuant;
-//  using lib::time::Time;
-  
-  namespace { // hidden local details of the service implementation....
-    
-    
-  } // (End) hidden service impl details
+#define MICRO_TICS_PER_NANOSECOND (1000*1000*1000 / GAVL_TIME_SCALE)
   
   
-  /** storage for the singleton accessor, holding
-   *  an instance of the RealClock service */
-  lib::Singleton RealClock::_clock;
   
-  
-  /** Initialise a service to retrieve system time with sufficient precision 
-   */
-  RealClock::RealClock ()
-    { 
-      UNIMPLEMENTED ("system clock service");
-    }
-  
-  RealClock::~RealClock ()
-    { 
-      UNIMPLEMENTED ("disconnect the system clock service");
-    }
-  
-  
-     
   TimeValue
-  RealClock::readSystemTime()
+  RealClock::_readSystemTime()
   {
-    UNIMPLEMENTED ("access the system clock");
-    gavl_time_t ticksSince1970 = 1261440000000000L;
+    timespec now;
+    clock_gettime(CLOCK_REALTIME, &now);
+                                      ////////////////////////////////////////////TODO shouldn't that be CLOCK_MONOTONIC ?
+                                      ////////////////////////////////////////////TODO (what happens on ntp adjustments?)
+    
+    gavl_time_t ticksSince1970 = now.tv_sec * GAVL_TIME_SCALE
+                               + now.tv_nsec / MICRO_TICS_PER_NANOSECOND; 
     
     ENSURE (ticksSince1970 == Time::limited (ticksSince1970));
     return TimeValue::buildRaw_(ticksSince1970);  // bypassing the limit check
diff --git a/src/backend/real-clock.hpp b/src/backend/real-clock.hpp
index 3ce89342d..4d1ad49eb 100644
--- a/src/backend/real-clock.hpp
+++ b/src/backend/real-clock.hpp
@@ -29,7 +29,9 @@
  ** @todo WIP-WIP-WIP 3/2012
  ** @todo this might be a good candidate also to provide some kind of
  **       translation service, i.e. a grid to anchor a logical time value
- **       with actual running wall clock time
+ **       with actual running wall clock time.
+ ** @todo not clear if this becomes some kind of central service (singleton)
+ **       or just a bunch of library routines
  ** 
  ** @see lib/time/timevalue.hpp
  */
@@ -42,7 +44,7 @@
 #include "lib/error.hpp"
 //#include "lib/handle.hpp"
 #include "lib/time/timevalue.hpp"
-#include "lib/singleton.hpp"
+//#include "lib/singleton.hpp"
 //#include "proc/engine/buffer-provider.hpp"
 //#include "lib/iter-source.hpp"
 //#include "lib/sync.hpp"
@@ -76,25 +78,18 @@ namespace backend {
   class RealClock
     {
       
-     ~RealClock ();
-      RealClock ();
-      
-      friend class lib::singleton::StaticCreate;
-      
-      
-      static lib::Singleton _clock;
       
     public:
       
       static Time
       now()
         {
-          return Time(_clock().readSystemTime());
+          return Time(_readSystemTime());
         }
       
       
     private:
-      TimeValue readSystemTime();
+      static TimeValue _readSystemTime();
     };
   
   
diff --git a/src/proc/engine/dispatcher.cpp b/src/proc/engine/dispatcher.cpp
index 10307282b..12c51d981 100644
--- a/src/proc/engine/dispatcher.cpp
+++ b/src/proc/engine/dispatcher.cpp
@@ -33,6 +33,22 @@ namespace engine {
   Dispatcher::~Dispatcher() { }  // emit VTable here....
   
   
+  /** */
+  Dispatcher::CoordBuilder
+  Dispatcher::onCalcStream (ModelPort modelPort, uint channel)
+  {
+    UNIMPLEMENTED ("build coordinates of frame to render");
+  }
+  
+  
+  /** */
+  FrameCoord
+  Dispatcher::CoordBuilder::relativeFrameLocation (TimeAnchor refPoint, uint frameCountOffset)
+  {
+    UNIMPLEMENTED ("build coordinates of frame to render");
+  }
+  
+  
   /** */
   JobTicket&
   Dispatcher::accessJobTicket (FrameCoord const& frameID)
diff --git a/src/proc/engine/time-anchor.hpp b/src/proc/engine/time-anchor.hpp
index d5812adf2..b9184af2a 100644
--- a/src/proc/engine/time-anchor.hpp
+++ b/src/proc/engine/time-anchor.hpp
@@ -109,7 +109,9 @@ namespace engine {
       build (play::Timings timings, int64_t startFrame)
         {
           const boost::rational DEFAULT_LATENCY_FACTOR (1,3);
-          Offset startDelay = timings.getFrameDurationAt(startFrame) * DEFAULT_LATENCY_FACTOR;
+          Offset startDelay(timings.outputLatency
+                          + timings.getFrameDurationAt(startFrame) * DEFAULT_LATENCY_FACTOR
+                           );
           
           return TimeAnchor (timings,startDelay,startFrame);
         }
diff --git a/src/proc/play/timings.cpp b/src/proc/play/timings.cpp
index 564f65d45..d5288b356 100644
--- a/src/proc/play/timings.cpp
+++ b/src/proc/play/timings.cpp
@@ -59,6 +59,7 @@ namespace play {
   Timings::Timings (FrameRate fps)
     : grid_(buildStandardGridForFramerate(fps))
     , playbackUrgency (ASAP)
+    , outputLatency (Duration::NIL)
     { 
       ENSURE (grid_);
     }
@@ -90,7 +91,7 @@ namespace play {
   Duration
   Timings::getFrameDurationAt (int64_t refFrameNr)  const
   {
-    return Offset (grid_->timeOf(frameNr), grid_->timeOf(frameNr+1));
+    return Offset (grid_->timeOf(refFrameNr), grid_->timeOf(refFrameNr + 1));
   }
 
   

From 080d6913c65120b8e209f3a625e6922c60d8fe7b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 25 Mar 2012 01:56:16 +0100
Subject: [PATCH 449/496] fix a type converison problem

...better state explicitly we're requiring
a rational factor
---
 src/lib/time/timevalue.hpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp
index b01f295c8..66d8c617b 100644
--- a/src/lib/time/timevalue.hpp
+++ b/src/lib/time/timevalue.hpp
@@ -238,6 +238,13 @@ namespace time {
     return Offset(distance);
   }
   
+  template
+  inline Offset
+  operator* (boost::rational factor, Offset const& o)
+  {
+    return boost::rational(factor.numerator(), factor.denominator()) * o;
+  }
+  
   /** stretch offset by a possibly fractional factor */
   Offset
   operator* (boost::rational factor, Offset const& o);

From 08d266819d5676781d9f654127ed7ce279186fb4 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 18 Apr 2012 03:46:21 +0200
Subject: [PATCH 450/496] re-read my own code and pick up the design work

..next question is: how to shape the dispatcher interface,
in order to support ongoing chunk wise planning
of new jobs, including a continuation
---
 src/backend/real-clock.cpp                    |  1 +
 src/lib/time/grid.hpp                         |  2 +-
 src/proc/engine/dispatcher.hpp                |  4 +-
 src/proc/engine/frame-coord.hpp               |  3 ++
 src/proc/engine/time-anchor.hpp               | 18 ++++++-
 src/proc/play/timings.hpp                     |  2 +-
 .../proc/engine/dispatcher-interface-test.cpp | 52 ++++++++++++++++---
 wiki/renderengine.html                        | 17 +++---
 8 files changed, 82 insertions(+), 17 deletions(-)

diff --git a/src/backend/real-clock.cpp b/src/backend/real-clock.cpp
index fc5fa5520..9e758057a 100644
--- a/src/backend/real-clock.cpp
+++ b/src/backend/real-clock.cpp
@@ -39,6 +39,7 @@ namespace backend {
     clock_gettime(CLOCK_REALTIME, &now);
                                       ////////////////////////////////////////////TODO shouldn't that be CLOCK_MONOTONIC ?
                                       ////////////////////////////////////////////TODO (what happens on ntp adjustments?)
+                                      ////////////////////////////////////////////TICKET #886
     
     gavl_time_t ticksSince1970 = now.tv_sec * GAVL_TIME_SCALE
                                + now.tv_nsec / MICRO_TICS_PER_NANOSECOND; 
diff --git a/src/lib/time/grid.hpp b/src/lib/time/grid.hpp
index fe068b385..f509cedd4 100644
--- a/src/lib/time/grid.hpp
+++ b/src/lib/time/grid.hpp
@@ -65,7 +65,7 @@ namespace time {
       virtual int64_t   gridPoint (TimeValue const& raw)  const   =0;
       virtual TimeValue gridAlign (TimeValue const& raw)  const   =0;
       virtual TimeValue timeOf    (int64_t gridPoint)     const   =0;
-      virtual TimeValue timeOf    (FSecs, int =0)         const   =0;
+      virtual TimeValue timeOf    (FSecs gridTime, int=0) const   =0;
     };
   
   typedef std::tr1::shared_ptr PGrid;
diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp
index 6f9d0af8d..babefd615 100644
--- a/src/proc/engine/dispatcher.hpp
+++ b/src/proc/engine/dispatcher.hpp
@@ -66,10 +66,10 @@ namespace engine {
       
       CoordBuilder onCalcStream (ModelPort modelPort, uint channel);
       
-      virtual FrameCoord locateFrameNext (uint frameCountOffset, TimeAnchor refPoint)   =0;
-      
       JobTicket& accessJobTicket (FrameCoord const&);
       
+    protected:      
+      virtual FrameCoord locateFrameNext (uint frameCountOffset, TimeAnchor refPoint)   =0;
     };
   
   
diff --git a/src/proc/engine/frame-coord.hpp b/src/proc/engine/frame-coord.hpp
index 11b751bb1..3eec79c19 100644
--- a/src/proc/engine/frame-coord.hpp
+++ b/src/proc/engine/frame-coord.hpp
@@ -55,6 +55,9 @@ namespace engine {
    * - the actual node to pull data from
    * - the segment holding that node                //////////////TODO : is this information really required??
    * 
+   * @remarks consider frame coordinates being "boiled down" to the actual values.
+   *          There is no reference to any kind of time grid (or similar session internals).
+   * 
    * @todo 1/12 WIP-WIP-WIP defining the invocation sequence and render jobs
    */
   class FrameCoord
diff --git a/src/proc/engine/time-anchor.hpp b/src/proc/engine/time-anchor.hpp
index b9184af2a..f944a5f86 100644
--- a/src/proc/engine/time-anchor.hpp
+++ b/src/proc/engine/time-anchor.hpp
@@ -58,6 +58,18 @@ namespace engine {
    * logical time of the session timeline, and the real
    * wall-clock time forming the deadline for rendering.
    * 
+   * \par internals
+   * The time anchor associates a nominal time, defined on the implicit time grid
+   * of some given Timings, with an actual wall clock time. Due to the usage situation,
+   * the TimeAnchor takes on the secondary meaning of a breaking point; everything \em before
+   * this anchor point has been handled during the preceding invocations of an ongoing chunk wise
+   * partial evaluation of the timeline to play back.
+   * - the #timings_ serve as an abstracted grid (actually, the implementation
+   *   does refer to a grid defined somewhere within the session)
+   * - the actual #anchorPoint_ is defined as frame number relative to this grid
+   * - this anchor point is scheduled to happen at a #relatedRealTime_, based on
+   *   system's real time clock scale (typically milliseconds since 1970)
+   * 
    * @todo 1/12 WIP-WIP-WIP just emerging as a concept
    *          /////////////////////////////////////////////////////////////////TODO: WIP needs to act as proxy for the grid, using the Timings
    */
@@ -68,7 +80,7 @@ namespace engine {
       Time relatedRealTime_;
       
       Time
-      expectedTimeofArival (play::Timings timings, Offset engineLatency)
+      expectedTimeofArival (play::Timings const& timings, Offset engineLatency)
         {
           TimeVar deadline;
           switch (timings.playbackUrgency)
@@ -119,6 +131,10 @@ namespace engine {
       //////////////////////////////////////////////////////////////////////////////////////////TODO: second builder function, relying on Engine timings
       
       
+      /** @internal for debugging and diagnostics:
+       * explicitly cast this TimeAnchor onto the underlying
+       * nominal time scale (as defined by the Timings of this
+       * playback or render process). */
       operator lib::time::TimeValue()  const
         {
           UNIMPLEMENTED ("representation of the Time Anchor closure");
diff --git a/src/proc/play/timings.hpp b/src/proc/play/timings.hpp
index 634ece475..b00909d1e 100644
--- a/src/proc/play/timings.hpp
+++ b/src/proc/play/timings.hpp
@@ -22,7 +22,7 @@
 
 /** @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.
+ ** A timing spec is used to anchor a 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.
diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp
index 005ebbb06..2ae15800f 100644
--- a/tests/components/proc/engine/dispatcher-interface-test.cpp
+++ b/tests/components/proc/engine/dispatcher-interface-test.cpp
@@ -32,13 +32,16 @@
 #include "lib/time/timevalue.hpp"
 #include "lib/time/timequant.hpp"
 #include "lib/singleton.hpp"
+#include "lib/itertools.hpp"
 #include "lib/util.hpp"
 
 //#include 
 //#include 
+#include 
 
 using test::Test;
 using util::isnil;
+using std::vector;
 //using std::cout;
 //using std::rand;
 
@@ -50,6 +53,8 @@ namespace test  {
   using lib::time::QuTime;
   using lib::time::FrameRate;
   using lib::time::Duration;
+  using lib::time::Offset;
+  using lib::time::TimeVar;
   using mobject::ModelPort;
   using play::Timings;
   
@@ -139,6 +144,7 @@ namespace test  {
                                              .relativeFrameLocation (refPoint, 15);
           CHECK (coordinates.absoluteNominalTime == Time(0,1));
           CHECK (coordinates.absoluteFrameNumber == 25);
+          CHECK (coordinates.remainingRealTime() <  Time(FSecs(25,25)));
           CHECK (coordinates.remainingRealTime() >= Time(FSecs(24,25)));
           CHECK (coordinates.modelPort == modelPort);
           CHECK (coordinates.channelNr == channel);
@@ -146,13 +152,10 @@ namespace test  {
           JobTicket& executionPlan = dispatcher.accessJobTicket (coordinates);
           CHECK (executionPlan.isValid());
           
-          JobTicket::JobsPlanning jobs = executionPlan.createJobsFor (coordinates);
-          CHECK (!isnil (jobs));
-          
-          Job headJob = *jobs;
-          CHECK (headJob.getNominalTime() == coordinates.absoluteNominalTime);
-          CHECK (0 < headJob.getInvocationInstanceID());
 #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
+          Job frameJob = executionPlan.createJobFor (coordinates);
+          CHECK (frameJob.getNominalTime() == coordinates.absoluteNominalTime);
+          CHECK (0 < frameJob.getInvocationInstanceID());
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
         }
       
@@ -164,8 +167,45 @@ namespace test  {
       void
       verify_standardDispatcherUsage()
         {
+          Dispatcher& dispatcher = mockDispatcher();
+          Timings timings (FrameRate::PAL);
+          ModelPort modelPort (getTestPort());
+          uint startFrame(10);
+          uint channel(0);
+          
+          TimeAnchor refPoint = TimeAnchor::build (timings, startFrame);
           
 #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
+          JobTicket::JobsPlanning jobs = dispatcher.onCalcStream(modelPort,channel)
+                                                   .establishNextJobs(refPoint);
+          
+          // Verify the planned Jobs
+          
+          CHECK (!isnil (jobs));
+          vector plannedChunk;
+          lib::append_all (jobs, plannedChunk);
+          
+          uint chunksize = plannedChunk.size();
+          CHECK (chunksize == 1 + timings.getPlanningChunkSize());
+          
+          TimeVar nextFrameStart = refPoint;
+          Offset expectedTimeIncrement (1, FrameRate::PAL);
+          for (uint i=0; i < chunksize-1; ++i )
+            {
+              Job& thisJob = plannedChunk[i];
+              CHECK (nextFrameStart == thisJob.getNominalTime());
+              CHECK (prevInvocationID < thisJob.getInvocationInstanceID());
+              
+              prevInvocationID = thisJob.getInvocationInstanceID();
+              nextFrameStart += expectedTimeIncrement;
+            }
+          
+          // but especially the last Job planned is a continuation
+          Job continuation = plannedChunk.back();
+          CHECK (META_JOB = continuation.getKind());
+          
+          // the Continuation will be scheduled sufficiently ahead of the planning end
+          CHECK (continuation.getNominalTime() < nextFrameStart - expectedTimeIncrement);
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
         }
     };
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index e4ebe4522..f9c468383 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -2138,7 +2138,7 @@ 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 [[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.
 
 !defining the dispatcher interface
@@ -2147,7 +2147,7 @@ The purpose of this interface is to support the planning of new jobs, for a give
 !!!Invocation situation
 * our //current starting point//&nbsp; is given as ''time anchor'' closure
 * we want to address a single frame, offset by a given number of frames relative to the current position
-* &rArr; the dispatcher returns the //fundamental coordinates// of that frame, namely
+* &rArr; the dispatcher returns the //fundamental coordinates// of that frame (''frame coordinates''), comprised of
 ** the absolute nominal time
 ** the absolute frame number
 ** a concrete ExitNode to pull for this frame
@@ -2158,6 +2158,11 @@ The purpose of this interface is to support the planning of new jobs, for a give
 !!!the ~TimeAnchor
 In fact, the whole process of playback or rendering is a continued series of exploration and evaluation. The outline of what needs to be calculated is determined continuously, proceeding in chunks of evaluation. The evaluation structure of the render engine is quite similar to the //fork-join//-pattern, just with the addition of timing constraints. This leads to an invocation pattern, where a partial evaluation happens from time to time. Each of those evaluations establishes a breaking point in time: everything //before// this point is settled and planned thus far. So, this point is an ''anchor'' or closure to root the next partial evaluation. More specifically, this ~TimeAnchor closure is the definitive binding between the abstract logical time of the session timeline, and the real wall-clock time forming the deadline for render evaluation.
 
+!!!related timelines and the frame grid
+The frame dispatch step joins and combines multiple time axes. Through the process of //scheduling,// the output generation is linked to real ''wall clock time'' and the dispatch step establishes the deadlines, taking the ''engine latency'' into account. As such, any render or playback process establishes an ''output time grid'', linking frame numbers to nominal output time or timecode, based on the ''output frame rate'' -- and both the framerate and the actual progression (speed) might be changed dynamically. But beyond all of this there is a third, basically independent temporal structure involved: the actual content to render, the ''effective session timeline''. While encoded in nominal, absolute, internal time values not necessarily grid aligned, in practice at least the //breaking points,// the temporal location where the content or structure of the pipeline changes, are aligned //to a grid used while creating the edit.// Yet this session timing structure need to be related in any way to the playback grid, nor is it necessarily the same as the ''source grid'' defined by the media data used to feed the pipeline.
+
+These complex relationships are reflected in the invocation structure leading to an individual frame job. The [[calculation stream|CalcStream]] provides the [[render/playback timings|Timings]], while the actual implementation of the dispatcher, backed by the [[Fixture]] and thus linked to the session models, gets to relate the effective nominal time, the frame number, the exit node and the //processing function.//
+
 !!!producing actual jobs
 The JobTicket is created on demand, specialised for a single [[segment|Segmentation]] of the timeline and a single [[feed|Feed]] of data frames to be pulled from a ModelPort. Thus, in turn, this means that all frames and all channels within that realm will rely on the same job ticket -- which is a //higher order function,// a function producing another function: when provided with the actual channel number and the specific frame coordinates, the job ticket produces a [[concrete job definition|RenderJob]], which itself is a function to be invoked by the [[scheduler|Scheduler]] just in time.
 
@@ -3163,7 +3168,7 @@ 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, 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.
@@ -3173,7 +3178,7 @@ Model ports are the effective, resulting outputs of each timeline; additional po
 !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.
 
-Model ports are represented by small non-copyable descriptor objects with distinct identity, which are owned and managed by the [[Fixture]]. Clients are bound to resolve a model port on each usage, as configuration changes within the model might cause ports to appear and decease. To stress this usage pattern, actually {{{ModelPort}}} instances are small copyable value objects (smart handles), which can be used to access data within an opaque registry. Each model port belongs to a specific Timeline and is aware of this association, as is the timeline, allowing to get the collection of all ports of a given timeline. Besides, within the Fixture each model port refers to a specific [[segmentation of the time axis|Segmentation]] (relevant for this special timeline actually). Thus, with the help of this segmentation, a model port can yield an ExitNode to pull frames for a given time.
+Model ports are represented by small non-copyable descriptor objects with distinct identity, which are owned and managed by the [[Fixture]]. Clients are mandated to resolve a model port on each usage, as configuration changes within the model might cause ports to appear and decease. To stress this usage pattern, actually {{{ModelPort}}} instances are small copyable value objects (smart handles), which can be used to access data within an opaque registry. Each model port belongs to a specific Timeline and is aware of this association, as is the timeline, allowing to get the collection of all ports of a given timeline. Besides, within the Fixture each model port refers to a specific [[segmentation of the time axis|Segmentation]] (relevant for this special timeline actually). Thus, with the help of this segmentation, a model port can yield an ExitNode to pull frames for a given time.
 
 
@@ -5386,7 +5391,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
@@ -5403,7 +5408,7 @@ When [[building the fixture|BuildFixture]], ~MObjects -- as handled by their Pla
 ;(2) commit stage
 : -- after the build process(es) are completed, the new fixture gets ''committed'', thus becoming the officially valid state to be rendered. As render processes might be going on in parallel, some kind of locking or barrier is required. It seems advisable to make the change into a single atomic hot-swap. Meaning we'd get a single access point to be protected. But there is another twist: We need to find out which render processes to cancel and restart, to pick up the changes introduced by this build process -- which might include adding and deleting of timelines as a whole, and any conceivable change to the segmentation grid. Because of the highly dynamic nature of the placements, on the other hand it isn't viable to expect the high-level model to provide this information. Thus we need to find out about a ''change coverage'' at this point. We might expand on that idea to //prune any new segments which aren't changed.// This way, only a write barrier would be necessary on switching the actually changed segments, and any render processes touching these would be //tainted.// Old allocations could be released after all tainted processes are known to be terminated.
 ;(3) rendering use
-:Each play/render process employs a ''frame dispatch step'' to get the right exit node for pulling a given frame. From there on, the process proceeds into the [[processing nodes|ProcNodes]], interleaved with backend/scheduler actions due to splitting into individually scheduled jobs. The storage of these processing nodes and accompanying wiring descriptors is hooked up behind the individual segments, by sharing a common {{{AllocationCluster}}}. Yet the calculation of individual frames also depends on ''parameters'' and especially ''automation'' linked with objects in the high-level model. It is likely that there might be some sharing or some kind of additional communication interface, as the intention was to allow ''live changes'' to automated values. <br/>{{red{WIP 12/2010}}} details need to be worked out. &rarr; [[parameter wiring concept|Wiring]]
+:Each play/render process employs a ''frame dispatch step'' to get the right exit node for pulling a given frame (&rarr; [[Dispatcher|FrameDispatcher]]). From there on, the process proceeds into the [[processing nodes|ProcNode]], interleaved with backend/scheduler actions due to splitting into individually scheduled jobs. The storage of these processing nodes and accompanying wiring descriptors is hooked up behind the individual segments, by sharing a common {{{AllocationCluster}}}. Yet the calculation of individual frames also depends on ''parameters'' and especially ''automation'' linked with objects in the high-level model. It is likely that there might be some sharing or some kind of additional communication interface, as the intention was to allow ''live changes'' to automated values. <br/>{{red{WIP 12/2010}}} details need to be worked out. &rarr; [[parameter wiring concept|Wiring]]
 !!!observations
 * Storage and initialisation for explicit placements is an issue. We should strive at making that inline as much as possible.
 * the overall segmentation emerges from a sorting of time points, which are start points of explicit placements

From 79bd8b71e37d748525032041eb572dd003727bc3 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 20 Apr 2012 04:05:41 +0200
Subject: [PATCH 451/496] better treat the building of a continuation job
 separately

---
 src/proc/play/timings.cpp                     |  7 ++
 src/proc/play/timings.hpp                     |  8 +++
 .../proc/engine/dispatcher-interface-test.cpp | 66 +++++++++++++++++--
 3 files changed, 76 insertions(+), 5 deletions(-)

diff --git a/src/proc/play/timings.cpp b/src/proc/play/timings.cpp
index d5288b356..5e50878e7 100644
--- a/src/proc/play/timings.cpp
+++ b/src/proc/play/timings.cpp
@@ -121,6 +121,13 @@ namespace play {
   }
   
   
+  uint
+  Timings::getPlanningChunkSize()  const
+  {
+    UNIMPLEMENTED ("how to control the engine evaluation chunk size");
+  }
+  
+  
   Timings
   Timings::constrainedBy (Timings additionalConditions)
   {
diff --git a/src/proc/play/timings.hpp b/src/proc/play/timings.hpp
index b00909d1e..e0e4a9c34 100644
--- a/src/proc/play/timings.hpp
+++ b/src/proc/play/timings.hpp
@@ -125,6 +125,14 @@ namespace play {
        */
       Time getTimeDue()  const;
       
+      /** number of jobs to be planned and scheduled in one sway.
+       *  The continuous planning of additional frame calculation jobs
+       *  for playback or rendering proceeds in chunks of jobs
+       *  controlled by this chunk size.
+       */
+      uint getPlanningChunkSize() const;
+      
+      
       //////////////TODO further accessor functions here
       
       Timings constrainedBy (Timings additionalConditions);
diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp
index 2ae15800f..ffab7088c 100644
--- a/tests/components/proc/engine/dispatcher-interface-test.cpp
+++ b/tests/components/proc/engine/dispatcher-interface-test.cpp
@@ -37,11 +37,13 @@
 
 //#include 
 //#include 
+#include 
 #include 
 
 using test::Test;
 using util::isnil;
 using std::vector;
+using std::tr1::function;
 //using std::cout;
 //using std::rand;
 
@@ -55,6 +57,7 @@ namespace test  {
   using lib::time::Duration;
   using lib::time::Offset;
   using lib::time::TimeVar;
+  using lib::time::Time;
   using mobject::ModelPort;
   using play::Timings;
   
@@ -122,6 +125,7 @@ namespace test  {
         {
            verify_basicDispatch();
            verify_standardDispatcherUsage();
+           check_ContinuationBuilder();
         }
       
       
@@ -186,11 +190,11 @@ namespace test  {
           lib::append_all (jobs, plannedChunk);
           
           uint chunksize = plannedChunk.size();
-          CHECK (chunksize == 1 + timings.getPlanningChunkSize());
+          CHECK (chunksize == timings.getPlanningChunkSize());
           
           TimeVar nextFrameStart = refPoint;
           Offset expectedTimeIncrement (1, FrameRate::PAL);
-          for (uint i=0; i < chunksize-1; ++i )
+          for (uint i=0; i < chunksize; ++i )
             {
               Job& thisJob = plannedChunk[i];
               CHECK (nextFrameStart == thisJob.getNominalTime());
@@ -199,15 +203,67 @@ namespace test  {
               prevInvocationID = thisJob.getInvocationInstanceID();
               nextFrameStart += expectedTimeIncrement;
             }
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
+        }
+      
+      
+      /** @test usually at the end of each standard invocation,
+       * after scheduling a chunk of new Jobs, an additional
+       * continuation job is created to re-invoke this
+       * scheduling step.
+       * - the refPoint gets bumped beyond the planned segment
+       * - the continuation job embodies a suitable closure,
+       *   usable for self-re-invocation
+       */
+      void
+      check_ContinuationBuilder()
+        {
+          Dispatcher& dispatcher = mockDispatcher();
+          Timings timings (FrameRate::PAL);
+          ModelPort modelPort (getTestPort());
+          uint startFrame(10);
+          uint channel(0);
           
-          // but especially the last Job planned is a continuation
-          Job continuation = plannedChunk.back();
+          // prepare the rest of this test to be invoked as "continuation"
+          function testFunc = verify_invocation_of_Continuation;
+          
+          TimeAnchor refPoint = TimeAnchor::build (timings, startFrame);
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
+          JobTicket::JobsPlanning jobs = dispatcher.onCalcStream(modelPort,channel)
+                                                   .establishNextJobs(refPoint)
+                                                   .prepareContinuation(testFunc);
+          
+          // an additional "continuation" Job has been prepared....
+          Job continuation = lib::pull_last(jobs);
           CHECK (META_JOB = continuation.getKind());
           
+          uint nrJobs = timings.getPlanningChunkSize();
+          Duration frameDuration (1, FrameRate::PAL);
+          
           // the Continuation will be scheduled sufficiently ahead of the planning end
-          CHECK (continuation.getNominalTime() < nextFrameStart - expectedTimeIncrement);
+          CHECK (continuation.getNominalTime() < Time(refPoint) + (nrJobs-1) * frameDuration);
+          
+          // now invoke the rest of this test, which has been embedded into the continuation job.
+          // Since we passed testFunc as action for the continuation, we expect the invocation
+          // of the function verify_invocation_of_Continuation()
+          continuation.triggerJob();
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
         }
+      
+      /** action used as "continuation" in #check_ContinuationBuilder
+       *  This function expects to be invoked with a time anchor bumped up
+       *  to point exactly behind the end of the previously planned chunk of Jobs
+       */
+      static void
+      verify_invocation_of_Continuation (TimeAnchor nextRefPoint)
+        {
+          Timings timings (FrameRate::PAL);
+          uint startFrame(10);
+          uint nrJobs = timings.getPlanningChunkSize();
+          Duration frameDuration (1, FrameRate::PAL);
+          
+          CHECK (Time(nextRefPoint) == Time::ZERO + (startFrame + nrJobs) * frameDuration);
+        }
     };
   
   

From 5222384f8bee9929ce268862c656668c54a1a2bf Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 24 Apr 2012 05:06:05 +0200
Subject: [PATCH 452/496] try to circumvent ambiguity in overload resolution

we want to support various integral and rational factors
for linear chaining of time offsets
---
 src/lib/time/timevalue.hpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp
index 66d8c617b..c41989c30 100644
--- a/src/lib/time/timevalue.hpp
+++ b/src/lib/time/timevalue.hpp
@@ -154,7 +154,7 @@ namespace time {
       TimeVar& operator-= (TimeVar const& tx)  { t_ -= tx.t_; return *this; }
       
       // Supporting multiplication with integral factor
-      TimeVar& operator*= (int fact)           { t_ *= fact;  return *this; }
+      TimeVar& operator*= (int64_t fact)       { t_ *= fact;  return *this; }
       
       // Supporting sign flip
       TimeVar  operator-  ()         const     { return TimeVar(*this)*=-1; }
@@ -230,8 +230,9 @@ namespace time {
     return factor*distance;
   }
   
+  template
   inline Offset
-  operator* (int factor, Offset const& o)
+  operator* (INT factor, Offset const& o)
   {
     TimeVar distance(o);
     distance *= factor;

From 0ab773ab7c8ee64c2377954cd2dc4e4889498e69 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 24 Apr 2012 06:00:55 +0200
Subject: [PATCH 453/496] helper to pull all elements from an iterator,
 yielding the last one

---
 src/lib/itertools.hpp        | 27 ++++++++++++++++++++++++++-
 tests/lib/itertools-test.cpp | 16 ++++++++++++++++
 2 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/src/lib/itertools.hpp b/src/lib/itertools.hpp
index 12b2286cb..5c5b5f750 100644
--- a/src/lib/itertools.hpp
+++ b/src/lib/itertools.hpp
@@ -289,7 +289,7 @@ namespace lib {
         {
           return (cached_ && isOK_)
               || (cached_ = true
-                 &&(isOK_ = predicate_(*Raw::pipe())));            
+                 &&(isOK_ = predicate_(*Raw::pipe())));
         }
       
       void
@@ -540,6 +540,31 @@ namespace lib {
   }
   
   
+  template
+  inline typename IT::value_type
+  pull_last (IT iter)
+    {
+      typedef typename IT::value_type Val;
+      typedef wrapper::ItemWrapper Item;
+      
+      Item lastElm;
+      
+      while (iter)
+        {
+          lastElm = *iter;
+          ++iter;
+        }
+      
+      if (lastElm)
+        return *lastElm;
+      else
+        throw lumiera::error::State ("attempt to retrieve the last element "
+                                     "of an exhausted or empty iterator"
+                                    ,lumiera::error::LUMIERA_ERROR_ITER_EXHAUST);
+    }
+  
+  
+  
   /** filters away repeated values
    *  emitted by source iterator */
   template
diff --git a/tests/lib/itertools-test.cpp b/tests/lib/itertools-test.cpp
index 6b47a69a7..226e7376b 100644
--- a/tests/lib/itertools-test.cpp
+++ b/tests/lib/itertools-test.cpp
@@ -23,6 +23,7 @@
 
 
 #include "lib/test/run.hpp"
+#include "lib/test/test-helper.hpp"
 #include "lib/util.hpp"
 #include "lib/util-foreach.hpp"
 
@@ -47,6 +48,8 @@ namespace test{
   using std::endl;
   using std::rand;
   
+  using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
+  
   
   
   namespace { // Test data
@@ -110,6 +113,8 @@ namespace test{
           verify_filterRepetitions();
           
           buildTransformingIterator (source.begin());
+          
+          verifyPullLast(source.begin());
         }
       
       
@@ -253,6 +258,17 @@ namespace test{
         }
       
       
+      void
+      verifyPullLast(Iter const& ii)
+        {
+          Iter::value_type lastElm = pull_last (ii);
+          CHECK (1 == lastElm);     // TestSource holds a decreasing sequence of numbers ending with 1
+          
+          Iter emptyIterator;
+          CHECK (isnil (emptyIterator));
+          
+          VERIFY_ERROR (ITER_EXHAUST, pull_last(emptyIterator) );
+        }
     };
   
   LAUNCHER (IterTools_test, "unit common");

From bb43c03ef96820ed7728fa82df9f1122699364d3 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 26 Apr 2012 04:11:31 +0200
Subject: [PATCH 454/496] stub all the job generating functions required for
 the dispatcher interface

---
 src/proc/engine/dispatcher.cpp                | 24 +++++++++++++++++--
 src/proc/engine/dispatcher.hpp                | 22 +++++++++++++----
 src/proc/engine/job-ticket.hpp                |  4 ++--
 src/proc/engine/job.hpp                       | 17 ++++++++++++-
 .../proc/engine/dispatcher-interface-test.cpp | 11 +++++----
 5 files changed, 64 insertions(+), 14 deletions(-)

diff --git a/src/proc/engine/dispatcher.cpp b/src/proc/engine/dispatcher.cpp
index 12c51d981..07a22728c 100644
--- a/src/proc/engine/dispatcher.cpp
+++ b/src/proc/engine/dispatcher.cpp
@@ -34,7 +34,7 @@ namespace engine {
   
   
   /** */
-  Dispatcher::CoordBuilder
+  Dispatcher::JobBuilder
   Dispatcher::onCalcStream (ModelPort modelPort, uint channel)
   {
     UNIMPLEMENTED ("build coordinates of frame to render");
@@ -43,12 +43,32 @@ namespace engine {
   
   /** */
   FrameCoord
-  Dispatcher::CoordBuilder::relativeFrameLocation (TimeAnchor refPoint, uint frameCountOffset)
+  Dispatcher::JobBuilder::relativeFrameLocation (TimeAnchor refPoint, uint frameCountOffset)
   {
     UNIMPLEMENTED ("build coordinates of frame to render");
   }
   
   
+  /** */
+  Dispatcher::JobBuilder& 
+  Dispatcher::JobBuilder::establishNextJobs (TimeAnchor refPoint)
+  {
+    UNIMPLEMENTED ("job planning and generation");
+    return *this;
+  }
+  
+  
+  /** */
+  Dispatcher::JobBuilder&
+  Dispatcher::JobBuilder::prepareContinuation (function delayedAction)
+    {
+      UNIMPLEMENTED ("wrap already planned jobs, appending a continuation to pick up later");
+      return *this;
+    }
+
+
+  
+  
   /** */
   JobTicket&
   Dispatcher::accessJobTicket (FrameCoord const& frameID)
diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp
index babefd615..e7409be4a 100644
--- a/src/proc/engine/dispatcher.hpp
+++ b/src/proc/engine/dispatcher.hpp
@@ -33,12 +33,13 @@
 #include "lib/time/timevalue.hpp"
 
 #include 
-
+#include 
 
 
 namespace proc {
 namespace engine {
   
+  using std::tr1::function;
   using mobject::ModelPort;
   using lib::time::TimeSpan;
   using lib::time::FSecs;
@@ -52,19 +53,32 @@ namespace engine {
   class Dispatcher
     : boost::noncopyable
     {
-      struct CoordBuilder
+      struct JobBuilder
         {
           Dispatcher& dispatcher_;
           ModelPort modelPort_;
           uint channel_;
           
-          FrameCoord relativeFrameLocation (TimeAnchor refPoint, uint frameCountOffset);    
+          FrameCoord relativeFrameLocation (TimeAnchor refPoint, uint frameCountOffset);
+          
+          JobBuilder& establishNextJobs (TimeAnchor refPoint);
+          
+          JobBuilder& prepareContinuation (function delayedAction);
+          
+          operator JobTicket::JobsPlanning()
+            {
+              UNIMPLEMENTED ("how to represent the closure for defining and scheduling jobs");
+              
+              ////////TODO: use a closure based on the JobTicket (which can be accessed through the dispatcher backlink)
+              ////////////  Because this closure is what backs the IterSource, it needs to have a reliable storage though. 
+            }
+
         };
       
     public:
       virtual ~Dispatcher();  ///< this is an interface
       
-      CoordBuilder onCalcStream (ModelPort modelPort, uint channel);
+      JobBuilder onCalcStream (ModelPort modelPort, uint channel);
       
       JobTicket& accessJobTicket (FrameCoord const&);
       
diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index 8fb4034ff..ef54308a9 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -99,8 +99,8 @@ namespace engine {
         }
       
       
-      JobsPlanning
-      createJobsFor (FrameCoord coordinates)
+      Job
+      createJobFor (FrameCoord coordinates)
         {
           UNIMPLEMENTED ("job planning and generation");
         }
diff --git a/src/proc/engine/job.hpp b/src/proc/engine/job.hpp
index c184b77e4..b8349bb98 100644
--- a/src/proc/engine/job.hpp
+++ b/src/proc/engine/job.hpp
@@ -42,6 +42,13 @@ enum JobState
     ABORTED    ///< got aborted
   };
 
+enum JobKind
+  {
+    CALC_JOB,  ///< calculating frame data, CPU bound
+    LOAD_JOB,  ///< accessing prerequisites, IO bound
+    META_JOB   ///< render process self organisation
+  };
+
 
 /** 
  * closure representing the execution context of a job.
@@ -109,7 +116,8 @@ typedef lumiera_jobDescriptor* LumieraJobDescriptor;
 
 #ifdef __cplusplus  /* ============== C++ Interface ================= */
 
-#include "proc/common.hpp"
+//#include "proc/common.hpp"
+#include "lib/error.hpp"
 //#include "proc/state.hpp"
 #include "lib/time/timevalue.hpp"
 //#include "lib/time/timequant.hpp"
@@ -172,9 +180,16 @@ using lib::time::Time;
           return this->parameter.invoKey;
         }
       
+      JobKind getKind()  const;
       bool isValid()  const;
     };
   
+    
+  inline JobKind
+  Job::getKind()  const
+    {
+      UNIMPLEMENTED ("need a better C-representation of the job");
+    }
   
   
 }} // namespace proc::engine
diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp
index ffab7088c..ab721989d 100644
--- a/tests/components/proc/engine/dispatcher-interface-test.cpp
+++ b/tests/components/proc/engine/dispatcher-interface-test.cpp
@@ -156,10 +156,10 @@ namespace test  {
           JobTicket& executionPlan = dispatcher.accessJobTicket (coordinates);
           CHECK (executionPlan.isValid());
           
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
           Job frameJob = executionPlan.createJobFor (coordinates);
           CHECK (frameJob.getNominalTime() == coordinates.absoluteNominalTime);
           CHECK (0 < frameJob.getInvocationInstanceID());
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
         }
       
@@ -179,7 +179,6 @@ namespace test  {
           
           TimeAnchor refPoint = TimeAnchor::build (timings, startFrame);
           
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
           JobTicket::JobsPlanning jobs = dispatcher.onCalcStream(modelPort,channel)
                                                    .establishNextJobs(refPoint);
           
@@ -192,7 +191,8 @@ namespace test  {
           uint chunksize = plannedChunk.size();
           CHECK (chunksize == timings.getPlanningChunkSize());
           
-          TimeVar nextFrameStart = refPoint;
+          TimeVar nextFrameStart (refPoint);
+          InvocationInstanceID prevInvocationID(0);
           Offset expectedTimeIncrement (1, FrameRate::PAL);
           for (uint i=0; i < chunksize; ++i )
             {
@@ -203,6 +203,7 @@ namespace test  {
               prevInvocationID = thisJob.getInvocationInstanceID();
               nextFrameStart += expectedTimeIncrement;
             }
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
         }
       
@@ -228,14 +229,13 @@ namespace test  {
           function testFunc = verify_invocation_of_Continuation;
           
           TimeAnchor refPoint = TimeAnchor::build (timings, startFrame);
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
           JobTicket::JobsPlanning jobs = dispatcher.onCalcStream(modelPort,channel)
                                                    .establishNextJobs(refPoint)
                                                    .prepareContinuation(testFunc);
           
           // an additional "continuation" Job has been prepared....
           Job continuation = lib::pull_last(jobs);
-          CHECK (META_JOB = continuation.getKind());
+          CHECK (META_JOB == continuation.getKind());
           
           uint nrJobs = timings.getPlanningChunkSize();
           Duration frameDuration (1, FrameRate::PAL);
@@ -247,6 +247,7 @@ namespace test  {
           // Since we passed testFunc as action for the continuation, we expect the invocation
           // of the function verify_invocation_of_Continuation()
           continuation.triggerJob();
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
         }
       

From 0320bc4b2ca7f7df6f506c08bc882dfa7a237d69 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 27 Apr 2012 18:59:08 +0200
Subject: [PATCH 455/496] considering the relation of Job and JobClosure

---
 src/proc/engine/frame-coord.hpp                     |  1 +
 src/proc/engine/job-ticket.hpp                      |  7 +++++++
 src/proc/engine/job.cpp                             | 13 +++++++++++++
 src/proc/engine/job.hpp                             | 13 +++----------
 .../proc/engine/dispatcher-interface-test.cpp       |  4 ++--
 5 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/src/proc/engine/frame-coord.hpp b/src/proc/engine/frame-coord.hpp
index 3eec79c19..c211b1193 100644
--- a/src/proc/engine/frame-coord.hpp
+++ b/src/proc/engine/frame-coord.hpp
@@ -82,6 +82,7 @@ namespace engine {
       Duration remainingRealTime()
         {
           UNIMPLEMENTED ("deterine the real wall clock time amount left until deadline");
+                                                ////////////////////////TODO the coordinates can't answer that question! Who else can?
         }
 
       
diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index ef54308a9..7d6b973c9 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -57,6 +57,13 @@ namespace engine {
     {
     public:
       
+      JobKind
+      getJobKind()  const
+        {
+          UNIMPLEMENTED ("representation of JobTicket and JobClosure");
+        }
+
+      
       bool
       verify (Time nominalJobTime)  const
         {
diff --git a/src/proc/engine/job.cpp b/src/proc/engine/job.cpp
index 0af1b192f..bc7bd0bd9 100644
--- a/src/proc/engine/job.cpp
+++ b/src/proc/engine/job.cpp
@@ -73,6 +73,19 @@ namespace engine {
   }
   
   
+  /** find out about the classification of this job.
+   *  Typically its not necessary for the normal scheduling of
+   *  Jobs to know anything beyond the contents of the #lumiera_jobDescriptor,
+   *  but the JobClosure is able to answer any additional introspection queries
+   */
+  JobKind
+  Job::getKind()  const
+  {
+    REQUIRE (isValid());
+    myClosure(this).getJobKind();
+  }
+    
+  
   /** Render Job self verification.
    *  performs a parameter consistency check
    *  including a call-back to the defining JobTicket
diff --git a/src/proc/engine/job.hpp b/src/proc/engine/job.hpp
index b8349bb98..2e60f3c98 100644
--- a/src/proc/engine/job.hpp
+++ b/src/proc/engine/job.hpp
@@ -67,8 +67,8 @@ typedef struct lumiera_jobClosure* LumieraJobClosure;
  */
 struct lumiera_jobParameter_struct
   {
-    InvocationInstanceID invoKey;
     gavl_time_t nominalTime;
+    InvocationInstanceID invoKey;
     //////////////////////////////////////////////////////////////TODO: place an additional parameter value here, or make the instanceID globally unique?
   };
 typedef struct lumiera_jobParameter_struct lumiera_jobParameter;
@@ -78,8 +78,8 @@ typedef lumiera_jobParameter* LumieraJobParameter;
 /** complete definition of an individual job */
 struct lumiera_jobDefinition_struct
   {
-    LumieraJobClosure jobClosure;
-    lumiera_jobParameter parameter;
+    LumieraJobClosure jobClosure;     ///< type and context of the job, including the actual functor
+    lumiera_jobParameter parameter;   ///< the "moving parts" for this individual invocation (Job)
   };
 typedef struct lumiera_jobDefinition_struct lumiera_jobDefinition;
 
@@ -184,13 +184,6 @@ using lib::time::Time;
       bool isValid()  const;
     };
   
-    
-  inline JobKind
-  Job::getKind()  const
-    {
-      UNIMPLEMENTED ("need a better C-representation of the job");
-    }
-  
   
 }} // namespace proc::engine
 
diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp
index ab721989d..6c037b602 100644
--- a/tests/components/proc/engine/dispatcher-interface-test.cpp
+++ b/tests/components/proc/engine/dispatcher-interface-test.cpp
@@ -148,7 +148,7 @@ namespace test  {
                                              .relativeFrameLocation (refPoint, 15);
           CHECK (coordinates.absoluteNominalTime == Time(0,1));
           CHECK (coordinates.absoluteFrameNumber == 25);
-          CHECK (coordinates.remainingRealTime() <  Time(FSecs(25,25)));
+          CHECK (coordinates.remainingRealTime() <  Time(FSecs(25,25))); ////////////////////////TODO the coordinates can't answer that question! Who else can?
           CHECK (coordinates.remainingRealTime() >= Time(FSecs(24,25)));
           CHECK (coordinates.modelPort == modelPort);
           CHECK (coordinates.channelNr == channel);
@@ -240,7 +240,7 @@ namespace test  {
           uint nrJobs = timings.getPlanningChunkSize();
           Duration frameDuration (1, FrameRate::PAL);
           
-          // the Continuation will be scheduled sufficiently ahead of the planning end
+          // the Continuation will be scheduled sufficiently ahead of the currently planned chunk's end
           CHECK (continuation.getNominalTime() < Time(refPoint) + (nrJobs-1) * frameDuration);
           
           // now invoke the rest of this test, which has been embedded into the continuation job.

From 4ceec90b9eff4d7dad56735142f515840139b4ae Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 28 Apr 2012 04:18:00 +0200
Subject: [PATCH 456/496] design decision how to invoke a job

without any trickery, we'll always get two indirections.
Thus, the decision is to turn the 2nd indirection
into a VTable call; this way, basically the JobClosure
also acts as job functor itself.
---
 src/proc/engine/job-ticket.cpp | 95 ++++++++++++++++++++++++++++++++++
 src/proc/engine/job-ticket.hpp | 19 +++----
 src/proc/engine/job.cpp        |  5 +-
 src/proc/engine/job.hpp        |  5 +-
 4 files changed, 108 insertions(+), 16 deletions(-)
 create mode 100644 src/proc/engine/job-ticket.cpp

diff --git a/src/proc/engine/job-ticket.cpp b/src/proc/engine/job-ticket.cpp
new file mode 100644
index 000000000..a5df9a262
--- /dev/null
+++ b/src/proc/engine/job-ticket.cpp
@@ -0,0 +1,95 @@
+/*
+  JobTicket  -  execution plan for render jobs
+
+  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.
+
+* *****************************************************/
+
+
+/** @file job-ticket.cpp 
+ ** Implementation details of preparing and performing job invocations.
+ ** 
+ ** @todo WIP-WIP-WIP 3/2012
+ ** @see Job
+ **
+ */
+
+
+#include "proc/engine/job-ticket.hpp"
+
+
+namespace proc {
+namespace engine {
+  
+  namespace { // Details...
+    
+  } // (END) Details...
+  
+  
+//  using mobject::Placement;
+//  using mobject::session::Effect;
+  
+  
+  JobClosure::~JobClosure() { }
+  
+  
+  
+  class FrameJobClosure
+    : public JobClosure
+    {
+      // data members?
+      
+    private: /* === JobClosure Interface === */
+      
+      JobKind
+      getJobKind()  const
+        {
+          return CALC_JOB;
+        }
+      
+      bool
+      verify (Time nominalJobTime)  const
+        {
+          UNIMPLEMENTED ("access the underlying JobTicket and verify the given job time is within the relevant timeline segment");
+          return false;
+        }
+      
+      void
+      invokeJobOperation (JobParameter parameter)
+        {
+          UNIMPLEMENTED ("representation of the job functor");
+        }
+      
+      
+      void
+      signalFailure (JobParameter parameter)
+        {
+          UNIMPLEMENTED ("what needs to be done when a job cant be invoked?");
+        }
+      
+    public:
+      
+      
+    };
+  
+  
+  
+  /**  */
+  
+  
+}} // namespace proc::engine
diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index 7d6b973c9..cbcaf04bb 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -56,23 +56,18 @@ namespace engine {
     : public lumiera_jobClosure
     {
     public:
+      virtual ~JobClosure();     ///< this is an interface
       
-      JobKind
-      getJobKind()  const
-        {
-          UNIMPLEMENTED ("representation of JobTicket and JobClosure");
-        }
-
       
-      bool
-      verify (Time nominalJobTime)  const
-        {
-          UNIMPLEMENTED ("access the underlying JobTicket and verify the given job time is within the relevant timeline segment");
-          return false;
-        }
+      virtual void invokeJobOperation (JobParameter parameter)  =0;
+      virtual void signalFailure      (JobParameter parameter)  =0;
+      
+      JobKind getJobKind()  const                               =0;
+      bool verify (Time nominalJobTime)  const                  =0;
     };
   
   
+  
   /**
    * execution plan for pulling a specific exit node.
    * Usable as blue print for generating actual render jobs.
diff --git a/src/proc/engine/job.cpp b/src/proc/engine/job.cpp
index bc7bd0bd9..8db0a5c77 100644
--- a/src/proc/engine/job.cpp
+++ b/src/proc/engine/job.cpp
@@ -62,7 +62,7 @@ namespace engine {
   void
   Job::triggerJob ()  const
   {
-    UNIMPLEMENTED ("how to access the JobTicket and build the RenderInvocation");
+    myClosure(this).invokeJobOperation (parameter);
   }
   
   
@@ -82,7 +82,7 @@ namespace engine {
   Job::getKind()  const
   {
     REQUIRE (isValid());
-    myClosure(this).getJobKind();
+    return myClosure(this).getJobKind();
   }
     
   
@@ -96,7 +96,6 @@ namespace engine {
     return this->jobClosure
         && this->parameter.invoKey > 0
         && myClosure(this).verify (getNominalTime());
-         ;
   }
   
   
diff --git a/src/proc/engine/job.hpp b/src/proc/engine/job.hpp
index 2e60f3c98..e9cfddcbd 100644
--- a/src/proc/engine/job.hpp
+++ b/src/proc/engine/job.hpp
@@ -135,6 +135,9 @@ using lib::time::Time;
 //  
 //class ExitNode;
   
+  typedef lumiera_jobParameter const& JobParameter;
+  
+  
   /**
    * Frame rendering task, represented as closure.
    * This functor encodes all information necessary to trigger
@@ -201,7 +204,7 @@ void lumiera_job_invoke  (lumiera_jobDefinition);
 /** signal inability to invoke this job
  * @todo decide what and how to communicate details of the failure
  * @remarks the purpose of this function is to allow for reliable checkpoints
- *          within the network of dependent jobs invocations, even after
+ *          within the network of dependent job invocations, even after
  *          missing deadlines or aborting a sequence of jobs */
 void lumiera_job_failure (lumiera_jobDefinition);
 

From ddff8b654b50a766db780e8c02cd8f373e823639 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 29 Apr 2012 04:43:24 +0200
Subject: [PATCH 457/496] WIP investigating the relation of Jobs, JobTicket and
 Closure in detail

---
 src/lib/scoped-collection.hpp                    |  2 +-
 src/proc/engine/job-ticket.cpp                   |  6 ++++++
 src/proc/engine/job-ticket.hpp                   |  7 ++-----
 .../proc/engine/dispatcher-interface-test.cpp    |  2 +-
 wiki/renderengine.html                           | 16 +++++++++++++---
 5 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp
index 729c88495..e1cba4ec2 100644
--- a/src/lib/scoped-collection.hpp
+++ b/src/lib/scoped-collection.hpp
@@ -30,7 +30,7 @@
  ** and never adjusted.
  ** 
  ** \par usage patterns
- ** The common ground for all usage of this container is to hold and some
+ ** The common ground for all usage of this container is to hold some elements
  ** with exclusive ownership; when the enclosing container goes out of scope,
  ** all the dtors of the embedded objects will be invoked. Frequently this
  ** side effect is the reason for using the container: we want to own some
diff --git a/src/proc/engine/job-ticket.cpp b/src/proc/engine/job-ticket.cpp
index a5df9a262..317bccc18 100644
--- a/src/proc/engine/job-ticket.cpp
+++ b/src/proc/engine/job-ticket.cpp
@@ -90,6 +90,12 @@ namespace engine {
   
   
   /**  */
+  Job
+  JobTicket::createJobFor (FrameCoord coordinates)
+  {
+    UNIMPLEMENTED ("job planning and generation");
+  }
+  
   
   
 }} // namespace proc::engine
diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index cbcaf04bb..1aefe3c15 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -101,11 +101,8 @@ namespace engine {
         }
       
       
-      Job
-      createJobFor (FrameCoord coordinates)
-        {
-          UNIMPLEMENTED ("job planning and generation");
-        }
+      Job createJobFor (FrameCoord coordinates);
+      
       
       bool
       isValid()  const
diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp
index 6c037b602..a26740e15 100644
--- a/tests/components/proc/engine/dispatcher-interface-test.cpp
+++ b/tests/components/proc/engine/dispatcher-interface-test.cpp
@@ -156,7 +156,7 @@ namespace test  {
           JobTicket& executionPlan = dispatcher.accessJobTicket (coordinates);
           CHECK (executionPlan.isValid());
           
-          Job frameJob = executionPlan.createJobFor (coordinates);
+          Job frameJob = executionPlan.createJobFor (coordinates); //////////////////////////////TODO this is wrong: we never create a single job!
           CHECK (frameJob.getNominalTime() == coordinates.absoluteNominalTime);
           CHECK (0 < frameJob.getInvocationInstanceID());
 #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index f9c468383..9d219506c 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -2797,13 +2797,19 @@ From experiences with other middle scale projects, I prefer having the test code
 [img[Example: Interfaces/Namespaces of the ~Session-Subsystems|uml/fig130053.png]]
 
-
-
The actual media data is rendered by [[individually scheduled render jobs|RenderJob]]. When preparing such jobs, in order to [[dispatch|FrameDispatcher]] the individual frames calculations required to make a given [[stream of calculations|CalcStream]] happen, a node planning phase is performed. The goal is to find out
+
+
The actual media data is rendered by [[individually scheduled render jobs|RenderJob]]. When preparing such jobs, in order to [[dispatch|FrameDispatcher]] the individual frames calculations required to make a given [[stream of calculations|CalcStream]] happen, a ''node planning phase'' is performed. The goal is to find out
 * what channel(s) to pull
 * what prerequisites to prepare
 * what parameters to provide
 The result of this planning phase is the {{{JobTicket}}}, a complete ''execution plan''.
-This planning is uniform for each segment and treated for all channels together, resulting in a nested tree structure of sub job tickets, allocated and stored alongside with the processing nodes and wiring descriptors to form the segment's data and descriptor network. Job tickets are //higher order functions:// entering a concrete frame number and channel into a given job ticket will produce an actual job descriptor, which in itself is again a function, to be invoked through the scheduler when it's time to trigger the actual calculations.
+This planning is uniform for each segment and treated for all channels together, resulting in a nested tree structure of sub job tickets, allocated and stored alongside with the processing nodes and wiring descriptors to form the segment's data and descriptor network. Job tickets are //higher order functions:// entering a concrete frame number and channel into a given job ticket will produce an actual job descriptor, which in itself is again a function, to be invoked through the scheduler when it's time to trigger the actual calculations. + +!Structure of the render Jobs created +To be more precise: in the general case, invoking the ~JobTicket with a given frame number will produce //multiple jobs// -- typically each frame rendering will require at least one further source media frame; and because Lumiera render jobs will //never block waiting on IO,// this source media access will be packaged as a separate [[resource retrieving job|ResourceJob]], to be treated specifically by the scheduler. + +To support the generation of multiple dependent jobs, a ~JobTicket might refer to further ~JobTickets corresponding to the prerequisites. These prerequisite ~JobTickets are the result of a classical recursive descent call into the top level ProcNode, to perform the planning step. There is always an 1:1 relation between actual jobs generated and the corresponding tickets. More precisely, for each possible job to generate there is a suitable ''job closure'', representing exactly the context information necessary to get that job started. To stress that point: a ProcNode might be configured such as to perform a series of recursive invocations of other prerequisite nodes right away (especially when those recursive invocations correspond just to further CPU bound calculations) -- yet still there is only //one//&nbsp; ~JobTicket, because there will be only one Job to invoke the whole sequence. On the other hand, when there is a prerequisite requiring an operation to be scheduled separately, a corresponding separate {{{JobClosure}}} will be referred. +
A major //business interface// &mdash; used by the layers for interfacing to each other; also to be invoked externally by scripts.
@@ -5305,6 +5311,10 @@ __see also__
 
 Rendering can be seen as a passive service available to the Backend, which remains in charge what to render and when. Render processes may be running in parallel without any limitations. All of the storage and data management falls into the realm of the Backend. The render nodes themselves are ''completely stateless'' &mdash; if some state is necessary for carrying out the calculations, the backend will provide a //state frame// in addition to the data frames.
+
+
a special kind of [[render job|RenderJob]], used to retrieve input data relying on external IO.
+Since a primary goal for the design of Lumiera's render engine is to use the available resources effectively, we try to avoid the situation where a working thread gets blocked waiting  for external IO to deliver data. Thus we create special marker jobs to keep track of any prerequisites and start the actual render calculations only if all these prerequisites are already fulfilled and all the required input data is available in RAM.
+
A distinct property of the Lumiera application is to rely on a rules based approach rather then on hard wired logic. When it comes to deciding and branching, a [[Query]] is issued, resulting either immediately in a {{{bool}}} result, or creating a //binding// for the variables used within the query. Commonly, there is more than one solution for a given query, allowing the result set to be enumerated.
 

From c33fcf9797bb4853a37567782fc384b4fd66ee3e Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 29 Apr 2012 19:04:18 +0200
Subject: [PATCH 458/496] WIP draft a linked list helper template

---
 src/lib/linked-elements.hpp        | 384 +++++++++++++++++++++++++++++
 src/lib/llist.h                    |  30 +--
 tests/40components.tests           |   5 +
 tests/lib/linked-elements-test.cpp | 348 ++++++++++++++++++++++++++
 4 files changed, 752 insertions(+), 15 deletions(-)
 create mode 100644 src/lib/linked-elements.hpp
 create mode 100644 tests/lib/linked-elements-test.cpp

diff --git a/src/lib/linked-elements.hpp b/src/lib/linked-elements.hpp
new file mode 100644
index 000000000..de6dd0d8f
--- /dev/null
+++ b/src/lib/linked-elements.hpp
@@ -0,0 +1,384 @@
+/*
+  LINKED-ELEMENTS.hpp  -  configurable intrusive single linked list template 
+
+  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.
+
+*/
+
+/** @file linked-elements.hpp
+ ** Intrusive single linked list with optional ownership.
+ ** This helper template allows to attach a number of tightly integrated
+ ** elements with low overhead. Typically, these elements are to be attached
+ ** once and never changed. Optionally, elements can be created in-place using
+ ** a custom allocation scheme; the holder might also take ownership. These
+ ** variations in functionality are controlled by policy templates.
+ ** 
+ ** The rationale for using this approach is
+ ** - variable number of elements
+ ** - explicit support for polymorphism
+ ** - no need to template the holder on the number of elements
+ ** - no heap allocations (contrast this to using std::vector)
+ ** - clear and expressive notation at the usage site
+ ** - the need to integrate tightly with a custom allocator
+ ** 
+ ** @note
+ ** @warning
+ ** 
+ ** @see LinkedElements_test
+ ** @see llist.h
+ ** @see ScopedCollection
+ ** @see itertools.hpp
+ */
+
+
+#ifndef LIB_LINKED_ELEMENTS_H
+#define LIB_LINKED_ELEMENTS_H
+
+
+#include "lib/error.hpp"
+#include "lib/iter-adapter.hpp"
+
+//#include 
+//#include 
+//#include 
+//#include 
+
+
+namespace lib {
+  
+  namespace error = lumiera::error;
+//  using error::LUMIERA_ERROR_CAPACITY;
+//  using error::LUMIERA_ERROR_INDEX_BOUNDS;
+  
+  
+  
+  /**
+   * TODO write type comment
+   */
+  template
+    < class N                    ///< node class or Base/Interface class for nodes
+    >
+  class LinkedElements
+    : boost::noncopyable
+    {
+      
+    public:
+      
+      
+      
+      
+     ~LinkedElements ()
+        { 
+          clear();
+        }
+      
+      explicit
+      LinkedElements (size_t maxElements)
+        : level_(0)
+        , capacity_(maxElements)
+        , elements_(new ElementHolder[maxElements])
+        { }
+      
+      /** creating a ScopedCollection in RAII-style:
+       *  The embedded elements will be created immediately.
+       *  Ctor fails in case of any error during element creation.
+       * @param builder functor to be invoked for each "slot".
+       *        It gets an ElementHolder& as parameter, and should
+       *        use this to create an object of some I-subclass
+       */
+      template
+      LinkedElements (size_t maxElements, CTOR builder)
+        : level_(0)
+        , capacity_(maxElements)
+        , elements_(new ElementHolder[maxElements])
+        {
+          populate_by (builder);
+        }
+      
+      /** variation of RAII-style: using a builder function,
+       *  which is a member of some object. This supports the
+       *  typical usage situation, where a manager object builds
+       *  a ScopedCollection of some components
+       * @param builder member function used to create the elements
+       * @param instance the owning class instance, on which the 
+       *        builder member function will be invoked ("this").
+       */
+      template
+      LinkedElements (size_t maxElements, void (TY::*builder) (ElementHolder&), TY * const instance)
+        : level_(0)
+        , capacity_(maxElements)
+        , elements_(new ElementHolder[maxElements])
+        {
+          populate_by (builder,instance);
+        }
+      
+      /* == some pre-defined Builders == */
+      
+      template
+      class PullFrom;          ///< fills by copy-constructing values pulled from the iterator IT
+      
+      template
+      static PullFrom
+      pull (IT iter)           ///< convenience shortcut to pull from any given Lumiera Forward Iterator
+        {
+          return PullFrom (iter);
+        }
+      
+      void
+      clear()
+        {
+          REQUIRE (level_ <= capacity_, "Storage corrupted");
+          
+          while (level_)
+            {
+              --level_;
+              try {
+                  elements_[level_].destroy();
+                }
+              ERROR_LOG_AND_IGNORE (progress, "Clean-up of element in ScopedCollection")
+            }
+        }
+      
+      
+      /** init all elements at once,
+       *  invoking a builder functor for each.
+       * @param builder to create the individual elements
+       *        this functor is responsible to invoke the appropriate
+       *        ElementHolder#create function, which places a new element
+       *        into the storage frame passed as parameter.
+       */
+      template
+      void
+      populate_by (CTOR builder)
+        try { 
+          while (level_ < capacity_)
+            {
+              ElementHolder& storageFrame (elements_[level_]);
+              builder (storageFrame);
+              ++level_;
+          } }
+        catch(...)
+          {
+            WARN (progress, "Failure while populating ScopedCollection. "
+                            "All elements will be discarded");
+            clear();
+            throw;
+          }
+      
+      /** variation of element initialisation,
+       *  invoking a member function of some manager object
+       *  for each collection element to be created.
+       */
+      template
+      void
+      populate_by (void (TY::*builder) (ElementHolder&), TY * const instance)
+        try { 
+          while (level_ < capacity_)
+            {
+              ElementHolder& storageFrame (elements_[level_]);
+              (instance->*builder) (storageFrame);
+              ++level_;
+          } }
+        catch(...)
+          {
+            WARN (progress, "Failure while populating ScopedCollection. "
+                            "All elements will be discarded");
+            clear();
+            throw;
+          }
+      
+      
+      
+      /** push a new element of default type
+       *  to the end of this container
+       * @note EX_STRONG */
+      I& appendNewElement() { return appendNew(); }
+      
+      
+      template< class TY >
+      TY&                                                  //_________________________________________
+      appendNew ()                                        ///< add object of type TY, using 0-arg ctor
+        {
+          __ensureSufficientCapacity();
+          TY& newElm = elements_[level_].template create();
+          ++level_;
+          return newElm;
+        }
+      
+      
+      template< class TY
+              , typename A1
+              >
+      TY&                                                  //_________________________________________
+      appendNew (A1 a1)                                   ///< add object of type TY, using 1-arg ctor
+        {
+          __ensureSufficientCapacity();
+          TY& newElm = elements_[level_].template create(a1);
+          ++level_;
+          return newElm;
+        }
+      
+      
+      template< class TY
+              , typename A1
+              , typename A2
+              >
+      TY&                                                  //_________________________________________
+      appendNew (A1 a1, A2 a2)                            ///< add object of type TY, using 2-arg ctor
+        {
+          __ensureSufficientCapacity();
+          TY& newElm = elements_[level_].template create(a1,a2);
+          ++level_;
+          return newElm;
+        }
+      
+      
+      template< class TY
+              , typename A1
+              , typename A2
+              , typename A3
+              >
+      TY&                                                  //_________________________________________
+      appendNew (A1 a1, A2 a2, A3 a3)                     ///< add object of type TY, using 3-arg ctor
+        {
+          __ensureSufficientCapacity();
+          TY& newElm = elements_[level_].template create(a1,a2,a3);
+          ++level_;
+          return newElm;
+        }
+      
+      
+      template< class TY
+              , typename A1
+              , typename A2
+              , typename A3
+              , typename A4
+              >
+      TY&                                                  //_________________________________________
+      appendNew (A1 a1, A2 a2, A3 a3, A4 a4)              ///< add object of type TY, using 4-arg ctor
+        {
+          __ensureSufficientCapacity();
+          TY& newElm = elements_[level_].template create(a1,a2,a3,a4);
+          ++level_;
+          return newElm;
+        }
+      
+      
+      template< class TY
+              , typename A1
+              , typename A2
+              , typename A3
+              , typename A4
+              , typename A5
+              >
+      TY&                                                  //_________________________________________
+      appendNew (A1 a1, A2 a2, A3 a3, A4 a4, A5 a5)       ///< add object of type TY, using 5-arg ctor
+        {
+          __ensureSufficientCapacity();
+          TY& newElm = elements_[level_].template create(a1,a2,a3,a4,a5);
+          ++level_;
+          return newElm;
+        }
+      
+      
+      
+      /* === Element access and iteration === */
+      
+      I&
+      operator[] (size_t index)  const
+        {
+          if (index < level_)
+            return elements_[index].accessObj();
+          
+          throw error::Logic ("Attempt to access not (yet) existing object in ScopedCollection"
+                             , LUMIERA_ERROR_INDEX_BOUNDS);
+        }
+      
+      
+      
+      typedef IterAdapter<      I *, const ScopedCollection *> iterator;
+      typedef IterAdapter const_iterator;
+      
+      
+      iterator       begin()       { return iterator       (this, _access_begin()); }
+      const_iterator begin() const { return const_iterator (this, _access_begin()); }
+      iterator       end ()        { return iterator();       }
+      const_iterator end ()  const { return const_iterator(); }
+      
+      
+      size_t  size ()        const { return level_;      }
+      size_t  capacity ()    const { return capacity_;   }
+      bool    empty ()       const { return 0 == level_; }
+      
+      
+      
+    private:
+      
+      
+      /* ==== internal callback API for the iterator ==== */
+      
+      /** Iteration-logic: switch to next position
+       * @note assuming here that the start address of the embedded object
+       *       coincides with the start of an array element (ElementHolder)
+       */
+      friend void
+      iterNext (const ScopedCollection*, I* & pos)
+      {
+        ElementHolder* & storageLocation = reinterpret_cast (pos);
+        ++storageLocation;
+      }
+      
+      friend void
+      iterNext (const ScopedCollection*, const I* & pos)
+      {
+        const ElementHolder* & storageLocation = reinterpret_cast (pos);
+        ++storageLocation;
+      }
+      
+      /** Iteration-logic: detect iteration end. */
+      template
+      friend bool
+      hasNext (const ScopedCollection* src, POS & pos)
+      {
+        REQUIRE (src);
+        if ((pos) && (pos < src->_access_end()))
+          return true;
+        else
+          {
+            pos = 0;
+            return false;
+      }   }
+      
+      
+      I* _access_begin() const { return & elements_[0].accessObj(); }
+      I* _access_end()   const { return & elements_[level_].accessObj(); }
+      
+    };
+  
+  
+  
+  
+  /* === Supplement: pre-defined  === */
+  
+  /** \par usage
+   */
+  
+  
+  
+} // namespace lib
+#endif
diff --git a/src/lib/llist.h b/src/lib/llist.h
index d5599a007..d81b77c00 100644
--- a/src/lib/llist.h
+++ b/src/lib/llist.h
@@ -33,18 +33,18 @@
  * this pointers point to the node itself. Note that these pointers can never ever become NULL.
  * This lists are used by using one node as 'root' node where its both pointers are the head/tail pointer to the actual list.
  * Care needs to be taken to ensure not to apply any operations meant to be applied to data nodes to the root node.
- * This way is the prefered way to use this lists.
+ * This way is the preferred way to use this lists.
  * Alternatively one can store only a chain of data nodes and use a LList pointer to point to the first item
  * (which might be NULL in case no data is stored). When using the 2nd approach care must be taken since most functions
  * below expect lists to have a root node.
  *
  * This header can be used in 2 different ways:
- * 1) (prerefered) just including it provides all functions as static inlined functions. This is the default
+ * 1) (preferred) just including it provides all functions as static inlined functions. This is the default
  * 2) #define LLIST_INTERFACE before including this header gives only the declarations
  *    #define LLIST_IMPLEMENTATION before including this header yields in definitions
  *    this can be used to generate a library. This is currently untested and not recommended.
  * The rationale for using inlined functions is that most functions are very small and likely to be used in performance critical parts.
- * Inlining can give a hughe performance and optimization improvement here.
+ * Inlining can give a huge performance and optimization improvement here.
  * The few functions which are slightly larger are expected to be the less common used ones, so inlining them too shouldn't be a problem either
  */
 
@@ -144,7 +144,7 @@ typedef llist ** LList_ref;
 
 /**
  * Iterate forward over a range.
- * @param start first node to be interated
+ * @param start first node to be iterated
  * @param end node after the last node be iterated
  * @param node pointer to the iterated node
  */
@@ -155,7 +155,7 @@ typedef llist ** LList_ref;
 
 /**
  * Iterate backward over a range.
- * @param rstart first node to be interated
+ * @param rstart first node to be iterated
  * @param rend node before the last node be iterated
  * @param node pointer to the iterated node
  */
@@ -281,7 +281,7 @@ LLIST_FUNC (unsigned llist_count (const_LList self),
             return cnt;
 );
 
-/* private, unlink self some any list but leaves self in a uninitialized state */
+/* private, unlink self some any list but leaves self in a uninitialised state */
 LLIST_FUNC (void llist_unlink_fast_ (LList self),
             LList nxt = self->next, pre = self->prev;
             nxt->prev = pre;
@@ -421,7 +421,7 @@ LLIST_FUNC (LList llist_inserbefore_range (LList self, LList start, LList end),
 
 /**
  * Swap a node with its next node.
- * @param self node to be advaced
+ * @param self node to be advanced
  * @return self
  * advancing will not stop at tail, one has to check that if this is intended
  */
@@ -534,13 +534,13 @@ LLIST_FUNC (LList llist_get_nth_stop (LList self, int n, const_LList stop),
 
 
 /**
- * The comparsion function function type.
- * certain sort and find functions depend on a user supplied coparsion function
- * @param a first operand for the comparsion
- * @param b second operand for the comparsion
+ * The comparison function function type.
+ * certain sort and find functions depend on a user supplied comparison function
+ * @param a first operand for the comparison
+ * @param b second operand for the comparison
  * @param extra user supplied data which passed through
- * @return shall return a value less than zero, zero, biggier than zero when
- *         a is less than, equal to, biggier than b
+ * @return shall return a value less than zero, zero, bigger than zero when
+ *         a is less than, equal to, bigger than b
  */
 typedef int (*llist_cmpfn)(const_LList a, const_LList b, void* extra);
 
@@ -623,11 +623,11 @@ LLIST_FUNC (LList llist_ufind (LList self, const_LList templ, llist_cmpfn cmp, v
 /**
  * Find a element in a sorted list.
  * searches the list until it find the searched element, exits searching when found an element
- * biggier than the searched one.
+ * bigger than the searched one.
  * @param self list to be searched
  * @param templ template for the element being searched
  * @param cmp function for comparing 2 nodes
- * @return pointer to the found LList element or NULL if nothing foound
+ * @return pointer to the found LList element or NULL if nothing found
  */
 LLIST_FUNC (LList llist_sfind (const_LList self, const_LList templ, llist_cmpfn cmp, void* extra),
             LLIST_FOREACH(self, node)
diff --git a/tests/40components.tests b/tests/40components.tests
index 4f60cf263..d55ef1507 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -532,6 +532,11 @@ out: can_IterForEach	 : Yes
 END
 
 
+PLANNED "Linked iterable elements" LinkedElements_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/util.hpp"
+
+#include "lib/linked-elements.hpp"
+#include "lib/test/testdummy.hpp"
+
+//#include 
+
+
+namespace lib {
+namespace test{
+  
+  namespace error = lumiera::error;
+  
+  
+  namespace { // test data...
+    
+    const uint NUM_ELEMENTS = 500;
+    
+    LUMIERA_ERROR_DEFINE(SUBVERSIVE, "undercover action");
+    
+    class Nummy
+      : public Dummy
+      {
+        
+      public:
+        Nummy* next;
+        
+        Nummy() 
+          : Dummy()
+          , next(0)
+          { }
+        
+        explicit
+        Nummy (int i)
+          : Dummy(i)
+          , next(0)
+          { }
+      };
+    
+    
+    inline uint
+    sum (uint n)
+    {
+      return n*(n+1) / 2;
+    }
+      
+  }//(End) subversive test data
+  
+  
+  
+  
+  using util::isnil;
+  using util::isSameObject;
+//  using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
+  
+  typedef LinkedElements List;
+  typedef LinkedElements ListNotOwner;
+  
+  
+  /********************************************************************
+   *  @test ScopedCollection manages a fixed set of objects, but these
+   *        child objects are noncopyable, may be polymorphic, an can
+   *        be created either all at once or chunk wise. The API is
+   *        similar to a vector and allows for element access
+   *        and iteration.
+   */
+  class LinkedElements_test : public Test
+    {
+      
+      virtual void
+      run (Arg)
+        {
+          simpleUsage();
+          iterating();
+          
+          verify_nonOwnership();
+          verify_ExceptionSafety();
+          populate_by_iterator();
+          verify_RAII_safety();
+        }
+      
+      
+      void
+      simpleUsage()
+        {
+          CHECK (0 == Dummy::checksum());
+          {
+            List elements;
+            CHECK (isnil (elements));
+            CHECK (0 == elements.size());
+            CHECK (0 == Dummy::checksum());
+            
+            elements.pushNew(1);
+            elements.pushNew(2);
+            elements.pushNew(3);
+            elements.pushNew(4);
+            elements.pushNew(5);
+            CHECK (!isnil (elements));
+            CHECK (5 == elements.size());
+            CHECK (0 != Dummy::checksum());
+            
+            CHECK (Dummy::checksum() == elements[0].getVal()
+                                      + elements[1].getVal()
+                                      + elements[2].getVal()
+                                      + elements[3].getVal()
+                                      + elements[4].getVal());
+            
+            elements.clear();
+            CHECK (isnil (elements));
+            CHECK (0 == elements.size());
+            CHECK (0 == Dummy::checksum());
+            
+            elements.pushNew();
+            elements.pushNew();
+            elements.pushNew();
+            
+            CHECK (3 == elements.size());
+            CHECK (0 != Dummy::checksum());
+          }
+          CHECK (0 == Dummy::checksum());
+        }
+      
+      
+      void
+      iterating()
+        {
+          CHECK (0 == Dummy::checksum());
+          {
+            List elements;
+            for (uint i=1; i<=NUM_ELEMENTS; ++i)
+              elements.pushNew(i);
+            
+            // since elements where pushed, 
+            // they should appear in reversed order
+            int check=NUM_ELEMENTS;
+            List::iterator ii = elements.begin();
+            while (ii)
+              {
+                CHECK (check == ii->getVal());
+                CHECK (check == ii->acc(+5) - 5);
+                --check;
+                ++ii;
+              }
+            CHECK (0 == check);
+            
+            
+            // Test the const iterator
+            List const& const_elm (elements);
+            check = NUM_ELEMENTS;
+            List::const_iterator cii = const_elm.begin();
+            while (cii)
+              {
+                CHECK (check == cii->getVal());
+                --check;
+                ++cii;
+              }
+            CHECK (0 == check);
+            
+            
+            // Verify correct behaviour of iteration end
+            CHECK (! (elements.end()));
+            CHECK (isnil (elements.end()));
+            
+            VERIFY_ERROR (ITER_EXHAUST, *elements.end() );
+            VERIFY_ERROR (ITER_EXHAUST, ++elements.end() );
+            
+            CHECK (ii == elements.end());
+            CHECK (cii == elements.end());
+            VERIFY_ERROR (ITER_EXHAUST, ++ii );
+            VERIFY_ERROR (ITER_EXHAUST, ++cii );
+            
+          }
+          CHECK (0 == Dummy::checksum());
+        }
+      
+      
+      void
+      verify_nonOwnership()
+        {
+          CHECK (0 == Dummy::checksum());
+          {
+            ListNotOwner elements;
+            CHECK (isnil (elements));
+            
+            Num<22> n2;
+            Num<44> n4;
+            Num<66> n6;
+            CHECK (22+44+66 == Dummy::checksum());
+            
+            elements.push(n2);
+            elements.push(n4);
+            elements.push(n6);
+            CHECK (!isnil (elements));
+            CHECK (3 == elements.size());
+            CHECK (22+44+66 == Dummy::checksum());   // not altered: we're referring the originals
+            
+            CHECK (66 == elements[0].getVal());
+            CHECK (44 == elements[1].getVal());
+            CHECK (22 == elements[2].getVal());
+            CHECK (isSameObject(n2, elements[2]));
+            CHECK (isSameObject(n4, elements[1]));
+            CHECK (isSameObject(n6, elements[0]));
+            
+            elements.clear();
+            CHECK (isnil (elements));
+            CHECK (22+44+66 == Dummy::checksum());   // referred elements unaffected
+          }
+          CHECK (0 == Dummy::checksum());
+        }
+      
+      
+      void
+      verify_ExceptionSafety()
+        {
+          CHECK (0 == Dummy::checksum());
+          {
+            List elements;
+            CHECK (isnil (elements));
+            
+            __triggerErrorAt(3);
+            
+            elements.pushNew(1);
+            elements.pushNew(2);
+            CHECK (1+2 == Dummy::checksum());
+            
+            VERIFY_ERROR (SUBVERSIVE, elements.pushNew(3) );
+            CHECK (1+2 == Dummy::checksum());
+            CHECK (2 == elements.size());
+            
+            CHECK (2 == elements[0].getVal());
+            CHECK (1 == elements[1].getVal());
+            
+            elements.clear();
+            CHECK (0 == Dummy::checksum());
+            __triggerError_reset();
+          }
+          CHECK (0 == Dummy::checksum());
+        }
+      
+      
+      void
+      populate_by_iterator()
+        {
+          CHECK (0 == Dummy::checksum());
+          {
+            Populator yieldSomeElements(NUM_ELEMENTS);
+            List elements (yieldSomeElements);
+            
+            CHECK (!isnil (elements));
+            CHECK (NUM_ELEMENTS == elements.size());
+            CHECK (sum(NUM_ELEMENTS) == Dummy::checksum());
+            
+            int check=NUM_ELEMENTS;
+            List::iterator ii = elements.begin();
+            while (ii)
+              {
+                CHECK (check == ii->getVal());
+                --check;
+                ++ii;
+              }
+            CHECK (0 == check);
+          }
+          CHECK (0 == Dummy::checksum());
+        }
+      
+      
+      void
+      verify_RAII_safety()
+        {
+          CHECK (0 == Dummy::checksum());
+          
+          __triggerErrorAt(3);
+          Populator yieldSomeElements(NUM_ELEMENTS);
+          VERIFY_ERROR (SUBVERSIVE, List(yieldSomeElements) );
+          
+          CHECK (0 == Dummy::checksum());
+          __triggerError_reset();
+        }
+      
+      
+      void
+      verify_customAllocator()
+        {
+          CHECK (0 == Dummy::checksum());
+          {
+            AllocationCluster allocator;
+            
+            ListCustomAllocated elements(allocator);
+            
+            elements.pushNew > (2);
+            elements.pushNew > (4,5);
+            elements.pushNew > (7,8,9);
+            
+            CHECK (sum(9) == Dummy::checksum());
+            CHECK (3 == allocator.size());
+            CHECK (1 == allocator.count >());
+            CHECK (1 == allocator.count >());
+            CHECK (1 == allocator.count >());
+            
+            CHECK (3 == elements.size());
+            CHECK (1+2 == elements[2].getVal());
+            CHECK (3+4+5 == elements[1].getVal());
+            CHECK (6+7+8+9 == elements[0].getVal());
+            
+            elements.clear();
+            CHECK (0 == allocator.size());
+            CHECK (0 == allocator.count >());
+            CHECK (0 == allocator.count >());
+            CHECK (0 == allocator.count >());
+            CHECK (0 == Dummy::checksum());
+          }
+          CHECK (0 == Dummy::checksum());
+        }
+    };
+  
+  
+  
+  LAUNCHER (LinkedElements_test, "unit common");
+  
+  
+}} // namespace lib::test
+

From 3ef1fb7697ec74cda965075741dbbd61e584b5cf Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 30 Apr 2012 04:28:16 +0200
Subject: [PATCH 459/496] linked list helper template finished and passes test

---
 src/lib/allocation-cluster.cpp        |  51 ++-
 src/lib/allocation-cluster.hpp        |  46 ++-
 src/lib/linked-elements.hpp           | 564 +++++++++++++++++---------
 src/proc/engine/job-ticket.hpp        |   4 +-
 tests/40components.tests              |   2 +-
 tests/lib/allocation-cluster-test.cpp |   5 +
 tests/lib/linked-elements-test.cpp    | 149 +++++--
 7 files changed, 591 insertions(+), 230 deletions(-)

diff --git a/src/lib/allocation-cluster.cpp b/src/lib/allocation-cluster.cpp
index 605e15a5c..68d75cbca 100644
--- a/src/lib/allocation-cluster.cpp
+++ b/src/lib/allocation-cluster.cpp
@@ -67,6 +67,8 @@ namespace lib {
       MemoryManager(TypeInfo info) : top_(0) { reset(info); }
       ~MemoryManager()                       { purge(); }
       
+      size_t size()  const;
+      
       void purge();
       void reset(TypeInfo info);
       
@@ -80,6 +82,17 @@ namespace lib {
   
   
   
+  /** the top_ index always points at the next slot 
+   *  not yet holding a finished, committed allocation.
+   *  Index is zero based, thus top_ == count of living objects
+   */
+  size_t
+  AllocationCluster::MemoryManager::size()  const
+  {
+    return top_;
+  }
+  
+  
   void
   AllocationCluster::MemoryManager::reset (TypeInfo info)
   {
@@ -240,14 +253,36 @@ namespace lib {
   }
   
   
+  /* === diagnostics helpers === */
+  
+  /** @return total number of objects
+   *          currently managed by this allocator */
+  size_t
+  AllocationCluster::size()  const
+  {
+    size_t size(0);
+    typedef ManagerTable::const_iterator Iter;
+    
+    for (Iter ii= typeHandlers_.begin(); ii != typeHandlers_.end(); ++ii )
+      if (*ii)
+        size += (*ii)->size();
+    
+    return size;
+  }
+  
+  
+  size_t
+  AllocationCluster::countActiveInstances (size_t& slot)  const
+  {
+    if (handler (slot))
+      return handler(slot)->size();
+    else
+      return 0;
+  }
+  
+  
+  
+  
   
   
 } // namespace lib
-
-/*
-// Local Variables:
-// mode: C
-// c-file-style: "gnu"
-// indent-tabs-mode: nil
-// End:
-*/
diff --git a/src/lib/allocation-cluster.hpp b/src/lib/allocation-cluster.hpp
index 12f9e9a9f..92b72eae0 100644
--- a/src/lib/allocation-cluster.hpp
+++ b/src/lib/allocation-cluster.hpp
@@ -82,6 +82,12 @@ namespace lib {
    *          to be used within the builder, which executes in a dedicated
    *          thread. Thus I doubt lock contention could be a problem and
    *          we can avoid using a mutex per instance. Re-evaluate this!
+   * @todo    currently all AllocationCluster instances share the same type-IDs.
+   *          When used within different usage contexts this leads to some slots
+   *          remaining empty, because not every situation uses any type encountered.
+   *          wouldn't it be desirable to have multiple distinct contexts, each with
+   *          its own set of Type-IDs and maybe also separate locking?
+   *          Is this issue worth the hassle?            //////////////////////////////TICKET #169
    */
   class AllocationCluster
     : boost::noncopyable
@@ -133,6 +139,14 @@ namespace lib {
         }
       
       
+      /* === diagnostics === */
+      
+      size_t size()  const;
+      
+      template
+      size_t count() const;
+      
+      
     private:
       /** initiate an allocation for the given type */
       template
@@ -153,7 +167,7 @@ namespace lib {
       
       /**
        * low-level memory manager responsible for
-       * the allocations of one specific type.  
+       * the allocations of one specific type.
        */
       class MemoryManager;
       
@@ -167,19 +181,27 @@ namespace lib {
       
       
       typedef ScopedPtrHolder HMemManager;
-      typedef Allocator_TransferNoncopyable Allo; 
+      typedef Allocator_TransferNoncopyable Allo;
       typedef std::vector ManagerTable;
       
-      ManagerTable typeHandlers_;  ///< table of active MemoryManager instances 
+      ManagerTable typeHandlers_;  ///< table of active MemoryManager instances
       
       
-      HMemManager& 
+      
+      HMemManager&
       handler (size_t slot)
         {
           ASSERT (0
+  size_t
+  AllocationCluster::count()  const
+  {
+    return countActiveInstances (TypeSlot::get());
+  }
+  
+  
+  
   
   
 } // namespace lib
diff --git a/src/lib/linked-elements.hpp b/src/lib/linked-elements.hpp
index de6dd0d8f..68c1d375f 100644
--- a/src/lib/linked-elements.hpp
+++ b/src/lib/linked-elements.hpp
@@ -24,8 +24,8 @@
  ** Intrusive single linked list with optional ownership.
  ** This helper template allows to attach a number of tightly integrated
  ** elements with low overhead. Typically, these elements are to be attached
- ** once and never changed. Optionally, elements can be created in-place using
- ** a custom allocation scheme; the holder might also take ownership. These
+ ** once and never changed. Optionally, elements can be created  using a
+ ** custom allocation scheme; the holder might also take ownership. These
  ** variations in functionality are controlled by policy templates.
  ** 
  ** The rationale for using this approach is
@@ -34,10 +34,20 @@
  ** - no need to template the holder on the number of elements
  ** - no heap allocations (contrast this to using std::vector)
  ** - clear and expressive notation at the usage site
+ ** - convenient access through Lumiera Forward Iterators
  ** - the need to integrate tightly with a custom allocator
  ** 
- ** @note
- ** @warning
+ ** @note this linked list container is \em intrusive and thus needs the help
+ **       of the element type, which must provide a pointer member \c next.
+ **       Consequently, each such node element can't be member in
+ **       multiple collections at the same time
+ ** @note as usual, any iterator relies on the continued existence and
+ **       unaltered state of the container. There is no sanity check.
+ ** @warning this container is deliberately not threadsafe
+ ** @warning be sure to understand the implications of taking ownership:
+ **       When adding an existing node element (by pointer) to a LinkedElements
+ **       list which takes ownership, the list will attempt to destroy or de-allocate
+ **       this node element when going out of scope.
  ** 
  ** @see LinkedElements_test
  ** @see llist.h
@@ -53,171 +63,356 @@
 #include "lib/error.hpp"
 #include "lib/iter-adapter.hpp"
 
-//#include 
-//#include 
-//#include 
-//#include 
+#include 
+#include 
+
 
 
 namespace lib {
   
   namespace error = lumiera::error;
-//  using error::LUMIERA_ERROR_CAPACITY;
-//  using error::LUMIERA_ERROR_INDEX_BOUNDS;
+  using error::LUMIERA_ERROR_INDEX_BOUNDS;
+  
+  
+  namespace linked_elements { ///< allocation policies for the LinkedElements list container
+    
+    /**
+     * Policy for LinkedElements: taking ownership
+     * and possibly creating heap allocated Nodes
+     */
+    struct OwningHeapAllocated
+      {
+        typedef void* CustomAllocator;
+        
+        /** this policy discards elements
+         *  by deallocating them from heap
+         */
+        template
+        void 
+        destroy (X* elm)
+          {
+            delete elm;
+          }
+        
+        
+        /** this policy creates new elements
+         *  simply by heap allocation */
+        template
+        TY&
+        create ()
+          {
+            return *new TY();
+          }
+        
+        
+        template
+        TY&                                               //____________________________________________
+        create (A1 a1)                                   ///< create object of type TY, using 1-arg ctor
+          {
+            return *new TY(a1);
+          }
+        
+        
+        template< class TY
+                , typename A1
+                , typename A2
+                >
+        TY&                                               //____________________________________________
+        create (A1 a1, A2 a2)                            ///< create object of type TY, using 2-arg ctor
+          {
+            return *new TY(a1,a2);
+          }
+        
+        
+        template< class TY
+                , typename A1
+                , typename A2
+                , typename A3
+                >
+        TY&                                               //____________________________________________
+        create (A1 a1, A2 a2, A3 a3)                     ///< create object of type TY, using 3-arg ctor
+          {
+            return *new TY(a1,a2,a3);
+          }
+        
+        
+        template< class TY
+                , typename A1
+                , typename A2
+                , typename A3
+                , typename A4
+                >
+        TY&                                               //____________________________________________
+        create (A1 a1, A2 a2, A3 a3, A4 a4)              ///< create object of type TY, using 4-arg ctor
+          {
+            return *new TY(a1,a2,a3,a4);
+          }
+        
+        
+        template< class TY
+                , typename A1
+                , typename A2
+                , typename A3
+                , typename A4
+                , typename A5
+                >
+        TY&                                               //____________________________________________
+        create (A1 a1, A2 a2, A3 a3, A4 a4, A5 a5)       ///< create object of type TY, using 5-arg ctor
+          {
+            return *new TY(a1,a2,a3,a4,a5);
+          }
+      };
+    
+    
+    
+    
+    
+    
+    /**
+     * Policy for LinkedElements: never create or destroy
+     * any elements, only allow to add already existing nodes.
+     * @note any added node needs to provide a \c next pointer
+     *       field, which is used ("intrusively") for managing
+     *       the list datastructure. But besides that, the
+     *       node element won't be altered or discarded
+     *       in any way.
+     */
+    struct NoOwnership
+      {
+        typedef void* CustomAllocator;
+        
+        /** this policy doesn't take ownership
+         *  and thus never discards anything
+         */
+        void 
+        destroy (void*)
+          {
+            /* does nothing */
+          }
+      };
+    
+    
+    
+    
+    
+#ifdef LIB_ALLOCATION_CLUSTER_H
+    
+    /**
+     * Policy for LinkedElements: especially using a
+     * lib::AllocationCluster instance for creating
+     * new node elements. It is mandatory to pass
+     * this cluster instance to the ctor of any
+     * LinkedElements list using this policy
+     * @note AllocationCluster always de-allocates
+     *       any created elements in one sway.
+     */
+    struct UseAllocationCluster
+      {
+        typedef AllocationCluster& CustomAllocator;
+        
+        CustomAllocator cluster_;
+        
+        
+        explicit
+        UseAllocationCluster (CustomAllocator clu)
+          : cluster_(clu)
+          { }
+        
+        
+        /** while this policy indeed implies creating and owing
+         *  new objects, the AllocationCluster doesn't support
+         *  discarding individual instances. All created objects
+         *  within the cluster will be bulk de-allocated
+         *  when the cluster as such goes out of scope.
+         */
+        void 
+        destroy (void*)
+          {
+            /* does nothing */
+          }
+        
+        
+        template
+        TY&                                               //__________________________________________
+        create ()                                        ///< place object of type TY, by default ctor
+          {
+            return cluster_.create();
+          }
+        
+        
+        template
+        TY&                                               //___________________________________________
+        create (A1 a1)                                   ///< place object of type TY, using 1-arg ctor
+          {
+            return cluster_.create (a1);
+          }
+        
+        
+        template< class TY
+                , typename A1
+                , typename A2
+                >
+        TY&                                               //___________________________________________
+        create (A1 a1, A2 a2)                            ///< place object of type TY, using 2-arg ctor
+          {
+            return cluster_.create (a1,a2);
+          }
+        
+        
+        template< class TY
+                , typename A1
+                , typename A2
+                , typename A3
+                >
+        TY&                                               //___________________________________________
+        create (A1 a1, A2 a2, A3 a3)                     ///< place object of type TY, using 3-arg ctor
+          {
+            return cluster_.create (a1,a2,a3);
+          }
+        
+        
+        template< class TY
+                , typename A1
+                , typename A2
+                , typename A3
+                , typename A4
+                >
+        TY&                                               //___________________________________________
+        create (A1 a1, A2 a2, A3 a3, A4 a4)              ///< place object of type TY, using 4-arg ctor
+          {
+            return cluster_.create (a1,a2,a3,a4);
+          }
+      };
+    
+#endif //(End)if lib/allocation-cluster.hpp was included
+    
+  }//(END)namespace linked_elements
+  
+  
+  
+  
+  
+  
+  
   
   
   
   /**
-   * TODO write type comment
+   * Intrusive single linked list, possibly taking ownership of node elements.
+   * Additional elements can be pushed (prepended) to the list; element access
+   * is per index number (slow) or through an Lumiera Forward Iterator traversing
+   * the linked list. There is no support for changing the list contents aside
+   * of discarding any element in the list.
+   * 
+   * The allocation and ownership related behaviour is controlled by a policy class
+   * provided as template parameter. When this policy supports creation of new
+   * elements, these might be created and prepended in one step.
    */
   template
-    < class N                    ///< node class or Base/Interface class for nodes
+    < class N                 ///< node class or Base/Interface class for nodes
+    , class ALO = linked_elements::OwningHeapAllocated
     >
   class LinkedElements
     : boost::noncopyable
+    , ALO
     {
+      N* head_;
+      
       
     public:
       
-      
-      
-      
      ~LinkedElements ()
         { 
           clear();
         }
       
-      explicit
-      LinkedElements (size_t maxElements)
-        : level_(0)
-        , capacity_(maxElements)
-        , elements_(new ElementHolder[maxElements])
+      LinkedElements ()
+        : head_(0)
         { }
       
-      /** creating a ScopedCollection in RAII-style:
-       *  The embedded elements will be created immediately.
-       *  Ctor fails in case of any error during element creation.
-       * @param builder functor to be invoked for each "slot".
-       *        It gets an ElementHolder& as parameter, and should
-       *        use this to create an object of some I-subclass
+      /** @param allo custom allocator or memory manager
+       *         to be used by the policy for creating and
+       *         discarding of node elements
        */
-      template
-      LinkedElements (size_t maxElements, CTOR builder)
-        : level_(0)
-        , capacity_(maxElements)
-        , elements_(new ElementHolder[maxElements])
-        {
-          populate_by (builder);
-        }
+      explicit
+      LinkedElements (typename ALO::CustomAllocator allo)
+        : ALO(allo)
+        , head_(0)
+        { }
       
-      /** variation of RAII-style: using a builder function,
-       *  which is a member of some object. This supports the
-       *  typical usage situation, where a manager object builds
-       *  a ScopedCollection of some components
-       * @param builder member function used to create the elements
-       * @param instance the owning class instance, on which the 
-       *        builder member function will be invoked ("this").
+      /** creating a LinkedElements list in RAII-style:
+       * @param elements iterator to provide all the elements
+       *        to be pushed into this newly created collection
+       * @note any exception while populating the container
+       *        causes already created elements to be destroyed
        */
-      template
-      LinkedElements (size_t maxElements, void (TY::*builder) (ElementHolder&), TY * const instance)
-        : level_(0)
-        , capacity_(maxElements)
-        , elements_(new ElementHolder[maxElements])
+      template
+      LinkedElements (IT elements)
+        : head_(0)
         {
-          populate_by (builder,instance);
-        }
+        try {
+            pushAll(elements);
+          }
+        catch(...)
+          {
+            WARN (progress, "Failure while populating LinkedElements list. "
+                            "All elements will be discarded");
+            clear();
+            throw;
+        } }
       
-      /* == some pre-defined Builders == */
       
-      template
-      class PullFrom;          ///< fills by copy-constructing values pulled from the iterator IT
-      
-      template
-      static PullFrom
-      pull (IT iter)           ///< convenience shortcut to pull from any given Lumiera Forward Iterator
-        {
-          return PullFrom (iter);
-        }
       
+      /** @note EX_FREE */
       void
       clear()
         {
-          REQUIRE (level_ <= capacity_, "Storage corrupted");
-          
-          while (level_)
+          while (head_)
             {
-              --level_;
+              N* elm = head_;
+              head_ = head_->next;
               try {
-                  elements_[level_].destroy();
+                  ALO::destroy(elm);
                 }
-              ERROR_LOG_AND_IGNORE (progress, "Clean-up of element in ScopedCollection")
+              ERROR_LOG_AND_IGNORE (progress, "Clean-up of element in LinkedElements list")
             }
         }
       
       
-      /** init all elements at once,
-       *  invoking a builder functor for each.
-       * @param builder to create the individual elements
-       *        this functor is responsible to invoke the appropriate
-       *        ElementHolder#create function, which places a new element
-       *        into the storage frame passed as parameter.
+      /** convenience shortcut to add all the elements
+       *  yielded by the given Lumiera Forward Iterator
+       *  @note EX_SANE
        */
-      template
+      template
       void
-      populate_by (CTOR builder)
-        try { 
-          while (level_ < capacity_)
-            {
-              ElementHolder& storageFrame (elements_[level_]);
-              builder (storageFrame);
-              ++level_;
-          } }
-        catch(...)
-          {
-            WARN (progress, "Failure while populating ScopedCollection. "
-                            "All elements will be discarded");
-            clear();
-            throw;
-          }
+      pushAll (IT elements)
+        {
+          for ( ; elements; ++elements )
+            push (*elements);
+        }
       
-      /** variation of element initialisation,
-       *  invoking a member function of some manager object
-       *  for each collection element to be created.
+      
+      
+      
+      /** accept the given element and prepend it to the list of elements;
+       *  depending on the allocation policy, this might imply taking ownership
+       * @note EX_STRONG
        */
-      template
-      void
-      populate_by (void (TY::*builder) (ElementHolder&), TY * const instance)
-        try { 
-          while (level_ < capacity_)
-            {
-              ElementHolder& storageFrame (elements_[level_]);
-              (instance->*builder) (storageFrame);
-              ++level_;
-          } }
-        catch(...)
-          {
-            WARN (progress, "Failure while populating ScopedCollection. "
-                            "All elements will be discarded");
-            clear();
-            throw;
-          }
-      
-      
-      
-      /** push a new element of default type
-       *  to the end of this container
-       * @note EX_STRONG */
-      I& appendNewElement() { return appendNew(); }
+      template
+      TY&
+      push (TY& elm)
+        {
+          elm.next = head_;
+          head_ = &elm;
+          return elm;
+        }
       
       
       template< class TY >
       TY&                                                  //_________________________________________
-      appendNew ()                                        ///< add object of type TY, using 0-arg ctor
+      pushNew ()                                          ///< add object of type TY, using 0-arg ctor
         {
-          __ensureSufficientCapacity();
-          TY& newElm = elements_[level_].template create();
-          ++level_;
-          return newElm;
+          return push (ALO::template create());
         }
       
       
@@ -225,12 +420,9 @@ namespace lib {
               , typename A1
               >
       TY&                                                  //_________________________________________
-      appendNew (A1 a1)                                   ///< add object of type TY, using 1-arg ctor
+      pushNew (A1 a1)                                     ///< add object of type TY, using 1-arg ctor
         {
-          __ensureSufficientCapacity();
-          TY& newElm = elements_[level_].template create(a1);
-          ++level_;
-          return newElm;
+          return push (ALO::template create(a1));
         }
       
       
@@ -239,12 +431,9 @@ namespace lib {
               , typename A2
               >
       TY&                                                  //_________________________________________
-      appendNew (A1 a1, A2 a2)                            ///< add object of type TY, using 2-arg ctor
+      pushNew (A1 a1, A2 a2)                              ///< add object of type TY, using 2-arg ctor
         {
-          __ensureSufficientCapacity();
-          TY& newElm = elements_[level_].template create(a1,a2);
-          ++level_;
-          return newElm;
+          return push (ALO::template create(a1,a2));
         }
       
       
@@ -254,12 +443,9 @@ namespace lib {
               , typename A3
               >
       TY&                                                  //_________________________________________
-      appendNew (A1 a1, A2 a2, A3 a3)                     ///< add object of type TY, using 3-arg ctor
+      pushNew (A1 a1, A2 a2, A3 a3)                       ///< add object of type TY, using 3-arg ctor
         {
-          __ensureSufficientCapacity();
-          TY& newElm = elements_[level_].template create(a1,a2,a3);
-          ++level_;
-          return newElm;
+          return push (ALO::template create(a1,a2,a3));
         }
       
       
@@ -270,12 +456,9 @@ namespace lib {
               , typename A4
               >
       TY&                                                  //_________________________________________
-      appendNew (A1 a1, A2 a2, A3 a3, A4 a4)              ///< add object of type TY, using 4-arg ctor
+      pushNew (A1 a1, A2 a2, A3 a3, A4 a4)                ///< add object of type TY, using 4-arg ctor
         {
-          __ensureSufficientCapacity();
-          TY& newElm = elements_[level_].template create(a1,a2,a3,a4);
-          ++level_;
-          return newElm;
+          return push (ALO::template create(a1,a2,a3,a4));
         }
       
       
@@ -287,98 +470,99 @@ namespace lib {
               , typename A5
               >
       TY&                                                  //_________________________________________
-      appendNew (A1 a1, A2 a2, A3 a3, A4 a4, A5 a5)       ///< add object of type TY, using 5-arg ctor
+      pushNew (A1 a1, A2 a2, A3 a3, A4 a4, A5 a5)         ///< add object of type TY, using 5-arg ctor
         {
-          __ensureSufficientCapacity();
-          TY& newElm = elements_[level_].template create(a1,a2,a3,a4,a5);
-          ++level_;
-          return newElm;
+          return push (ALO::template create(a1,a2,a3,a4,a5));
         }
       
       
       
+      
+      
       /* === Element access and iteration === */
       
-      I&
+      N&
       operator[] (size_t index)  const
         {
-          if (index < level_)
-            return elements_[index].accessObj();
+          N* p = head_;
+          while (p && index)
+            {
+              p = p->next;
+              --index;
+            }
           
-          throw error::Logic ("Attempt to access not (yet) existing object in ScopedCollection"
-                             , LUMIERA_ERROR_INDEX_BOUNDS);
+          if (!p || index)
+            throw error::Logic ("Attempt to access element beyond the end of LinkedElements list"
+                               , LUMIERA_ERROR_INDEX_BOUNDS);
+          else
+            return *p;
+        }
+      
+      
+      /** @warning traverses to count the elements */
+      size_t
+      size ()  const
+        {
+          uint count=0;
+          N* p = head_;
+          while (p)
+            {
+              p = p->next;
+              ++count;
+            }
+          return count;
+        }
+      
+      
+      bool
+      empty () const
+        {
+          return !head_;
         }
       
       
       
-      typedef IterAdapter<      I *, const ScopedCollection *> iterator;
-      typedef IterAdapter const_iterator;
+      typedef IterAdapter<      N*, const LinkedElements*> iterator;
+      typedef IterAdapter const_iterator;
       
       
-      iterator       begin()       { return iterator       (this, _access_begin()); }
-      const_iterator begin() const { return const_iterator (this, _access_begin()); }
+      iterator       begin()       { return iterator       (this, head_); }
+      const_iterator begin() const { return const_iterator (this, head_); }
       iterator       end ()        { return iterator();       }
       const_iterator end ()  const { return const_iterator(); }
       
       
-      size_t  size ()        const { return level_;      }
-      size_t  capacity ()    const { return capacity_;   }
-      bool    empty ()       const { return 0 == level_; }
       
-      
-      
-    private:
-      
-      
-      /* ==== internal callback API for the iterator ==== */
+    private: /* ==== internal callback API for the iterator ==== */
       
       /** Iteration-logic: switch to next position
-       * @note assuming here that the start address of the embedded object
-       *       coincides with the start of an array element (ElementHolder)
+       * @warning assuming the given node pointer belongs to this collection.
+       *          actually this is never checked; also the given node might
+       *          have been deallocated in the meantime.
        */
       friend void
-      iterNext (const ScopedCollection*, I* & pos)
+      iterNext (const LinkedElements*, N* & pos)
       {
-        ElementHolder* & storageLocation = reinterpret_cast (pos);
-        ++storageLocation;
+        pos = pos->next;
       }
       
       friend void
-      iterNext (const ScopedCollection*, const I* & pos)
+      iterNext (const LinkedElements*, const N* & pos)
       {
-        const ElementHolder* & storageLocation = reinterpret_cast (pos);
-        ++storageLocation;
+        pos = pos->next;
       }
       
       /** Iteration-logic: detect iteration end. */
       template
       friend bool
-      hasNext (const ScopedCollection* src, POS & pos)
+      hasNext (const LinkedElements*, POS & pos)
       {
-        REQUIRE (src);
-        if ((pos) && (pos < src->_access_end()))
-          return true;
-        else
-          {
-            pos = 0;
-            return false;
-      }   }
-      
-      
-      I* _access_begin() const { return & elements_[0].accessObj(); }
-      I* _access_end()   const { return & elements_[level_].accessObj(); }
-      
+        return bool(pos);
+      }
     };
   
   
   
   
-  /* === Supplement: pre-defined  === */
-  
-  /** \par usage
-   */
-  
-  
-  
 } // namespace lib
 #endif
diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index 1aefe3c15..f824b4488 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -62,8 +62,8 @@ namespace engine {
       virtual void invokeJobOperation (JobParameter parameter)  =0;
       virtual void signalFailure      (JobParameter parameter)  =0;
       
-      JobKind getJobKind()  const                               =0;
-      bool verify (Time nominalJobTime)  const                  =0;
+      virtual JobKind getJobKind()  const                       =0;
+      virtual bool verify (Time nominalJobTime)  const          =0;
     };
   
   
diff --git a/tests/40components.tests b/tests/40components.tests
index d55ef1507..ca790c658 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -532,7 +532,7 @@ out: can_IterForEach	 : Yes
 END
 
 
-PLANNED "Linked iterable elements" LinkedElements_test < >());
+          CHECK (2 == clu.count >());
+          CHECK (1 == clu.count >());
         }
       
       
diff --git a/tests/lib/linked-elements-test.cpp b/tests/lib/linked-elements-test.cpp
index 85bfc5058..d97e347b5 100644
--- a/tests/lib/linked-elements-test.cpp
+++ b/tests/lib/linked-elements-test.cpp
@@ -26,10 +26,13 @@
 #include "lib/test/test-helper.hpp"
 #include "lib/util.hpp"
 
+#include "lib/allocation-cluster.hpp"
 #include "lib/linked-elements.hpp"
 #include "lib/test/testdummy.hpp"
+#include "lib/iter-source.hpp"
+
+#include 
 
-//#include 
 
 
 namespace lib {
@@ -37,18 +40,29 @@ namespace test{
   
   namespace error = lumiera::error;
   
+  using util::isnil;
+  using util::isSameObject;
+  using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
+  
   
   namespace { // test data...
     
+    LUMIERA_ERROR_DEFINE(PROVOKED_FAILURE, "provoked failure");
+    
     const uint NUM_ELEMENTS = 500;
+    int exception_trigger = -1;
     
-    LUMIERA_ERROR_DEFINE(SUBVERSIVE, "undercover action");
+    inline void __triggerErrorAt(int i) { exception_trigger = i; }
+    inline void __triggerError_reset()  { exception_trigger =-1; }
     
-    class Nummy
-      : public Dummy
+    
+    /**
+     * Test-Element, supporting intrusive linked list storage.
+     * Also tracks ctor/dtor calls by virtue of the Dummy baseclass.
+     */
+    struct Nummy
+      : Dummy
       {
-        
-      public:
         Nummy* next;
         
         Nummy() 
@@ -60,10 +74,80 @@ namespace test{
         Nummy (int i)
           : Dummy(i)
           , next(0)
+          {
+            if (i == exception_trigger)
+              throw error::Fatal("simulated error", LUMIERA_ERROR_PROVOKED_FAILURE);
+          }
+      };
+    
+    
+    /**
+     * to demonstrate holding subclasses
+     */
+    template
+    struct Num
+      : Nummy
+      {
+        void* storage[I];   // note size depends on template parameter
+        
+        Num (int i=0, int j=0, int k=0)
+          : Nummy(I+i+j+k)
           { }
       };
     
     
+    
+    /**
+     * Helper to produce a pre-determined series 
+     * of objects to populate a LinkedElements list.
+     * @note just happily heap allocating new instances
+     *       and handing them out. The LinkedElements list
+     *       will take ownership of them and care for
+     *       clean de-allocation.
+     */
+    class NummyGenerator
+      : public IterSource
+      {
+        uint maxNum_;
+        
+        virtual Pos
+        firstResult()
+          {
+            return new Nummy(1);
+          }
+        
+        virtual void
+        nextResult(Pos& num)
+          {
+            uint current = num->getVal();
+            if (maxNum_ <= current)
+              num = 0;
+            else
+              num = new Nummy(current+1);
+          }
+        
+      public:
+        NummyGenerator (uint maxElms)
+          : maxNum_(maxElms)
+          { }
+      };
+    
+    /** Iterator-Frontend to generate this series of objects */
+    class Populator
+      : public NummyGenerator::iterator
+      {
+        
+      public:
+        explicit
+        Populator (uint numElms)
+          : NummyGenerator::iterator (
+              NummyGenerator::build (new NummyGenerator(numElms)))
+          { }
+      };
+    
+    
+    
+    
     inline uint
     sum (uint n)
     {
@@ -75,20 +159,22 @@ namespace test{
   
   
   
-  using util::isnil;
-  using util::isSameObject;
-//  using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
   
+  /// default case: ownership for heap allocated nodes
   typedef LinkedElements List;
+  
+  /// managing existing node elements without taking ownership
   typedef LinkedElements ListNotOwner;
   
+  /// creating nodes in-place, using a custom allocator for creation and disposal
+  typedef LinkedElements ListCustomAllocated;
+  
+  
   
   /********************************************************************
-   *  @test ScopedCollection manages a fixed set of objects, but these
-   *        child objects are noncopyable, may be polymorphic, an can
-   *        be created either all at once or chunk wise. The API is
-   *        similar to a vector and allows for element access
-   *        and iteration.
+   *  @test cover our custom single linked list template,
+   *        in combination with Lumiera Forward Iterators
+   *        and the usage of a custom allocator.
    */
   class LinkedElements_test : public Test
     {
@@ -103,6 +189,7 @@ namespace test{
           verify_ExceptionSafety();
           populate_by_iterator();
           verify_RAII_safety();
+          verify_customAllocator();
         }
       
       
@@ -136,9 +223,9 @@ namespace test{
             CHECK (0 == elements.size());
             CHECK (0 == Dummy::checksum());
             
-            elements.pushNew();
-            elements.pushNew();
-            elements.pushNew();
+            elements.pushNew();
+            elements.pushNew();
+            elements.pushNew();
             
             CHECK (3 == elements.size());
             CHECK (0 != Dummy::checksum());
@@ -200,6 +287,14 @@ namespace test{
         }
       
       
+      /** @test add some node elements to the LinkedElements list
+       *        but without taking ownership or performing any
+       *        memory management. This usage pattern is helpful
+       *        when the node elements are already managed elsewhere.
+       *  @note we're still (intrusively) using the next pointer
+       *        within the node elements. This means, that still
+       *        a given node can't be member in multiple lists.
+       */
       void
       verify_nonOwnership()
         {
@@ -249,7 +344,7 @@ namespace test{
             elements.pushNew(2);
             CHECK (1+2 == Dummy::checksum());
             
-            VERIFY_ERROR (SUBVERSIVE, elements.pushNew(3) );
+            VERIFY_ERROR (PROVOKED_FAILURE, elements.pushNew(3) );
             CHECK (1+2 == Dummy::checksum());
             CHECK (2 == elements.size());
             
@@ -290,6 +385,12 @@ namespace test{
         }
       
       
+      /** @test to support using LinkedElements within RAII-style components,
+       *        all the elements might be added in one sway, by pulling them
+       *        from a Lumiera Forward Iterator. In case this is done in the
+       *        ctor, any exception while doing so will trigger cleanup
+       *        of all elements (and then failure of the ctor alltogether)
+       */
       void
       verify_RAII_safety()
         {
@@ -297,7 +398,7 @@ namespace test{
           
           __triggerErrorAt(3);
           Populator yieldSomeElements(NUM_ELEMENTS);
-          VERIFY_ERROR (SUBVERSIVE, List(yieldSomeElements) );
+          VERIFY_ERROR (PROVOKED_FAILURE, List elements(yieldSomeElements) );
           
           CHECK (0 == Dummy::checksum());
           __triggerError_reset();
@@ -329,11 +430,10 @@ namespace test{
             CHECK (6+7+8+9 == elements[0].getVal());
             
             elements.clear();
-            CHECK (0 == allocator.size());
-            CHECK (0 == allocator.count >());
-            CHECK (0 == allocator.count >());
-            CHECK (0 == allocator.count >());
-            CHECK (0 == Dummy::checksum());
+            CHECK (3 == allocator.size());
+            CHECK (sum(9) == Dummy::checksum());
+            // note: elements won't be discarded unless
+            //       the AllocationCluster goes out of scope
           }
           CHECK (0 == Dummy::checksum());
         }
@@ -345,4 +445,3 @@ namespace test{
   
   
 }} // namespace lib::test
-

From fa15a3801636a2e4713294654b175f1234f5a120 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 5 May 2012 05:02:29 +0200
Subject: [PATCH 460/496] draft JobTicket implementation datastructure

...but need an extension on our iterator helpers
to build a suitable functional datastructure
for generating Jobs.
---
 src/proc/engine/job-ticket.hpp | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index f824b4488..7eb78a715 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -30,6 +30,7 @@
 #include "proc/engine/frame-coord.hpp"
 //#include "lib/time/timevalue.hpp"
 //#include "lib/time/timequant.hpp"
+#include "lib/linked-elements.hpp"
 #include "lib/iter-source.hpp"
 
 #include 
@@ -42,6 +43,7 @@ namespace engine {
 //using lib::time::Duration;
 //using lib::time::FSecs;
 //using lib::time::Time;
+using lib::LinkedElements;
 //  
 //class ExitNode;
   
@@ -76,7 +78,7 @@ namespace engine {
    * to a single model port). Once created, they are final for this segment,
    * stored together with the other descriptor objects (ProcNode and WiringDescriptor)
    * and finally discarded in bulk, in case that segment of the low-level model becomes
-   * obsolete and is replaced by a newly built new version of.
+   * obsolete and is replaced by a newly built new version of this model segment.
    * 
    * Job tickets are created by a classical recursive descent call on the exit node,
    * which figures out everything to be done for generating data from this node.
@@ -91,9 +93,28 @@ namespace engine {
   class JobTicket
     : boost::noncopyable
     {
+      struct Provision
+        {
+          Provision* next;
+        };
+      
+      struct Prerequisite
+        {
+          Prerequisite* next;
+        };
+      
+      struct Prerequisites
+        {
+          Prerequisites* next;
+          LinkedElements preparations_;
+        };
+      
+      
+      LinkedElements channelConfig_;
+      LinkedElements requirement_;
       
     public:
-       typedef lib::IterSource::iterator  JobsPlanning;
+      typedef lib::IterSource::iterator  JobsPlanning;
       
       JobTicket()
         {

From ab2a6b2fce50af031118957de8eb2a43dca8d6f6 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 14 May 2012 05:24:16 +0200
Subject: [PATCH 461/496] WIP brainstorming about a monadic iterator

The idea is to avoid building a data structure
for intermediary results, while still being able
to process a variably sized and arbitrary shaped
set of source data
---
 src/lib/iter-explorer.hpp        |  86 +++++++++
 tests/lib/iter-explorer-test.cpp | 311 +++++++++++++++++++++++++++++++
 2 files changed, 397 insertions(+)
 create mode 100644 src/lib/iter-explorer.hpp
 create mode 100644 tests/lib/iter-explorer-test.cpp

diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
new file mode 100644
index 000000000..aa27c363b
--- /dev/null
+++ b/src/lib/iter-explorer.hpp
@@ -0,0 +1,86 @@
+/*
+  ITER-EXPLORER.hpp  -  building blocks for iterator evaluation strategies 
+
+  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.
+
+*/
+
+/** @file iter-explorer.hpp
+ ** Helper template(s) for establishing various evaluation strategies for hierarchical data structures.
+ ** Based on the Lumiera Forward Iterators concept and using the basic IterAdaptor templates,
+ ** these components allow to implement typical evaluation strategies, like e.g. depth-first or
+ ** breadth-first exploration of a hierarchical structure. Since the access to this structure is
+ ** abstracted through the underlying iterator, what we effectively get is a functional datastructure.
+ ** The implementation is based on the IterableStateWrapper, which is one of the basic helper templates
+ ** provided by iter-adapter.hpp.
+ ** 
+ ** @see IterExplorer_test.cpp
+ ** @see iter-adapter.hpp
+ ** @see itertools.hpp
+ ** @see IterSource (completely opaque iterator)
+ **
+ */
+
+
+#ifndef LIB_ITER_EXPLORER_H
+#define LIB_ITER_EXPLORER_H
+
+
+#include "lib/error.hpp"
+//#include "lib/bool-checkable.hpp"
+#include "lib/iter-adapter.hpp"
+
+//#include 
+
+
+namespace lib {
+  
+  
+  namespace { // internal helpers
+  }
+  
+  
+
+  /**
+   * Adapter for building....
+   * TODO write type comment
+   */
+  template
+  class IterExplorer
+//    : public lib::BoolCheckable >
+    {
+//      CON source_;
+//      mutable POS pos_;
+      
+    public:
+//      typedef typename iter::TypeBinding::pointer pointer;
+//      typedef typename iter::TypeBinding::reference reference;
+//      typedef typename iter::TypeBinding::value_type value_type;
+      
+      
+    protected: /* === xxx === */
+      
+      /** */ 
+      
+    private:
+    };
+  
+  
+  
+} // namespace lib
+#endif
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
new file mode 100644
index 000000000..f47f99000
--- /dev/null
+++ b/tests/lib/iter-explorer-test.cpp
@@ -0,0 +1,311 @@
+/*
+  IterExplorer(Test)  -  verify evaluation patterns built using iterators
+
+  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.
+
+* *****************************************************/
+
+
+
+#include "lib/test/run.hpp"
+#include "lib/test/test-helper.hpp"
+#include "lib/util.hpp"
+#include "lib/util-foreach.hpp"
+
+#include "lib/iter-explorer.hpp"
+#include "lib/linked-elements.hpp"
+
+#include 
+#include 
+#include 
+
+
+
+namespace lib {
+namespace test{
+  
+  using ::Test;
+//  using boost::lexical_cast;
+//  using util::for_each;
+//  using util::isnil;
+  using util::isSameObject;
+//  using std::vector;
+//  using std::cout;
+//  using std::endl;
+  using lib::LinkedElements;
+  using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
+  
+  
+  namespace { // test dummy source iterator
+  
+    uint NUM_ELMS = 10;
+    
+    /** */
+    class NumberSequence
+      {
+      };
+    
+  } // (END) impl test dummy container
+  
+  
+  
+  
+  
+  
+  
+  /*********************************************************************
+   * @test use a simple source iterator yielding numbers
+   *       to build various functional evaluation structures,
+   *       based on the IterExplorer template.
+   * 
+   * \par Explanation
+   * Both this test and the IterExplorer template might be bewildering
+   * and cryptic, unless you know the Monad design pattern. Monads are
+   * heavily used in functional programming, actually they originate
+   * from Category Theory. Basically, Monad is a pattern where we
+   * combine several computation steps in a specific way; but instead
+   * of intermingling the individual computation steps and their
+   * combination, the goal is to separate and isolate the mechanics
+   * of combination, so we can focus on the actual computation steps:
+   * The mechanics of combination are embedded into the Monad type,
+   * which acts as a kind of container, holding elements to be processed.
+   * The actual processing steps are then fed to the monad as parameters.
+   * 
+   * Using the monad pattern is well suited when both the mechanics of
+   * combination and the individual computation steps tend to be complex.
+   * In such a situation, it is beneficial to develop and test both
+   * in isolation. The IterExplorer template applies this pattern
+   * to the task of processing a source sequence. Typically we use
+   * this in situations where we can't effort building elaborate
+   * data structures in (global) memory, but rather strive at
+   * doing everything on-the-fly. A typical example is the
+   * processing of a variably sized data set without
+   * using heap memory for intermediary results.
+   * 
+   * @see IterExplorer
+   * @see IterAdapter
+   */
+  class IterExplorer_test : public Test
+    {
+      
+      virtual void
+      run (Arg arg)
+        {
+          verifyChainedIterators ();
+          verifyComparisons ();
+        }
+      
+      
+      
+      /** @test a simple and practical helper built on top of IterExplorer.
+       * The resulting iterator \em combines and \em flattens a sequence
+       * of source iterators, resulting in a simple sequence accessible
+       * again as iterator. Here we verify the convenience / default
+       * implementation; it uses a STL container behind the scenes
+       * to keep track of all the added source iterators
+       */
+      void
+      verifyChainedIterators ()
+        {
+          typedef ChainedIters Chain;
+          
+          Chain ci = iter::chain (seq(5),seq(7),seq(9));
+          
+          CHECK (!isnil (ci));
+          pullOut (ci);
+          CHECK ( isnil (ci));
+          VERIFY_ERROR (ITER_EXHAUST, *ci );
+          VERIFY_ERROR (ITER_EXHAUST, ++ci );
+          
+          CHECK (isnil(Chain()));
+          CHECK (iter::chain (NIL_Sequence));
+          
+          // Iterator chaining "flattens" one level of indirection
+          NumberSequence s9 = seq(9); 
+          ci = iter::chain (s9);
+          
+          for ( ; s9 && ci; ++s9, ++ci )
+            CHECK (*s9 == *ci);
+          
+          CHECK (isnil(s9));
+          CHECK (isnil(ci));
+          
+          // Note: Iterator chain is created based on (shallow) copy
+          //       of the source sequences. In case these have an independent
+          //       per-instance state (like e.g. NumberSequence used for this test),
+          //       then the created chain is independent from the source iterators.
+          s9 = seq(9);
+          ci = iter::chain (s9);
+          CHECK (0 == *s9);
+          CHECK (0 == *ci);
+          
+          pullOut (ci);
+          CHECK (isnil(ci));
+          CHECK (0 == *s9);
+          pullOut (s9);
+          CHECK (isnil(s9));
+        }
+      
+      
+      /** @test variation of the iterator chaining facility.
+       * This is the "raw" version without any convenience shortcuts.
+       * The source iterators are given as iterator yielding other iterators. 
+       */
+      void
+      verifyRawChainedIterators ()
+        {
+          typedef LinkedElements IterContainer;
+          typedef IterContainer::const_iterator IterIter;
+          
+          typedef ChainedIters Chain;
+          
+          NumberSequence s5 (1,5);
+          NumberSequence s7 (5,8);
+          NumberSequence s9 (8,10);
+          
+          CHECK (1 == *s5);
+          CHECK (5 == *s7);
+          CHECK (8 == *s9);
+          
+          IterContainer srcIters;
+          srcIters.push (s9);
+          srcIters.push (s7);
+          srcIters.push (s5);
+          
+          IterIter iti(srcIters.begin());
+          CHECK (!isnil (iti));
+          CHECK (isSameObject (s5, *iti));
+          
+          Chain chain(iti);
+          CHECK (!isnil (iti));
+          CHECK (1 == *chain);
+          
+          ++chain;
+          CHECK (2 == *chain);
+          CHECK (2 == *s5);
+          CHECK (5 == *s7);
+          CHECK (8 == *s9);
+          
+          ++++chain;
+          CHECK (4 == *chain);
+          CHECK (4 == *s5);
+          CHECK (5 == *s7);
+          CHECK (8 == *s9);
+          
+          ++chain;
+          CHECK (5 == *chain);
+          CHECK (isnil(s5));
+          CHECK (5 == *s7);
+          CHECK (8 == *s9);
+          
+          ++chain;
+          CHECK (6 == *chain);
+          CHECK (isnil(s5));
+          CHECK (6 == *s7);
+          CHECK (8 == *s9);
+          
+          ++++chain;
+          CHECK (8 == *chain);
+          CHECK (isnil(s5));
+          CHECK (isnil(s7));
+          CHECK (8 == *s9);
+          
+          ++chain;
+          CHECK (9 == *chain);
+          CHECK (isnil(s5));
+          CHECK (isnil(s7));
+          CHECK (9 == *s9);
+          
+          ++chain;
+          CHECK (isnil(chain));
+          CHECK (isnil(s5));
+          CHECK (isnil(s7));
+          CHECK (isnil(s9));
+        }
+      
+      
+      
+      /** @test a depth-first visiting and exploration scheme
+       * of a tree like system, built on top of the IterExplorer monad.
+       * 
+       * \par Test data structure
+       * We built a functional datastructure here, on the fly, while exploring it.
+       * The \c exploreChildren(m) function generates this tree like datastructure:
+       * For a given node with number value i, it returns the children (2*i-1) and 2*i
+       * -- but only up to the given maximum value m.
+       * If we start such a tree structure with a root node 1, and maximum = 8, this yields:
+       * \code
+       * (       1       )
+       * (   1       2   )
+       * ( 1   2   3   4 )
+       * (1 2 3 4 5 6 7 8)
+       * \endcode
+       * This tree has no meaning in itself, beyond being an easy testbed for tree exploration schemes.
+       * 
+       * \par How the exploration works
+       * We use a pre defined Template \link DepthFirstExplorer \endlink, which is built on top of IterExplorer.
+       * It contains the depth-first exploration strategy hardwired. Actually this effect is achieved by
+       * defining a specific way how to \em combine the results of a \em exploration -- the latter being
+       * the function which generates the data structure. To yield a depth-first exploration, all we have to do
+       * is to delve down immediately into the children, right after visiting the node itself.
+       * 
+       * Now, when creating such a DepthFirstExplorer by wrapping a given source iterator, the result is again
+       * an iterator, but a specific iterator which at the same time is a Monad: It supports the \c >>= operation
+       * (also known as \em bind operator or \em flatMap operator). This operator takes as second argument a
+       * function, which in our case is the function to generate or explore the data structure.
+       * 
+       * The result of applying this \c >>= operation is a \em transformed version of the source iterator,
+       * i.e. it is again an iterator, which yields the results of the exploration function, combined together
+       * in the order as defined by the built-in exploration strategy (here: depth first)
+       */
+      void
+      verifyDepthFirstExploration ()
+        {
+          typedef DepthFirstExplorer DepthFirst;
+          DepthFirst root (seq(1));
+          
+          NumberSequence explorationResult = root >>= exploreChildren(8);
+          CHECK (materialise(explorationResult) == "1-1-1-1-2-2-3-4-2-3-5-6-4-7-8");
+        }
+      
+      
+      
+      /** @test a breadth-first visiting and exploration scheme
+       * of a tree like system, built on top of the IterExplorer monad;
+       * here, an internal queue is used to explore the hierarchy in layers.
+       * The (functional) datastructure is the same, just we're visiting it
+       * differently here (in rows or layers).
+       */
+      void
+      verifyBreadthFirstExploration ()
+        {
+          typedef BreadthFirstExplorer BreadthFirst;
+          BreadthFirst root (seq(1));
+          
+          NumberSequence explorationResult = root >>= exploreChildren(8);
+          CHECK (materialise(explorationResult) == "1-1-2-1-2-3-4-1-2-3-4-5-6-7-8");
+        }
+      
+      
+    };
+  
+  LAUNCHER (IterExplorer_test, "unit common");
+  
+  
+}} // namespace lib::test
+

From 83a0f4b41fc8b4ce7e5af591fedaa8e32fe45f9d Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 16 May 2012 01:01:20 +0200
Subject: [PATCH 462/496] Implementation (I) : IterStateWrapper as foundation
 (passing test)

---
 src/lib/iter-adapter.hpp         |  94 ++++++++++++++++++++
 tests/lib/iter-explorer-test.cpp | 143 +++++++++++++++++++++++++++++--
 2 files changed, 232 insertions(+), 5 deletions(-)

diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp
index 9cb5f6bee..ca69dec8d 100644
--- a/src/lib/iter-adapter.hpp
+++ b/src/lib/iter-adapter.hpp
@@ -256,6 +256,100 @@ namespace lib {
   
   
   
+
+  /**
+   * Another Lumiera Forward Iterator building block, based on incorporating a state type 
+   * right into the iterator. Contrast this to IterAdapter referring to an controlling
+   * container behind the scenes. Here, all of the state is assumed to live in the
+   * custom type embedded into this iterator, accessed and manipulated through
+   * an set of free function to be resolved by ADL. 
+   * 
+   * @see IterExplorer a monadic iterator built on top of IterStateWrapper
+   * @see iter-explorer-test.hpp
+   * @see iter-adaptor-test.cpp
+   */
+  template
+  class IterStateWrapper
+    : public lib::BoolCheckable >
+    {
+      ST core_;
+      
+    public:
+      typedef typename iter::TypeBinding::pointer pointer;
+      typedef typename iter::TypeBinding::reference reference;
+      typedef typename iter::TypeBinding::value_type value_type;
+      
+      IterStateWrapper (ST initialState)
+        : core_(initialState)
+        { 
+          checkPos(core_);
+        }
+      
+      IterStateWrapper ()
+        : core_()
+        { }
+      
+      
+      /* === lumiera forward iterator concept === */
+      
+      reference
+      operator*() const
+        {
+          _maybe_throw();
+          return yield (core_);   // extension point: yield
+        }
+      
+      pointer
+      operator->() const
+        {
+          _maybe_throw();
+          return & yield(core_);  // extension point: yield
+        }
+      
+      IterStateWrapper&
+      operator++()
+        {
+          _maybe_throw();
+          iterNext (core_);       // extension point: iterNext
+          return *this;
+        }
+      
+      bool
+      isValid ()  const
+        {
+          return checkPos(core_); // extension point: checkPos
+        }
+      
+      bool
+      empty ()    const
+        {
+          return !isValid();
+        }
+      
+      
+    private:
+      
+      void
+      _maybe_throw()  const
+        {
+          if (!isValid())
+            _throwIterExhausted();
+        }
+      
+      /// comparison of equivalent iterators
+      friend bool operator== (IterStateWrapper const& il, IterStateWrapper const& ir)
+      {
+        return (il.empty() && ir.empty())
+            || (il.isValid() && ir.isValid() && *il == *ir);
+      }
+      friend bool operator!= (IterStateWrapper const& il, IterStateWrapper const& ir)
+      {
+        return ! (il == ir);
+      }
+    };
+  
+  
+  
   
   
   
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index f47f99000..0ac8ac045 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -31,18 +31,34 @@
 #include "lib/linked-elements.hpp"
 
 #include 
-#include 
+//#include 
+#include 
 #include 
 
 
 
 namespace lib {
+  
+  namespace iter {
+    
+    template<>
+    struct TypeBinding
+      {
+        typedef uint value_type;
+        typedef uint const& reference;
+        typedef uint const* pointer;
+      };
+  }
+  
+
+  
 namespace test{
   
   using ::Test;
 //  using boost::lexical_cast;
 //  using util::for_each;
 //  using util::isnil;
+  using util::isnil;
   using util::isSameObject;
 //  using std::vector;
 //  using std::cout;
@@ -53,12 +69,86 @@ namespace test{
   
   namespace { // test dummy source iterator
   
-    uint NUM_ELMS = 10;
+//    uint NUM_ELMS = 10;
+    
+    class State
+      {
+        uint p,e;
+        
+      public:
+        State(uint start, uint end)
+          : p(start)
+          , e(end)
+          { }
+        
+        friend bool
+        checkPos (State const& st)
+        {
+          return st.p < st.e;
+        }
+        
+        friend uint const&
+        yield (State const& st)
+        {
+          return checkPos(st)? st.p : st.e;
+        }
+        
+        friend void
+        iterNext (State & st)
+        {
+          if (!checkPos (st)) return;
+          ++st.p;
+        }
+      };
+    
+    
     
     /** */
     class NumberSequence
+      : public IterStateWrapper
       {
+          
+        public:
+          explicit
+          NumberSequence(uint end = 0)
+            : IterStateWrapper (State(0,end))
+            { }
+          NumberSequence(uint start, uint end)
+            : IterStateWrapper (State(start,end))
+            { }
       };
+      
+    inline NumberSequence
+    seq (uint end)
+    {
+      return NumberSequence(end); 
+    }
+    
+    inline NumberSequence
+    seq (uint start, uint end)
+    {
+      return NumberSequence(start, end); 
+    }
+    
+    NumberSequence NIL_Sequence;
+    
+    
+    
+    /** Diagnostic helper: "squeeze out" the given iterator
+     * and join all yielded elements into a string
+     */
+    template
+    inline string
+    materialise (II & ii)
+    {
+      std::ostringstream buff;
+      while (ii)
+        {
+          buff << *ii;
+          if (++ii) buff << "-";
+        }
+      return buff.str();
+    }
     
   } // (END) impl test dummy container
   
@@ -104,10 +194,45 @@ namespace test{
     {
       
       virtual void
-      run (Arg arg)
+      run (Arg)
         {
-          verifyChainedIterators ();
-          verifyComparisons ();
+          verifyStateAdapter();
+          
+          UNIMPLEMENTED ("IterExplorer Monad");
+          verifyChainedIterators();
+          verifyRawChainedIterators();
+          
+          verifyDepthFirstExploration();
+          verifyBreadthFirstExploration();
+        }
+      
+      
+      
+      /** @test all of the following IterExplorer flavours are built on top
+       * of a special iterator adapter, centred at the notion of an iterable
+       * state element type. The actual iterator just embodies one element
+       * of this state representation, and typically there is not an hidden
+       * back-link to some kind of container in charge of the elements yielded
+       */
+      void
+      verifyStateAdapter ()
+        {
+          NumberSequence ii = seq(5);
+          CHECK (!isnil (ii));
+          CHECK (0 == *ii);
+          CHECK (materialise(ii) == "0-1-2-3-4");
+          CHECK ( isnil (ii));
+          CHECK (!ii);
+          
+          VERIFY_ERROR (ITER_EXHAUST, *ii );
+          VERIFY_ERROR (ITER_EXHAUST, ++ii );
+          
+          ii = seq(5,8);
+          CHECK (materialise(ii) == "5-6-7");
+          
+          ii = NIL_Sequence;
+          CHECK ( isnil (ii));
+          CHECK (!ii);
         }
       
       
@@ -122,6 +247,7 @@ namespace test{
       void
       verifyChainedIterators ()
         {
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
           typedef ChainedIters Chain;
           
           Chain ci = iter::chain (seq(5),seq(7),seq(9));
@@ -159,6 +285,7 @@ namespace test{
           CHECK (0 == *s9);
           pullOut (s9);
           CHECK (isnil(s9));
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
         }
       
       
@@ -169,6 +296,7 @@ namespace test{
       void
       verifyRawChainedIterators ()
         {
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
           typedef LinkedElements IterContainer;
           typedef IterContainer::const_iterator IterIter;
           
@@ -236,6 +364,7 @@ namespace test{
           CHECK (isnil(s5));
           CHECK (isnil(s7));
           CHECK (isnil(s9));
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
         }
       
       
@@ -276,11 +405,13 @@ namespace test{
       void
       verifyDepthFirstExploration ()
         {
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
           typedef DepthFirstExplorer DepthFirst;
           DepthFirst root (seq(1));
           
           NumberSequence explorationResult = root >>= exploreChildren(8);
           CHECK (materialise(explorationResult) == "1-1-1-1-2-2-3-4-2-3-5-6-4-7-8");
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
         }
       
       
@@ -294,11 +425,13 @@ namespace test{
       void
       verifyBreadthFirstExploration ()
         {
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
           typedef BreadthFirstExplorer BreadthFirst;
           BreadthFirst root (seq(1));
           
           NumberSequence explorationResult = root >>= exploreChildren(8);
           CHECK (materialise(explorationResult) == "1-1-2-1-2-3-4-1-2-3-4-5-6-7-8");
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
         }
       
       

From 378ebe21f0f4d9eb8a509def5891e7fd5744d480 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 19 May 2012 02:13:18 +0200
Subject: [PATCH 463/496] Fix naming of Iteration control API functions (closes
 #410)

comes in handy now, since IterStateWrapper uses a similar API
---
 src/lib/iter-adapter.hpp                    | 105 ++++++++++++--------
 src/lib/iter-source.hpp                     |   2 +-
 src/lib/scoped-collection.hpp               |   2 +-
 src/proc/mobject/session/query-resolver.hpp |   2 +-
 tests/lib/iter-adapter-test.cpp             |   2 +-
 tests/lib/iter-explorer-test.cpp            |   8 +-
 6 files changed, 70 insertions(+), 51 deletions(-)

diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp
index ca69dec8d..cc95c1859 100644
--- a/src/lib/iter-adapter.hpp
+++ b/src/lib/iter-adapter.hpp
@@ -32,6 +32,9 @@
  ** Depending on the concrete situation, several flavours are provided:
  ** - the IterAdapter retains an active callback connection to the
  **   controlling container, thus allowing arbitrary complex behaviour.
+ ** - the IterStateWrapper uses a variation of that approach, where the
+ **   representation of the current state is embedded as an state value
+ **   element right into the iterator instance.
  ** - the RangeIter allows just to expose a range of elements defined
  **   by a STL-like pair of "start" and "end" iterators
  ** - often, objects are managed internally by pointers, while allowing
@@ -47,7 +50,7 @@
  ** extended with the help of itertools.hpp
  ** 
  ** Basically every class in compliance with our specific iterator concept
- ** can be used as a building block in this framework.
+ ** can be used as a building block within this framework.
  ** 
  ** 
  ** \par Lumiera forward iterator concept
@@ -75,8 +78,6 @@
  **   \em dereferenced to yield the "current" value.
  ** - moreover, iterators may be incremented until exhaustion.
  ** 
- ** @todo naming of the iteration control function: TICKET #410
- ** 
  ** @see iter-adapter-test.cpp
  ** @see itertools.hpp
  ** @see IterSource (completely opaque iterator)
@@ -134,8 +135,8 @@ namespace lib {
    *       -# it should be convertible to the pointer type it declares
    *       -# dereferencing should yield a type that is convertible to the reference type
    * - CON points to the data source of this iterator (typically a data container type)
-   *       We store a pointer-like backlink to invoke a special iteration control API:     //////////////TICKET #410
-   *       -# \c hasNext yields true iff the source has yet more result values to yield
+   *       We store a pointer-like backlink to invoke a special iteration control API:
+   *       -# \c checkPoint yields true iff the source has yet more result values to yield
    *       -# \c iterNext advances the POS to the next element 
    * 
    * @see scoped-ptrvect.hpp usage example
@@ -158,7 +159,7 @@ namespace lib {
         : source_(src)
         , pos_(startpos)
         { 
-          checkPos();
+          check();
         }
       
       IterAdapter ()
@@ -194,7 +195,7 @@ namespace lib {
       bool
       isValid ()  const
         {
-          return checkPos();
+          return check();
         }
       
       bool
@@ -212,28 +213,25 @@ namespace lib {
        *        for example setting it to a "stop iteration" mark.
        */ 
       bool
-      checkPos()  const
-      {
-        return source_ && hasNext (source_,pos_);                    //////////////TICKET #410
-      }
+      check()  const
+        {
+          return source_ && checkPoint (source_,pos_);           // extension point: free function checkPoint(...)
+        }
       
       /** ask the controlling container to yield the next position.
        *  The call is dispatched only if the current position is valid;
-       *  any new position returned is again validated, so to detect
-       *  the iteration end as soon as possible.
+       *  any new position reached will typically be validated prior
+       *  to any further access, through invocation of #check.
        */
-      bool
-      iterate ()
-      {
-        if (!checkPos()) return false;
-        
-        iterNext (source_,pos_);
-        return checkPos();
-      }
+      void
+      iterate()
+        {
+          if (check())
+            iterNext (source_,pos_);                             // extension point: free function iterNext(...)
+        }
       
       
     private:
-      
       void
       _maybe_throw()  const
         {
@@ -262,9 +260,19 @@ namespace lib {
    * right into the iterator. Contrast this to IterAdapter referring to an controlling
    * container behind the scenes. Here, all of the state is assumed to live in the
    * custom type embedded into this iterator, accessed and manipulated through
-   * an set of free function to be resolved by ADL. 
+   * a set of free function to be resolved by ADL.
    * 
-   * @see IterExplorer a monadic iterator built on top of IterStateWrapper
+   * \par Assumptions when building iterators based on IterStateWrapper
+   * There is a custom state representation type ST.
+   * - default constructible
+   * - this default state represents the \em bottom (invalid) state.
+   * - copyable, because iterators are passed by value
+   * - this type needs to provide an iteration control API through free functions
+   *   -# \c checkPoint establishes, if the given state element represents a valid state
+   *   -# \c iterNext evolves this state by one step (sideeffect)
+   *   -# \c yield realises the given state, yielding an element of result type T
+   * 
+   * @see IterExplorer an iterator monad built on top of IterStateWrapper
    * @see iter-explorer-test.hpp
    * @see iter-adaptor-test.cpp
    */
@@ -275,14 +283,14 @@ namespace lib {
       ST core_;
       
     public:
-      typedef typename iter::TypeBinding::pointer pointer;
-      typedef typename iter::TypeBinding::reference reference;
-      typedef typename iter::TypeBinding::value_type value_type;
+      typedef T* pointer;
+      typedef T& reference;
+      typedef T  value_type;
       
       IterStateWrapper (ST initialState)
         : core_(initialState)
         { 
-          checkPos(core_);
+          checkPoint (core_);
         }
       
       IterStateWrapper ()
@@ -296,28 +304,28 @@ namespace lib {
       operator*() const
         {
           _maybe_throw();
-          return yield (core_);   // extension point: yield
+          return yield (core_);     // extension point: yield
         }
       
       pointer
       operator->() const
         {
           _maybe_throw();
-          return & yield(core_);  // extension point: yield
+          return & yield(core_);    // extension point: yield
         }
       
       IterStateWrapper&
       operator++()
         {
           _maybe_throw();
-          iterNext (core_);       // extension point: iterNext
+          iterNext (core_);         // extension point: iterNext
           return *this;
         }
       
       bool
       isValid ()  const
         {
-          return checkPos(core_); // extension point: checkPos
+          return checkPoint(core_); // extension point: checkPoint
         }
       
       bool
@@ -326,7 +334,6 @@ namespace lib {
           return !isValid();
         }
       
-      
     private:
       
       void
@@ -336,20 +343,32 @@ namespace lib {
             _throwIterExhausted();
         }
       
-      /// comparison of equivalent iterators
-      friend bool operator== (IterStateWrapper const& il, IterStateWrapper const& ir)
-      {
-        return (il.empty() && ir.empty())
-            || (il.isValid() && ir.isValid() && *il == *ir);
-      }
-      friend bool operator!= (IterStateWrapper const& il, IterStateWrapper const& ir)
-      {
-        return ! (il == ir);
-      }
+      
+      /// comparison is allowed to access state implementation core
+      template
+      friend bool operator== (IterStateWrapper const&, IterStateWrapper const&);
     };
   
   
   
+  /// Supporting equality comparisons of equivalent iterators (same state type)...
+  template
+  bool operator== (IterStateWrapper const& il, IterStateWrapper const& ir)
+  {
+    return (il.empty() && ir.empty())
+        || (il.isValid() && ir.isValid() && il.core_ == ir.core_);
+  }
+  
+  template
+  bool operator!= (IterStateWrapper const& il, IterStateWrapper const& ir)
+  { 
+    return ! (il == ir);
+  }
+  
+  
+  
+  
+  
   
   
   
diff --git a/src/lib/iter-source.hpp b/src/lib/iter-source.hpp
index 41b56e2c5..c2f090710 100644
--- a/src/lib/iter-source.hpp
+++ b/src/lib/iter-source.hpp
@@ -112,7 +112,7 @@ namespace lib {
       /* == Iteration control API for IterAdapter frontend == */
       
       friend bool
-      hasNext (DataHandle const&, Pos const& pos)          ////TICKET #410
+      checkPoint (DataHandle const&, Pos const& pos)
         {
           return bool(pos);
         }
diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp
index e1cba4ec2..58f2f0433 100644
--- a/src/lib/scoped-collection.hpp
+++ b/src/lib/scoped-collection.hpp
@@ -526,7 +526,7 @@ namespace lib {
       /** Iteration-logic: detect iteration end. */
       template
       friend bool
-      hasNext (const ScopedCollection* src, POS & pos)
+      checkPoint (const ScopedCollection* src, POS & pos)
       {
         REQUIRE (src);
         if ((pos) && (pos < src->_access_end()))
diff --git a/src/proc/mobject/session/query-resolver.hpp b/src/proc/mobject/session/query-resolver.hpp
index 7fe5631dc..c09ee9d9d 100644
--- a/src/proc/mobject/session/query-resolver.hpp
+++ b/src/proc/mobject/session/query-resolver.hpp
@@ -239,7 +239,7 @@ namespace session {
       
       
       friend bool
-      hasNext  (PReso const&, Result const& pos)          ////TICKET #410
+      checkPoint (PReso const&, Result const& pos)
         {
           return bool(pos);
         }
diff --git a/tests/lib/iter-adapter-test.cpp b/tests/lib/iter-adapter-test.cpp
index ef6f2c9da..0361319f1 100644
--- a/tests/lib/iter-adapter-test.cpp
+++ b/tests/lib/iter-adapter-test.cpp
@@ -147,7 +147,7 @@ namespace test{
          */
         template
         friend bool
-        hasNext (const TestContainer* src, ITER& pos)
+        checkPoint (const TestContainer* src, ITER& pos)
           {
             REQUIRE (src);
             if ((pos != ITER(0)) && (pos != src->numberz_.end()))
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index 0ac8ac045..e29768c62 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -82,21 +82,21 @@ namespace test{
           { }
         
         friend bool
-        checkPos (State const& st)
+        checkPoint (State const& st)
         {
           return st.p < st.e;
         }
         
-        friend uint const&
+        friend uint&
         yield (State const& st)
         {
-          return checkPos(st)? st.p : st.e;
+          return util::unConst(checkPoint(st)? st.p : st.e);
         }
         
         friend void
         iterNext (State & st)
         {
-          if (!checkPos (st)) return;
+          if (!checkPoint(st)) return;
           ++st.p;
         }
       };

From 45f4c96c6f4d4112a4a35e0d3f461328e0113a44 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 19 May 2012 02:16:07 +0200
Subject: [PATCH 464/496] change LinkedElements to us a more space efficient
 iterator

Actually we don't need any backreference to the
container for iterating a singly linked list
---
 src/lib/linked-elements.hpp        | 45 +++++++++++++++++++++---------
 tests/lib/linked-elements-test.cpp |  2 ++
 2 files changed, 34 insertions(+), 13 deletions(-)

diff --git a/src/lib/linked-elements.hpp b/src/lib/linked-elements.hpp
index 68c1d375f..9bb76b4df 100644
--- a/src/lib/linked-elements.hpp
+++ b/src/lib/linked-elements.hpp
@@ -520,14 +520,27 @@ namespace lib {
           return !head_;
         }
       
+      struct IterationState
+        {
+          N* node;
+          
+          IterationState(N* p=0)
+            : node(p)
+            { }
+          
+          friend bool
+          operator== (IterationState const& il, IterationState const& ir)
+          {
+            return il.node == ir.node;
+          }
+        };
+      
+      typedef IterStateWrapper iterator;
+      typedef IterStateWrapper const_iterator;
       
       
-      typedef IterAdapter<      N*, const LinkedElements*> iterator;
-      typedef IterAdapter const_iterator;
-      
-      
-      iterator       begin()       { return iterator       (this, head_); }
-      const_iterator begin() const { return const_iterator (this, head_); }
+      iterator       begin()       { return iterator       (head_); }
+      const_iterator begin() const { return const_iterator (head_); }
       iterator       end ()        { return iterator();       }
       const_iterator end ()  const { return const_iterator(); }
       
@@ -541,23 +554,29 @@ namespace lib {
        *          have been deallocated in the meantime.
        */
       friend void
-      iterNext (const LinkedElements*, N* & pos)
+      iterNext (IterationState & pos)
       {
-        pos = pos->next;
+        pos.node = pos.node->next;
       }
       
       friend void
-      iterNext (const LinkedElements*, const N* & pos)
+      iterNext (IterationState const& pos)
       {
-        pos = pos->next;
+        pos.node = pos.node->next;
       }
       
       /** Iteration-logic: detect iteration end. */
-      template
       friend bool
-      hasNext (const LinkedElements*, POS & pos)
+      checkPoint (IterationState const& pos)
       {
-        return bool(pos);
+        return bool(pos.node);
+      }
+      
+      friend N&
+      yield (IterationState const& pos)
+      {
+        REQUIRE (pos.node);
+        return * unConst(pos).node;
       }
     };
   
diff --git a/tests/lib/linked-elements-test.cpp b/tests/lib/linked-elements-test.cpp
index d97e347b5..63655aa62 100644
--- a/tests/lib/linked-elements-test.cpp
+++ b/tests/lib/linked-elements-test.cpp
@@ -278,7 +278,9 @@ namespace test{
             VERIFY_ERROR (ITER_EXHAUST, ++elements.end() );
             
             CHECK (ii == elements.end());
+            CHECK (ii == List::iterator());
             CHECK (cii == elements.end());
+            CHECK (cii == List::const_iterator());
             VERIFY_ERROR (ITER_EXHAUST, ++ii );
             VERIFY_ERROR (ITER_EXHAUST, ++cii );
             

From eedcd6994161f4084497a2da2b45b663ae281a47 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 23 May 2012 04:06:24 +0200
Subject: [PATCH 465/496] draft the IterExplorer design

the tricky part seems to be how to combine the
source iterators into a new monad instance, while
keeping this "Combinator" Strategy configurable

...just passes the compiler, while still lacking
even the generic implementation of joining
together the source iterators
---
 src/lib/iter-explorer.hpp        | 274 +++++++++++++++++++++++++++++--
 src/lib/linked-elements.hpp      |  10 +-
 tests/lib/iter-explorer-test.cpp |  29 ++--
 3 files changed, 281 insertions(+), 32 deletions(-)

diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index aa27c363b..cc03ea2bd 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -26,7 +26,7 @@
  ** these components allow to implement typical evaluation strategies, like e.g. depth-first or
  ** breadth-first exploration of a hierarchical structure. Since the access to this structure is
  ** abstracted through the underlying iterator, what we effectively get is a functional datastructure.
- ** The implementation is based on the IterableStateWrapper, which is one of the basic helper templates
+ ** The implementation is based on the IterStateWrapper, which is one of the basic helper templates
  ** provided by iter-adapter.hpp.
  ** 
  ** @see IterExplorer_test.cpp
@@ -43,42 +43,290 @@
 
 #include "lib/error.hpp"
 //#include "lib/bool-checkable.hpp"
+#include "lib/meta/function.hpp"
 #include "lib/iter-adapter.hpp"
 
 //#include 
 
 
 namespace lib {
+
   
   
   namespace { // internal helpers
+    
+    using std::tr1::function;
+    using meta::FunctionSignature;
+    
+    /**
+     * Helper to dissect an arbitrary function signature,
+     * irrespective if the parameter is given as function reference,
+     * function pointer, member function pointer or functor object.
+     * The base case assumes a (language) function reference.
+     */
+    template
+    struct _Fun
+      {
+        typedef typename FunctionSignature >::Ret Ret;
+        typedef typename FunctionSignature >::Args Args;
+      };
+    /** Specialisation for using a function pointer */
+    template
+    struct _Fun
+      {
+        typedef typename FunctionSignature >::Ret Ret;
+        typedef typename FunctionSignature >::Args Args;
+      };
+    /** Specialisation for passing a functor */
+    template
+    struct _Fun >
+      {
+        typedef typename FunctionSignature >::Ret Ret;
+        typedef typename FunctionSignature >::Args Args;
+      };
+    
+    
+    
+    
   }
-  
+
+  namespace iter_explorer {
+    
+    template
+    class DefaultCombinator;
+  }
+
   
 
   /**
-   * Adapter for building....
-   * TODO write type comment
+   * Adapter for using an Iterator in the way of a Monad
+   * This allows to "bind" or "flatMap" a functor, thereby creating
+   * a derived version of the iterator, yielding the (flattened) combination
+   * of all the results generated by this bound functor. The rationale is
+   * to apply some exploration or expansion pattern on the elements of the
+   * source sequence (source iterator) -- while completely separating out
+   * the \em mechanics how to treat and combine individual elements.
+   * 
+   * \par implementation approach
+   * IterExplorer is a thin wrapper, based on IterStateWrapper; thus the assumption
+   * is for the actual elements to be generated by a "state core", which is embedded
+   * right into each instance. To provide the monad bind operator \c >>= , we need
+   * the help of a strategy template, the so called \em Combinator. This strategy
+   * contains the details how to combine the results of various iterators and
+   * to join them together into a single new IterExplorer instance. Thus, obviously
+   * this Combinator strategy template relies on additional knowledge about the
+   * source iterator's implementation. Such is unavoidable, since C++ isn't a
+   * functional programming language.
+   * 
+   * When invoking the bind (flat map) operator, a suitably represented \em functor
+   * is embedded into an instance of the Combinator template. The resulting object
+   * becomes the state core of the new IterExplorer object returned as result.
+   * So the result \em is an iterator, but -- when "pulled" -- it will in turn
+   * pull from the source iterator and feed the provided elements through the
+   * \em exploration functor, which this way has been \em bound into the resulting
+   * monad. The purpose of the Combinator strategy is to join together various
+   * resulting iterators, thereby "flattening" one level of "monad packaging"
+   * (hence the name "flat map operator").
+   * 
+   * @remarks figuring out what happens here just from the code might lead to confusion.
+   *          You really need to grasp the monad concept first, which is a design pattern
+   *          commonly used in functional programming. For the practical purpose in question
+   *          here, you might consider it a "programmable semicolon": it links together a
+   *          chain of operations detailed elsewhere, and provided in a dynamic fashion
+   *          (as functor at runtime). More specifically, these operations will expand
+   *          and explore a dynamic source data set on the fly. Notably this allows
+   *          us to handle such an "exploration" on-the-fly, without the need to
+   *          allocate heap memory to store intermediary results, but also without
+   *          using the stack and recursive programming.
    */
-  template
+  template class _COM_ = iter_explorer::DefaultCombinator
+          >
   class IterExplorer
-//    : public lib::BoolCheckable >
+    : public IterStateWrapper
     {
-//      CON source_;
-//      mutable POS pos_;
+      
+      typedef typename SRC::value_type value_type;
+      typedef typename SRC::reference  reference;
+      
+
       
     public:
-//      typedef typename iter::TypeBinding::pointer pointer;
-//      typedef typename iter::TypeBinding::reference reference;
-//      typedef typename iter::TypeBinding::value_type value_type;
+      /** wrap an iterator like state representation
+       *  to build it into a monad. The resulting entity
+       *  is both an iterator yielding the elements generated
+       *  by the core, and it provides the (monad) bind operator.
+       */
+      explicit
+      IterExplorer (SRC const& iterStateCore)
+        : IterStateWrapper (iterStateCore)
+        { }
       
       
-    protected: /* === xxx === */
+      /** monad bind ("flat map") operator */
+      template
+      IterExplorer<_COM_, _COM_>
+      operator >>= (FUN explorer)
+        {
+          typedef _COM_ Combinator;
+        
+          return IterExplorer(
+                   Combinator(explorer)
+                             .startWith( explorer(accessFirstElement()))
+                             .followUp (accessRemainingElements()));
+        }
       
-      /** */ 
       
     private:
+      
+      reference
+      accessFirstElement()
+        {
+          return this->operator* ();
+        }
+      
+      IterExplorer&
+      accessRemainingElements()
+        {
+          this->operator++ ();
+          return *this;
+        }
     };
+      
+      
+    
+    
+  namespace iter_explorer { ///< predefined policies and configurations
+      
+      
+    /**
+     * a generic "Combinator strategy" for IterExplorer.
+     * This fallback solution just assumes that the source is a
+     * Lumiera Forward Iterator -- consequently we need to do heap
+     * allocations behind the scenes, as the size and structure of
+     * the expansion results is determined at runtime.
+     * @note the whole purpose of IterExplorer is to be more efficient
+     *       then this fallback, thus requiring more specific Combinator
+     *       strategies, able to exploit specific knowledge of the
+     *       source iterator's implementation.
+     */
+    template
+    class DefaultCombinator
+      {
+        typedef typename _Fun::Ret  ResultIter;
+        
+      public:
+        typedef typename ResultIter::value_type value_type;
+        typedef typename ResultIter::reference  reference;
+        typedef typename ResultIter::pointer    pointer;
+  
+        
+        
+        DefaultCombinator(FUN explorer)
+          {
+            UNIMPLEMENTED ("representation of the sequence of partial results");
+          }
+        
+        DefaultCombinator &
+        startWith (ResultIter firstExplorationResult)
+          {
+            UNIMPLEMENTED ("DefaultCombinator: yield first result");
+            return *this;
+          }
+        
+        template
+        DefaultCombinator&
+        followUp (SRC followUpSourceElements)
+          {
+            UNIMPLEMENTED ("DefaultCombinator: follow up iteration");
+            return *this;
+          }
+        
+        
+        /* === Iteration control API for IterStateWrapper== */
+        
+        friend bool
+        checkPoint (DefaultCombinator const& sequence)
+        {
+          UNIMPLEMENTED ("how to determine exhausted state on the combined results");
+          return false;
+        }
+        
+        friend reference
+        yield (DefaultCombinator const& sequence)
+        {
+          UNIMPLEMENTED ("how to yield a result from the combined iterator");
+        }
+        
+        friend void
+        iterNext (DefaultCombinator & sequence)
+        {
+          UNIMPLEMENTED ("increment the source iterator and switch to the next partial result sequence if necessary");
+        }
+        
+      };
+    
+    
+    /**
+     * Helper template to bootstrap a chain of IterExplorers.
+     * This is a "state corer", which basically just wraps a given
+     * source iterator and provides the necessary free functions
+     * (iteration control API) to use this as iteration state
+     * within IterExplorer.
+     * @note to ease building such an initial version of the Iterator Monad,
+     *       use the free function \link #exploreIter \endlink
+     */
+    template
+    class WrappedSequence
+      : public IT
+      {
+      public:
+        WrappedSequence()
+          : IT()
+          { }
+        
+        WrappedSequence(IT const& srcIter)
+          : IT(srcIter)
+          { }
+        
+        
+        /* === Iteration control API for IterStateWrapper == */
+  
+        inline bool
+        checkPoint (WrappedSequence const& sequence)
+        {
+          return bool(sequence);
+        }
+        
+        inline typename IT::reference
+        yield (WrappedSequence const& sequence)
+        {
+          return *sequence;
+        }
+        
+        inline void
+        iterNext (WrappedSequence & sequence)
+        {
+          ++sequence;
+        }
+      };
+      
+  }//(End) namespace iter_explorer : predefined policies and configurations
+  
+  
+  
+  
+  template
+  IterExplorer >
+  exploreIter (IT const& srcSeq)
+  {
+    return IterExplorer > (srcSeq);
+  }
+    
+    
+  
+  
+  
   
   
   
diff --git a/src/lib/linked-elements.hpp b/src/lib/linked-elements.hpp
index 9bb76b4df..c12342dc0 100644
--- a/src/lib/linked-elements.hpp
+++ b/src/lib/linked-elements.hpp
@@ -520,6 +520,13 @@ namespace lib {
           return !head_;
         }
       
+    private:
+      /**
+       * Iteration is just following the single linked list.
+       * We encapsulate this simple pointer into a dedicated marker type
+       * to ease the handling and mixing of iterators and const iterators.
+       * (Explanation: IterationState depends on our type parameters...)
+       */
       struct IterationState
         {
           N* node;
@@ -535,7 +542,8 @@ namespace lib {
           }
         };
       
-      typedef IterStateWrapper iterator;
+    public:
+      typedef IterStateWrapper<      N, IterationState> iterator;
       typedef IterStateWrapper const_iterator;
       
       
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index e29768c62..71e0f5ffc 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -38,20 +38,6 @@
 
 
 namespace lib {
-  
-  namespace iter {
-    
-    template<>
-    struct TypeBinding
-      {
-        typedef uint value_type;
-        typedef uint const& reference;
-        typedef uint const* pointer;
-      };
-  }
-  
-
-  
 namespace test{
   
   using ::Test;
@@ -71,6 +57,10 @@ namespace test{
   
 //    uint NUM_ELMS = 10;
     
+    /**
+     * This iteration state type describes
+     * a sequence of numbers still to be delivered.
+     */
     class State
       {
         uint p,e;
@@ -103,7 +93,10 @@ namespace test{
     
     
     
-    /** */
+    /** 
+     * A straight number sequence as basic test iterator.
+     * The tests will dress up this source sequence in various ways.
+     */
     class NumberSequence
       : public IterStateWrapper
       {
@@ -117,7 +110,7 @@ namespace test{
             : IterStateWrapper (State(start,end))
             { }
       };
-      
+    
     inline NumberSequence
     seq (uint end)
     {
@@ -135,7 +128,7 @@ namespace test{
     
     
     /** Diagnostic helper: "squeeze out" the given iterator
-     * and join all yielded elements into a string
+     * and join all the elements yielded into a string
      */
     template
     inline string
@@ -150,7 +143,7 @@ namespace test{
       return buff.str();
     }
     
-  } // (END) impl test dummy container
+  } // (END) test helpers
   
   
   

From 3a7db1603e3736e6ecb9927de76ca6b9cf61c91c Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 28 May 2012 03:42:15 +0200
Subject: [PATCH 466/496] add test for the base case

---
 tests/lib/iter-explorer-test.cpp | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index 71e0f5ffc..3a5edfcca 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -34,6 +34,7 @@
 //#include 
 #include 
 #include 
+#include 
 
 
 
@@ -49,6 +50,7 @@ namespace test{
 //  using std::vector;
 //  using std::cout;
 //  using std::endl;
+  using std::string;
   using lib::LinkedElements;
   using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
   
@@ -191,6 +193,7 @@ namespace test{
         {
           verifyStateAdapter();
           
+          verifyMonadOperator ();
           UNIMPLEMENTED ("IterExplorer Monad");
           verifyChainedIterators();
           verifyRawChainedIterators();
@@ -428,6 +431,35 @@ namespace test{
         }
       
       
+      
+      /** @test cover the basic monad bind operator,
+       * which is used to build all the specialised Iterator flavours.
+       * The default implementation ("Combinator strategy") uses heap allocations
+       * to keep track of the intermediary results. (Of course this can be changed)
+       */
+      void
+      verifyMonadOperator ()
+        {
+          typedef IterExplorer > SrcSequence;
+          
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
+          SrcSequence src = exploreIter(seq(5));
+          
+          string result = materialise(src >>= explode);
+          CHECK (result == "0-0-1-0-1-2-0-1-2-3");
+          
+          result = materialise(src >>= explode >>= explode);
+          CHECK (result == "0-0-0-1-0-0-1-0-1-2");
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
+        }
+      
+      static NumberSequence
+      explode (uint top)
+        {
+          return seq(0,top);
+        }
+      
+      
     };
   
   LAUNCHER (IterExplorer_test, "unit common");

From dc3ebd4a8f632a214b725f9921f066c1b87f6549 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 3 Jun 2012 04:09:01 +0200
Subject: [PATCH 467/496] first working concept for an "iterator monad"

the intention is to use this to simplify
generating render jobs based on the elaborated
dependency network of the render nodes. The key
challenge is to overcome the necessity to
store partially done evaluations as
continuation
---
 src/lib/iter-adapter.hpp         |   2 +-
 src/lib/iter-explorer.hpp        | 176 +++++++++++++++++++++----------
 tests/lib/iter-explorer-test.cpp |  74 ++++++++++---
 3 files changed, 183 insertions(+), 69 deletions(-)

diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp
index cc95c1859..50e70aac4 100644
--- a/src/lib/iter-adapter.hpp
+++ b/src/lib/iter-adapter.hpp
@@ -287,7 +287,7 @@ namespace lib {
       typedef T& reference;
       typedef T  value_type;
       
-      IterStateWrapper (ST initialState)
+      IterStateWrapper (ST const& initialState)
         : core_(initialState)
         { 
           checkPoint (core_);
diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index cc03ea2bd..ffc5865bc 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -29,6 +29,27 @@
  ** The implementation is based on the IterStateWrapper, which is one of the basic helper templates
  ** provided by iter-adapter.hpp.
  ** 
+ ** \par Iterators as Monad
+ ** The fundamental idea behind the implementation technique used here is the \em Monad pattern
+ ** known from functional programming. A Monad is a (abstract) container created by using some specific functions.
+ ** This is an quite abstract concept with a wide variety of applications (things like IO state, parsers, combinators,
+ ** calculations with exception handling but also simple data structures like lists or trees). The key point with
+ ** any monad is the ability to \em bind a function into the monad; this function will work on the \em internals
+ ** of the monad and produce a modified new monad instance. In the simple case of a list, "binding" a function
+ ** basically means to map the function onto the elements in the list.
+ ** 
+ ** \par Rationale
+ ** The primary benefit of using the monad pattern is to separate the transforming operation completely from
+ ** the mechanics of applying that operation and combining the results. More specifically, we rely on an iterator
+ ** to represent an abstracted source of data and we expose the combined and transformed results again as such
+ ** an abstracted data sequence. The transformation to apply can be selected at runtime (as a functor), and
+ ** also the logic how to combine elements can be implemented elsewhere. The monad pattern defines a sane
+ ** way of representing this partial evaluation state without requiring a container for intermediary
+ ** results. This is especially helpful when
+ ** - a flexible and unspecific source data structure needs to be processed
+ ** - and this evaluation needs to be done asynchronously and in parallel (no locking, immutable data)
+ ** - and a partial evaluation needs to be stored as continuation (not relying on the stack for partial results) 
+ ** 
  ** @see IterExplorer_test.cpp
  ** @see iter-adapter.hpp
  ** @see itertools.hpp
@@ -45,6 +66,7 @@
 //#include "lib/bool-checkable.hpp"
 #include "lib/meta/function.hpp"
 #include "lib/iter-adapter.hpp"
+#include "lib/util.hpp"
 
 //#include 
 
@@ -92,7 +114,7 @@ namespace lib {
 
   namespace iter_explorer {
     
-    template
+    template
     class DefaultCombinator;
   }
 
@@ -119,8 +141,9 @@ namespace lib {
    * functional programming language.
    * 
    * When invoking the bind (flat map) operator, a suitably represented \em functor
-   * is embedded into an instance of the Combinator template. The resulting object
-   * becomes the state core of the new IterExplorer object returned as result.
+   * is embedded into an instance of the Combinator template. Moreover, a copy of
+   * the current IterExplorer is embedded alongside. The resulting object becomes
+   * the state core of the new IterExplorer object returned as result.
    * So the result \em is an iterator, but -- when "pulled" -- it will in turn
    * pull from the source iterator and feed the provided elements through the
    * \em exploration functor, which this way has been \em bound into the resulting
@@ -135,23 +158,35 @@ namespace lib {
    *          chain of operations detailed elsewhere, and provided in a dynamic fashion
    *          (as functor at runtime). More specifically, these operations will expand
    *          and explore a dynamic source data set on the fly. Notably this allows
-   *          us to handle such an "exploration" on-the-fly, without the need to
-   *          allocate heap memory to store intermediary results, but also without
+   *          us to handle such an "exploration" in a flexible way, without the need
+   *          to allocate heap memory to store intermediary results, but also without
    *          using the stack and recursive programming.
+   * @warning be sure to consider the effects of copying iterators on any hidden state
+   *          referenced by the source iterator(s). Basically any iterator must behave
+   *          sane when copied while in iteration: IterExplorer first evaluates the
+   *          head element of the source (the explorer function should build an 
+   *          independent, new result sequence based on this first element).
+   *          Afterwards, the source is \em advanced and then \em copied 
+   *          into the result iterator.
    */
   template class _COM_ = iter_explorer::DefaultCombinator
+          ,template class _COM_ = iter_explorer::DefaultCombinator
           >
   class IterExplorer
     : public IterStateWrapper
     {
       
-      typedef typename SRC::value_type value_type;
-      typedef typename SRC::reference  reference;
-      
-
       
     public:
+      typedef typename SRC::value_type value_type;
+      typedef typename SRC::reference reference;
+      typedef typename SRC::pointer  pointer;
+      
+      
+      /** by default create an empty iterator */
+      IterExplorer() { }
+      
+      
       /** wrap an iterator like state representation
        *  to build it into a monad. The resulting entity
        *  is both an iterator yielding the elements generated
@@ -163,24 +198,37 @@ namespace lib {
         { }
       
       
-      /** monad bind ("flat map") operator */
+      /** monad bind ("flat map") operator.
+       *  Using a specific function to explore and work
+       *  on the "contents" of this IterExplorer, with the goal
+       *  to build a new IterExplorer combining the results of this
+       *  function application. The enclosing IterExplorer instance
+       *  provides a Strategy template _COM_, which defines how those
+       *  results are actually to be combined. An instantiation of
+       *  this "Combinator" strategy becomes the state core
+       *  of the result iterator. 
+       */
       template
-      IterExplorer<_COM_, _COM_>
+      IterExplorer<_COM_, _COM_>
       operator >>= (FUN explorer)
         {
-          typedef _COM_ Combinator;
-        
-          return IterExplorer(
-                   Combinator(explorer)
-                             .startWith( explorer(accessFirstElement()))
-                             .followUp (accessRemainingElements()));
+          typedef _COM_         Combinator;       // instantiation of the combinator strategy
+          typedef IterExplorer ResultsIter;      // result IterExplorer using that instance as state core
+          
+          Combinator combinator(explorer);                          // build a new iteration state core
+          if (this->isValid())                                      // if source iterator isn't empty...
+            {
+              combinator.startWith( explorer(accessHeadElement())); // immediately apply the function to the first element
+              combinator.followUp (accessRemainingElements());      // and provide state to allow for continued exploration later
+            }
+          return ResultsIter (combinator);                                    
         }
       
       
-    private:
       
+    private:
       reference
-      accessFirstElement()
+      accessHeadElement()
         {
           return this->operator* ();
         }
@@ -197,23 +245,31 @@ namespace lib {
     
     
   namespace iter_explorer { ///< predefined policies and configurations
-      
+    
+    using util::unConst;
       
     /**
      * a generic "Combinator strategy" for IterExplorer.
-     * This fallback solution just assumes that the source is a
-     * Lumiera Forward Iterator -- consequently we need to do heap
-     * allocations behind the scenes, as the size and structure of
-     * the expansion results is determined at runtime.
-     * @note the whole purpose of IterExplorer is to be more efficient
-     *       then this fallback, thus requiring more specific Combinator
-     *       strategies, able to exploit specific knowledge of the
-     *       source iterator's implementation.
+     * This fallback solution doesn't assume anything beyond the source
+     * and the intermediary result(s) being a Lumiera Forward Iterators.
+     * @note the implementation stores the functor into a std::function object,
+     *       which might cause heap allocations, depending on given function.
+     *       Besides, the implementation holds one instance of the (intermediary)
+     *       result iterator (yielded by invoking the function) and a copy of the
+     *       original IterExplorer source sequence, to get the further elements
+     *       when the initial results are exhausted.
      */
-    template
+    template
     class DefaultCombinator
       {
         typedef typename _Fun::Ret  ResultIter;
+        typedef typename SRC::value_type SrcElement;
+        typedef function Explorer;
+        
+         
+        SRC srcSeq_;
+        ResultIter results_;
+        Explorer explorer_;
         
       public:
         typedef typename ResultIter::value_type value_type;
@@ -221,49 +277,61 @@ namespace lib {
         typedef typename ResultIter::pointer    pointer;
   
         
+        DefaultCombinator() { }
         
-        DefaultCombinator(FUN explorer)
-          {
-            UNIMPLEMENTED ("representation of the sequence of partial results");
-          }
+        DefaultCombinator(FUN explorerFunction)
+          : srcSeq_()
+          , results_()
+          , explorer_(explorerFunction)
+          { }
         
-        DefaultCombinator &
+        // using standard copy operations
+        
+        
+        void
         startWith (ResultIter firstExplorationResult)
           {
-            UNIMPLEMENTED ("DefaultCombinator: yield first result");
-            return *this;
+            results_ = firstExplorationResult;
           }
         
-        template
-        DefaultCombinator&
-        followUp (SRC followUpSourceElements)
+        void
+        followUp (SRC & followUpSourceElements)
           {
-            UNIMPLEMENTED ("DefaultCombinator: follow up iteration");
-            return *this;
+            REQUIRE (explorer_);
+            srcSeq_ = followUpSourceElements;
           }
         
-        
+      private:
+        bool
+        findNextResultElement()
+          {
+            while (!results_ && srcSeq_)
+              {
+                results_ = explorer_(*srcSeq_);
+                ++srcSeq_;
+              }
+            return bool(results_);
+          
+          }
         /* === Iteration control API for IterStateWrapper== */
         
         friend bool
-        checkPoint (DefaultCombinator const& sequence)
+        checkPoint (DefaultCombinator const& seq)
         {
-          UNIMPLEMENTED ("how to determine exhausted state on the combined results");
-          return false;
+          return unConst(seq).findNextResultElement();
         }
         
         friend reference
-        yield (DefaultCombinator const& sequence)
+        yield (DefaultCombinator const& seq)
         {
-          UNIMPLEMENTED ("how to yield a result from the combined iterator");
+          return *(seq.results_);
         }
         
         friend void
-        iterNext (DefaultCombinator & sequence)
+        iterNext (DefaultCombinator & seq)
         {
-          UNIMPLEMENTED ("increment the source iterator and switch to the next partial result sequence if necessary");
+          ++(seq.results_);
         }
-        
       };
     
     
@@ -292,19 +360,19 @@ namespace lib {
         
         /* === Iteration control API for IterStateWrapper == */
   
-        inline bool
+        friend bool
         checkPoint (WrappedSequence const& sequence)
         {
           return bool(sequence);
         }
         
-        inline typename IT::reference
+        friend typename IT::reference
         yield (WrappedSequence const& sequence)
         {
           return *sequence;
         }
         
-        inline void
+        friend void
         iterNext (WrappedSequence & sequence)
         {
           ++sequence;
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index 3a5edfcca..88001f510 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -31,7 +31,7 @@
 #include "lib/linked-elements.hpp"
 
 #include 
-//#include 
+#include 
 #include 
 #include 
 #include 
@@ -48,8 +48,8 @@ namespace test{
   using util::isnil;
   using util::isSameObject;
 //  using std::vector;
-//  using std::cout;
-//  using std::endl;
+  using std::cout;
+  using std::endl;
   using std::string;
   using lib::LinkedElements;
   using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
@@ -134,7 +134,7 @@ namespace test{
      */
     template
     inline string
-    materialise (II & ii)
+    materialise (II ii)
     {
       std::ostringstream buff;
       while (ii)
@@ -145,6 +145,18 @@ namespace test{
       return buff.str();
     }
     
+    template
+    inline void
+    pullOut (II & ii)
+    {
+      while (ii)
+        {
+          cout << *ii;
+          if (++ii) cout << "-";
+        }
+      cout << endl;
+    }
+    
   } // (END) test helpers
   
   
@@ -213,16 +225,20 @@ namespace test{
       void
       verifyStateAdapter ()
         {
-          NumberSequence ii = seq(5);
+          NumberSequence ii = seq(9);
           CHECK (!isnil (ii));
           CHECK (0 == *ii);
-          CHECK (materialise(ii) == "0-1-2-3-4");
+          ++ii;
+          CHECK (1 == *ii);
+          pullOut(ii);
           CHECK ( isnil (ii));
           CHECK (!ii);
           
           VERIFY_ERROR (ITER_EXHAUST, *ii );
           VERIFY_ERROR (ITER_EXHAUST, ++ii );
           
+          ii = seq(5);
+          CHECK (materialise(ii) == "0-1-2-3-4");
           ii = seq(5,8);
           CHECK (materialise(ii) == "5-6-7");
           
@@ -434,23 +450,53 @@ namespace test{
       
       /** @test cover the basic monad bind operator,
        * which is used to build all the specialised Iterator flavours.
-       * The default implementation ("Combinator strategy") uses heap allocations
-       * to keep track of the intermediary results. (Of course this can be changed)
+       * The default implementation ("Combinator strategy") just joins and flattens
+       * the result sequences created by the functor bound into the monad. For this test,
+       * we use a functor \c explode(top), which returns the sequence 0...top.
        */
       void
       verifyMonadOperator ()
         {
-          typedef IterExplorer > SrcSequence;
+          // IterExplorer as such is an iterator wrapping the source sequence
+          string result = materialise (exploreIter(seq(5)));
+          CHECK (result == "0-1-2-3-4");
           
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
-          SrcSequence src = exploreIter(seq(5));
+          // now, if the source sequence yields exactly one element 5...
+          result = materialise (exploreIter(seq(5,6)));
+          CHECK (result == "5");
           
-          string result = materialise(src >>= explode);
+          // then binding the explode()-Function yields just the result of invoking explode(5) 
+          result = materialise (exploreIter(seq(5,6)) >>= explode);
+          CHECK (result == "0-1-2-3-4");
+          
+          // binding anything into an empty sequence still results in an empty sequence
+          result = materialise (exploreIter(seq(0)) >>= explode);
+          CHECK (result == "");
+          
+          // also, n case the bound function yields an empty sequence, the result remains empty
+          result = materialise (exploreIter(seq(1)) >>= explode);
+          CHECK (result == "");
+          
+          // combining an empty sequence and the one element sequence (seq(0,1)) results in just one element
+          result = materialise (exploreIter(seq(2)) >>= explode);
+          CHECK (result == "0");
+          
+          // multiple result sequences will be joined (flattened) into one sequence
+          result = materialise (exploreIter(seq(5)) >>= explode);
           CHECK (result == "0-0-1-0-1-2-0-1-2-3");
           
-          result = materialise(src >>= explode >>= explode);
+          // since the result is a monad, we can again bind yet another function
+          result = materialise((exploreIter(seq(5)) >>= explode) >>= explode);
           CHECK (result == "0-0-0-1-0-0-1-0-1-2");
-#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
+          
+                        // Explanation:
+                        // 0 -> empty sequence, gets dropped
+                        // 1 -> 1-element sequence {0}
+                        // 2 -> {0,1}
+                        // 3 -> {0,1,2}
+                        
+                        // Note: when cascading multiple >>= the parentheses are necessary, since in C++ unfortunately
+                        // the ">>=" associates to the right, while the proper monad bind operator should associate to the left
         }
       
       static NumberSequence

From da4a343e9e2bbc584a1f0471b596072ce4570e1b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 3 Jun 2012 20:26:43 +0200
Subject: [PATCH 468/496] refactor IterExplorer to allow for more flexible
 strategy definition

---
 src/lib/iter-explorer.hpp        | 151 ++++++++++++++++++++++++-------
 tests/lib/iter-explorer-test.cpp |  22 ++---
 2 files changed, 131 insertions(+), 42 deletions(-)

diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index ffc5865bc..a9a939419 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -22,7 +22,7 @@
 
 /** @file iter-explorer.hpp
  ** Helper template(s) for establishing various evaluation strategies for hierarchical data structures.
- ** Based on the Lumiera Forward Iterators concept and using the basic IterAdaptor templates,
+ ** Based on the Lumiera Forward Iterator concept and using the basic IterAdaptor templates,
  ** these components allow to implement typical evaluation strategies, like e.g. depth-first or
  ** breadth-first exploration of a hierarchical structure. Since the access to this structure is
  ** abstracted through the underlying iterator, what we effectively get is a functional datastructure.
@@ -32,10 +32,10 @@
  ** \par Iterators as Monad
  ** The fundamental idea behind the implementation technique used here is the \em Monad pattern
  ** known from functional programming. A Monad is a (abstract) container created by using some specific functions.
- ** This is an quite abstract concept with a wide variety of applications (things like IO state, parsers, combinators,
- ** calculations with exception handling but also simple data structures like lists or trees). The key point with
- ** any monad is the ability to \em bind a function into the monad; this function will work on the \em internals
- ** of the monad and produce a modified new monad instance. In the simple case of a list, "binding" a function
+ ** This is an rather abstract concept with a wide variety of applications (things like IO state, parsers, combinators,
+ ** calculations with exception handling but also simple data structures like lists or trees). The key point with any
+ ** monad is the ability to \em bind a function into the monad; this function will work on the \em internals of the
+ ** monad and produce a modified new monad instance. In the simple case of a list, "binding" a function
  ** basically means to map the function onto the elements in the list.
  ** 
  ** \par Rationale
@@ -168,6 +168,12 @@ namespace lib {
    *          independent, new result sequence based on this first element).
    *          Afterwards, the source is \em advanced and then \em copied 
    *          into the result iterator.
+   * @param SRC the source sequence or iterator to wrap
+   * @param _COM_ "Combinator" strategy template. When binding (\c >>= ) a function,
+   *          an instance of that strategy becomes the new SRC for the created new
+   *          IterExplorer. This instantiation of the strategy gets as type parameters
+   *          - this IterExplorer's instance type
+   *          - the type of the function bound with \c >>= 
    */
   template class _COM_ = iter_explorer::DefaultCombinator
@@ -187,7 +193,7 @@ namespace lib {
       IterExplorer() { }
       
       
-      /** wrap an iterator like state representation
+      /** wrap an iterator-like state representation
        *  to build it into a monad. The resulting entity
        *  is both an iterator yielding the elements generated
        *  by the core, and it provides the (monad) bind operator.
@@ -247,29 +253,64 @@ namespace lib {
   namespace iter_explorer { ///< predefined policies and configurations
     
     using util::unConst;
-      
+    
     /**
-     * a generic "Combinator strategy" for IterExplorer.
-     * This fallback solution doesn't assume anything beyond the source
-     * and the intermediary result(s) being a Lumiera Forward Iterators.
-     * @note the implementation stores the functor into a std::function object,
-     *       which might cause heap allocations, depending on given function.
-     *       Besides, the implementation holds one instance of the (intermediary)
-     *       result iterator (yielded by invoking the function) and a copy of the
-     *       original IterExplorer source sequence, to get the further elements
-     *       when the initial results are exhausted.
+     * Building block: evaluating source elements.
+     * This strategy will be tied into a "Combinator"
+     * to hold the actual functor bound into the enclosing
+     * IterExplorer monad to work on the contained elements.
      */
-    template
-    class DefaultCombinator
+    template
+    struct ExploreByFunction
+      : function
       {
-        typedef typename _Fun::Ret  ResultIter;
-        typedef typename SRC::value_type SrcElement;
-        typedef function Explorer;
+        template
+        ExploreByFunction(FUN explorationFunctionDefinition)
+          : function(explorationFunctionDefinition)
+          { }
         
-         
-        SRC srcSeq_;
+        ExploreByFunction()  { } ///< by default initialised to bottom function
+      };
+    
+    /**
+     * Support for a special use case: an Iterator of Iterators, joining results.
+     * In this case, already the source produces a sequence of Iterators, which
+     * just need to be passed through to the output buffer unaltered. Using this
+     * within the DefaultCombinator strategy creates a combined, flattened iterator
+     * of all the source iterator's contents.  
+     */
+    template
+    struct UnalteredPassThrough;
+    
+    template
+    struct UnalteredPassThrough
+      {
+        IT operator() (IT elm)  const { return elm; }
+      };
+    
+    
+    /**
+     * Building block: evaluate and combine a sequence of iterators.
+     * This implementation helper provides two kinds of "buffers" (actually implemented
+     * as iterators): A result buffer (iterator) which holds a sequence of already prepared
+     * result elements, which can be retrieved through iteration right away. And a supply buffer
+     * (iterator) holding raw source elements. When the result buffer is exhausted, the next source
+     * element will be pulled from there and fed through the "evaluation strategy", which typically
+     * is a function processing the source element and producing a new result buffer (iterator). 
+     */
+    template class  _EXP_  = ExploreByFunction   ///< Strategy: how to store and evaluate the function to apply on each element
+            >
+    class CombinedIteratorEvaluation
+      {
+        typedef typename _Fun::Ret     ResultIter;
+        typedef typename SRC::value_type    SrcElement;
+        typedef _EXP_ Explorer;
+        
+        
+        SRC         srcSeq_;
         ResultIter results_;
-        Explorer explorer_;
+        Explorer  explorer_;
         
       public:
         typedef typename ResultIter::value_type value_type;
@@ -277,9 +318,9 @@ namespace lib {
         typedef typename ResultIter::pointer    pointer;
   
         
-        DefaultCombinator() { }
+        CombinedIteratorEvaluation() { }
         
-        DefaultCombinator(FUN explorerFunction)
+        CombinedIteratorEvaluation(FUN explorerFunction)
           : srcSeq_()
           , results_()
           , explorer_(explorerFunction)
@@ -289,13 +330,13 @@ namespace lib {
         
         
         void
-        startWith (ResultIter firstExplorationResult)
+        setResultSequence (ResultIter firstExplorationResult)
           {
             results_ = firstExplorationResult;
           }
         
         void
-        followUp (SRC & followUpSourceElements)
+        setSourceSequence (SRC & followUpSourceElements)
           {
             REQUIRE (explorer_);
             srcSeq_ = followUpSourceElements;
@@ -316,24 +357,72 @@ namespace lib {
         /* === Iteration control API for IterStateWrapper== */
         
         friend bool
-        checkPoint (DefaultCombinator const& seq)
+        checkPoint (CombinedIteratorEvaluation const& seq)
         {
           return unConst(seq).findNextResultElement();
         }
         
         friend reference
-        yield (DefaultCombinator const& seq)
+        yield (CombinedIteratorEvaluation const& seq)
         {
           return *(seq.results_);
         }
         
         friend void
-        iterNext (DefaultCombinator & seq)
+        iterNext (CombinedIteratorEvaluation & seq)
         {
           ++(seq.results_);
         }
       };
     
+    /**
+     * a generic "Combinator strategy" for IterExplorer.
+     * This fallback solution doesn't assume anything beyond the source
+     * and the intermediary result(s) being a Lumiera Forward Iterators.
+     * @note the implementation stores the functor into a std::function object,
+     *       which might cause heap allocations, depending on given function.
+     *       Besides, the implementation holds one instance of the (intermediary)
+     *       result iterator (yielded by invoking the function) and a copy of the
+     *       original IterExplorer source sequence, to get the further elements
+     *       when the initial results are exhausted.
+     */
+    template
+    class DefaultCombinator
+      : public CombinedIteratorEvaluation
+      {
+        typedef typename _Fun::Ret  ResultIter;
+        
+      public:
+        DefaultCombinator() { }
+        
+        DefaultCombinator(FUN explorerFunction)
+          : CombinedIteratorEvaluation(explorerFunction)
+          { }
+        
+        
+        void
+        startWith (ResultIter firstExplorationResult)
+          {
+            this->setResultSequence (firstExplorationResult);
+          }
+        
+        void
+        followUp (SRC & followUpSourceElements)
+          {
+            this->setSourceSequence (followUpSourceElements);
+          }
+      };
+    
+    
+    /**
+     * Special configuration for combining / flattening the results
+     * of a sequence of iterators 
+     */
+    template
+    class ChainedIters
+      {
+        /////////////TODO unimplemented
+      };
     
     /**
      * Helper template to bootstrap a chain of IterExplorers.
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index 88001f510..070fbbeca 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -61,7 +61,7 @@ namespace test{
     
     /**
      * This iteration state type describes
-     * a sequence of numbers still to be delivered.
+     * a sequence of numbers yet to be delivered.
      */
     class State
       {
@@ -385,10 +385,11 @@ namespace test{
        * of a tree like system, built on top of the IterExplorer monad.
        * 
        * \par Test data structure
-       * We built a functional datastructure here, on the fly, while exploring it.
+       * We build a functional datastructure here, on the fly, while exploring it.
        * The \c exploreChildren(m) function generates this tree like datastructure:
        * For a given node with number value i, it returns the children (2*i-1) and 2*i
        * -- but only up to the given maximum value m.
+       * 
        * If we start such a tree structure with a root node 1, and maximum = 8, this yields:
        * \code
        * (       1       )
@@ -400,9 +401,9 @@ namespace test{
        * 
        * \par How the exploration works
        * We use a pre defined Template \link DepthFirstExplorer \endlink, which is built on top of IterExplorer.
-       * It contains the depth-first exploration strategy hardwired. Actually this effect is achieved by
-       * defining a specific way how to \em combine the results of a \em exploration -- the latter being
-       * the function which generates the data structure. To yield a depth-first exploration, all we have to do
+       * It contains the depth-first exploration strategy in a hardwired fashion. Actually this effect is achieved
+       * by defining a specific way how to \em combine the results of an \em exploration -- the latter being the
+       * function which generates the data structure. To yield a depth-first exploration, all we have to do
        * is to delve down immediately into the children, right after visiting the node itself.
        * 
        * Now, when creating such a DepthFirstExplorer by wrapping a given source iterator, the result is again
@@ -421,8 +422,8 @@ namespace test{
           typedef DepthFirstExplorer DepthFirst;
           DepthFirst root (seq(1));
           
-          NumberSequence explorationResult = root >>= exploreChildren(8);
-          CHECK (materialise(explorationResult) == "1-1-1-1-2-2-3-4-2-3-5-6-4-7-8");
+          string explorationResult = materialise(root >>= exploreChildren(8));
+          CHECK (explorationResult == "1-1-1-1-2-2-3-4-2-3-5-6-4-7-8");
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
         }
       
@@ -441,8 +442,8 @@ namespace test{
           typedef BreadthFirstExplorer BreadthFirst;
           BreadthFirst root (seq(1));
           
-          NumberSequence explorationResult = root >>= exploreChildren(8);
-          CHECK (materialise(explorationResult) == "1-1-2-1-2-3-4-1-2-3-4-5-6-7-8");
+          string explorationResult = materialise(root >>= exploreChildren(8));
+          CHECK (explorationResult == "1-1-2-1-2-3-4-1-2-3-4-5-6-7-8");
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
         }
       
@@ -504,10 +505,9 @@ namespace test{
         {
           return seq(0,top);
         }
-      
-      
     };
   
+  
   LAUNCHER (IterExplorer_test, "unit common");
   
   

From d14b37a71db86bb5348bd343ad524b2eb7867265 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 4 Jun 2012 01:08:58 +0200
Subject: [PATCH 469/496] utility class factored out and covered by test

...a stack which can be Lumiera-iterated
---
 src/lib/iter-adapter.hpp      |  18 ++++--
 src/lib/iter-stack.hpp        | 111 ++++++++++++++++++++++++++++++++++
 tests/40components.tests      |   4 ++
 tests/lib/iter-stack-test.cpp | 111 ++++++++++++++++++++++++++++++++++
 4 files changed, 239 insertions(+), 5 deletions(-)
 create mode 100644 src/lib/iter-stack.hpp
 create mode 100644 tests/lib/iter-stack-test.cpp

diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp
index 50e70aac4..c2ac2eafe 100644
--- a/src/lib/iter-adapter.hpp
+++ b/src/lib/iter-adapter.hpp
@@ -303,21 +303,21 @@ namespace lib {
       reference
       operator*() const
         {
-          _maybe_throw();
+          __throw_if_empty();
           return yield (core_);     // extension point: yield
         }
       
       pointer
       operator->() const
         {
-          _maybe_throw();
+          __throw_if_empty();
           return & yield(core_);    // extension point: yield
         }
       
       IterStateWrapper&
       operator++()
         {
-          _maybe_throw();
+          __throw_if_empty();
           iterNext (core_);         // extension point: iterNext
           return *this;
         }
@@ -334,10 +334,18 @@ namespace lib {
           return !isValid();
         }
       
-    private:
+    protected:
+      
+      /** allow derived classes to
+       *  access state representation */
+      ST &
+      stateCore()
+        {
+          return core_;
+        }
       
       void
-      _maybe_throw()  const
+      __throw_if_empty()  const
         {
           if (!isValid())
             _throwIterExhausted();
diff --git a/src/lib/iter-stack.hpp b/src/lib/iter-stack.hpp
new file mode 100644
index 000000000..607f43bf2
--- /dev/null
+++ b/src/lib/iter-stack.hpp
@@ -0,0 +1,111 @@
+/*
+  ITER-STACK.hpp  -  a stack which can be popped by iterating 
+
+  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.
+
+*/
+
+
+
+#ifndef LIB_ITER_STACK_H
+#define LIB_ITER_STACK_H
+
+
+#include "lib/error.hpp"
+#include "lib/iter-adapter.hpp"
+#include "lib/util.hpp"
+
+#include 
+
+
+namespace lib {
+  
+  
+  namespace { 
+    using util::unConst;
+    
+    /** 
+     * Wrapper to mark a std::stack instance for use
+     * as "state core" within lib::IterStateWrapper
+     */
+    template
+    struct IterStackStorage
+      : std::stack
+      {
+        /* === Iteration control API for IterStateWrapper == */
+        
+        friend bool
+        checkPoint (IterStackStorage const& elements)
+        {
+          return !elements.empty();
+        }
+        
+        friend TY &
+        yield (IterStackStorage const& elements)
+        {
+          return unConst(elements).top();
+        }
+        
+        friend void
+        iterNext (IterStackStorage & elements)
+        {
+          REQUIRE (!elements.empty());
+          elements.pop();
+        }
+      };
+  }//(End) Wrapper/Helper
+  
+  
+  
+  /** 
+   * A Stack which can be popped by iterating.
+   * This is a simple helper built on top of std::stack.
+   * Thus, each instance holds the full state, which is
+   * actually kept in heap allocated storage. Pushing of
+   * new elements and iterator use may be mixed.
+   * 
+   * Contrary to just using std::stack
+   * - the iteration is compliant to the Lumiera Forward Iterator concept
+   * - there is a simplified #pop() function which removes and returns at once
+   */
+  template
+  struct IterStack
+    : IterStateWrapper >
+    {
+      
+      // using default create and copy operations
+      
+      void
+      push (TY const& elm)
+        {
+          this->stateCore().push (elm);
+        }
+      
+      TY
+      pop()
+        {
+          this->__throw_if_empty();
+          TY frontElement (this->stateCore().top());
+          this->stateCore().pop();
+          return frontElement;
+        }
+    };
+  
+  
+} // namespace lib
+#endif
diff --git a/tests/40components.tests b/tests/40components.tests
index ca790c658..3b901d8d3 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -508,6 +508,10 @@ out: values_4_key(::[0-9]{1,2})+
 END
 
 
+TEST "Stack-like iterator" IterStack_test
+END
+
+
 TEST "Iterable type detection" IterableClassification_test <	 : Yes
 out: can_STL_ForEach	 : Yes
diff --git a/tests/lib/iter-stack-test.cpp b/tests/lib/iter-stack-test.cpp
new file mode 100644
index 000000000..82d93b4b5
--- /dev/null
+++ b/tests/lib/iter-stack-test.cpp
@@ -0,0 +1,111 @@
+/*
+  IterStack(Test)  -  verify stack-like iterator
+
+  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.
+
+* *****************************************************/
+
+
+
+#include "lib/test/run.hpp"
+#include "lib/test/test-helper.hpp"
+#include "lib/util.hpp"
+
+#include "lib/iter-stack.hpp"
+
+
+
+namespace lib {
+namespace test{
+  
+  using ::Test;
+  using util::isnil;
+  using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
+  
+  
+  
+  
+  typedef IterStack IStack;
+  
+  
+  
+  
+  /*********************************************************************
+   * @test cover behaviour of a convenience class built by using a
+   *       std::stack as "state core" of an Lumiera IterStateWrapper.
+   *       - iterable according to the Lumiera Forward Iterator concept
+   *       - can push and pop values repeatedly
+   *       - iterating and push / pop can be mixed
+   *       - empty state detected consistently
+   * 
+   * @see IterExplorer
+   * @see IterAdapter
+   */
+  class IterStack_test : public Test
+    {
+      
+      virtual void
+      run (Arg)
+        {
+          IStack stack;
+          CHECK (isnil (stack));
+          
+          VERIFY_ERROR (ITER_EXHAUST,  *stack );
+          VERIFY_ERROR (ITER_EXHAUST, ++stack );
+          
+          stack.push (1);
+          stack.push (3);
+          stack.push (5);
+          stack.push (7);
+          stack.push (9);
+          
+          CHECK (!isnil (stack));
+          CHECK (9 == *stack);
+          
+          ++stack;
+          CHECK (7 == *stack);
+          
+          CHECK (7 == stack.pop());
+          CHECK (5 == *stack);
+          
+          ++++stack;
+          CHECK (1 == *stack);
+          CHECK (1 == stack.pop());
+          
+          CHECK (isnil (stack));
+          VERIFY_ERROR (ITER_EXHAUST,  *stack );
+          VERIFY_ERROR (ITER_EXHAUST, ++stack );
+          VERIFY_ERROR (ITER_EXHAUST, stack.pop() );
+          
+          
+          stack.push(23);
+          CHECK (23 == *stack);
+          
+          int i = stack.pop();
+          CHECK (i == 23);
+          VERIFY_ERROR (ITER_EXHAUST,  *stack );
+          CHECK (isnil (stack));
+        }
+      
+    };
+  
+  
+  LAUNCHER (IterStack_test, "unit common");
+  
+  
+}} // namespace lib::test

From 8db54131999afa2ed7ac4be4c42e3e879609c062 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 4 Jun 2012 02:35:00 +0200
Subject: [PATCH 470/496] implemented chained-iterators

...using the IterExplorer building blocks
---
 src/lib/iter-explorer.hpp        | 161 ++++++++++++++++++++++++++++---
 tests/lib/iter-explorer-test.cpp |  22 ++---
 2 files changed, 157 insertions(+), 26 deletions(-)

diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index a9a939419..1df055788 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -66,9 +66,13 @@
 //#include "lib/bool-checkable.hpp"
 #include "lib/meta/function.hpp"
 #include "lib/iter-adapter.hpp"
+#include "lib/iter-stack.hpp"
+#include "lib/meta/trait.hpp"
 #include "lib/util.hpp"
 
 //#include 
+#include 
+#include 
 
 
 namespace lib {
@@ -239,7 +243,7 @@ namespace lib {
           return this->operator* ();
         }
       
-      IterExplorer&
+      IterExplorer const&
       accessRemainingElements()
         {
           this->operator++ ();
@@ -253,6 +257,8 @@ namespace lib {
   namespace iter_explorer { ///< predefined policies and configurations
     
     using util::unConst;
+    using boost::enable_if;
+    using boost::disable_if;
     
     /**
      * Building block: evaluating source elements.
@@ -286,6 +292,7 @@ namespace lib {
     struct UnalteredPassThrough
       {
         IT operator() (IT elm)  const { return elm; }
+        bool operator! ()       const { return false; }  ///< identity function is always valid
       };
     
     
@@ -336,7 +343,7 @@ namespace lib {
           }
         
         void
-        setSourceSequence (SRC & followUpSourceElements)
+        setSourceSequence (SRC const& followUpSourceElements)
           {
             REQUIRE (explorer_);
             srcSeq_ = followUpSourceElements;
@@ -407,26 +414,94 @@ namespace lib {
           }
         
         void
-        followUp (SRC & followUpSourceElements)
+        followUp (SRC const& followUpSourceElements)
           {
             this->setSourceSequence (followUpSourceElements);
           }
       };
     
     
-    /**
-     * Special configuration for combining / flattening the results
-     * of a sequence of iterators 
-     */
-    template
-    class ChainedIters
+     
+    template
+    struct _is_iterator_of_iterators
       {
-        /////////////TODO unimplemented
+        typedef typename IT::value_type IteratorElementType;
+        
+        enum{ value = meta::can_IterForEach::value };
+      };
+    
+    
+    template
+    class ChainedIteratorImpl
+      : public CombinedIteratorEvaluation
+      { };
+    
+    
+    /**
+     * Special iterator configuration for combining / flattening the
+     * results of a sequence of iterators. This sequence of source iterators
+     * is assumed to be available as "Iterator yielding Iterators".
+     * The resulting class is a Lumiera Forward Iterator, delivering all the
+     * elements of all source iterators in sequence.
+     * @remarks this is quite similar to the IterExplorer monad, but without
+     *        binding an exploration function to produce the result sequences.
+     *        Rather, the result sequences are directly pulled from the source
+     *        sequence, which thus needs to be an "Iterator of Iterators".
+     *        Beyond that, the implementation relies on the same building
+     *        blocks as used for the full-blown IterExplorer.
+     * @param ITI iterator of iterators
+     * @param SEQ type of the individual sequence (iterator).
+     *            The value_type of this sequence will be the overall
+     *            resulting value type of the flattened sequence
+     */
+    template
+    class ChainedIters;
+    
+    template
+    class ChainedIters >::type
+                      >
+      : public IterStateWrapper
+                               >
+      {
+      public:
+        ChainedIters(ITI const& iteratorOfIterators)
+          { //  note: default ctor on parent -> empty sequences
+            this->stateCore().setSourceSequence (iteratorOfIterators);
+          }
+      };
+    
+    /**
+     * Convenience specialisation: manage the sequence of iterators automatically.
+     * @note in this case the \em first template parameter denotes the \em element sequence type;
+     *       we use a IterStack to hold the sequence-of-iterators in heap storage.
+     * @warning this specialisation will not be picked, if the \em value-type
+     *       of the given iterator is itself an iterator
+     */ 
+    template
+    class ChainedIters >::type
+                      > 
+      : public IterStateWrapper, SEQ>
+                               >
+      { 
+      public:
+        typedef IterStack IteratorIterator;
+        
+        ChainedIters(IteratorIterator const& iteratorOfIterators)
+          {
+            this->stateCore().setSourceSequence (iteratorOfIterators);
+          }
+        
+        /** empty result sequence by default */
+        ChainedIters() { }
       };
     
     /**
      * Helper template to bootstrap a chain of IterExplorers.
-     * This is a "state corer", which basically just wraps a given
+     * This is a "state core", which basically just wraps a given
      * source iterator and provides the necessary free functions
      * (iteration control API) to use this as iteration state
      * within IterExplorer.
@@ -474,16 +549,74 @@ namespace lib {
   
   
   template
-  IterExplorer >
+  inline IterExplorer >
   exploreIter (IT const& srcSeq)
   {
     return IterExplorer > (srcSeq);
   }
-    
-    
   
   
   
+  template
+  inline iter_explorer::ChainedIters
+  iterChain(IT const& seq)
+  {
+    typename iter_explorer::ChainedIters::IteratorIterator sequenceOfIterators;
+    
+    sequenceOfIterators.push (seq);
+    return iter_explorer::ChainedIters(sequenceOfIterators);
+  }
+  
+  template
+  inline iter_explorer::ChainedIters
+  iterChain(IT const& seq1, IT const& seq2)
+  {
+    typename iter_explorer::ChainedIters::IteratorIterator sequenceOfIterators;
+    
+    sequenceOfIterators.push (seq2);
+    sequenceOfIterators.push (seq1);
+    return iter_explorer::ChainedIters(sequenceOfIterators);
+  }
+  
+  template
+  inline iter_explorer::ChainedIters
+  iterChain(IT const& seq1, IT const& seq2, IT const& seq3)
+  {
+    typename iter_explorer::ChainedIters::IteratorIterator sequenceOfIterators;
+    
+    sequenceOfIterators.push (seq3);
+    sequenceOfIterators.push (seq2);
+    sequenceOfIterators.push (seq1);
+    return iter_explorer::ChainedIters(sequenceOfIterators);
+  }
+  
+  template
+  inline iter_explorer::ChainedIters
+  iterChain(IT const& seq1, IT const& seq2, IT const& seq3, IT const& seq4)
+  {
+    typename iter_explorer::ChainedIters::IteratorIterator sequenceOfIterators;
+    
+    sequenceOfIterators.push (seq4);
+    sequenceOfIterators.push (seq3);
+    sequenceOfIterators.push (seq2);
+    sequenceOfIterators.push (seq1);
+    return iter_explorer::ChainedIters(sequenceOfIterators);
+  }
+  
+  template
+  inline iter_explorer::ChainedIters
+  iterChain(IT const& seq1, IT const& seq2, IT const& seq3, IT const& seq4, IT const& seq5)
+  {
+    typename iter_explorer::ChainedIters::IteratorIterator sequenceOfIterators;
+    
+    sequenceOfIterators.push (seq5);
+    sequenceOfIterators.push (seq4);
+    sequenceOfIterators.push (seq3);
+    sequenceOfIterators.push (seq2);
+    sequenceOfIterators.push (seq1);
+    return iter_explorer::ChainedIters(sequenceOfIterators);
+  }
+  
   
   
   
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index 070fbbeca..02db3beeb 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -52,6 +52,7 @@ namespace test{
   using std::endl;
   using std::string;
   using lib::LinkedElements;
+  using lib::iter_explorer::ChainedIters;
   using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
   
   
@@ -206,7 +207,6 @@ namespace test{
           verifyStateAdapter();
           
           verifyMonadOperator ();
-          UNIMPLEMENTED ("IterExplorer Monad");
           verifyChainedIterators();
           verifyRawChainedIterators();
           
@@ -249,20 +249,19 @@ namespace test{
       
       
       
-      /** @test a simple and practical helper built on top of IterExplorer.
+      /** @test a convenient helper built using IterExplorer building blocks.
        * The resulting iterator \em combines and \em flattens a sequence
        * of source iterators, resulting in a simple sequence accessible
-       * again as iterator. Here we verify the convenience / default
-       * implementation; it uses a STL container behind the scenes
-       * to keep track of all the added source iterators
+       * as iterator again. Here we verify the convenience / default
+       * implementation; it uses a STL container (actually std:deque)
+       * behind the scenes to keep track of all added source iterators.
        */
       void
       verifyChainedIterators ()
         {
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
           typedef ChainedIters Chain;
           
-          Chain ci = iter::chain (seq(5),seq(7),seq(9));
+          Chain ci = iterChain (seq(5),seq(7),seq(9));
           
           CHECK (!isnil (ci));
           pullOut (ci);
@@ -271,11 +270,11 @@ namespace test{
           VERIFY_ERROR (ITER_EXHAUST, ++ci );
           
           CHECK (isnil(Chain()));
-          CHECK (iter::chain (NIL_Sequence));
+          CHECK (!iterChain (NIL_Sequence));
           
-          // Iterator chaining "flattens" one level of indirection
+          // Iterator chaining "flattens" one level of packaging
           NumberSequence s9 = seq(9); 
-          ci = iter::chain (s9);
+          ci = iterChain (s9);
           
           for ( ; s9 && ci; ++s9, ++ci )
             CHECK (*s9 == *ci);
@@ -288,7 +287,7 @@ namespace test{
           //       per-instance state (like e.g. NumberSequence used for this test),
           //       then the created chain is independent from the source iterators.
           s9 = seq(9);
-          ci = iter::chain (s9);
+          ci = iterChain (s9);
           CHECK (0 == *s9);
           CHECK (0 == *ci);
           
@@ -297,7 +296,6 @@ namespace test{
           CHECK (0 == *s9);
           pullOut (s9);
           CHECK (isnil(s9));
-#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
         }
       
       

From 697924925cb8dcf74f327021fb7106def4bffdfb Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 4 Jun 2012 04:18:15 +0200
Subject: [PATCH 471/496] pragmatic fix for the long-standing problem of
 detecting increment operator

our duck-detector had a shortcoming here and failed
to detect iterators, in case the increment operator
was inherited from a mix-in
---
 src/lib/itertools.hpp          | 14 ----------
 src/lib/meta/duck-detector.hpp | 47 ++++++++++++++++++++++------------
 2 files changed, 31 insertions(+), 30 deletions(-)

diff --git a/src/lib/itertools.hpp b/src/lib/itertools.hpp
index 5c5b5f750..8a4e78e96 100644
--- a/src/lib/itertools.hpp
+++ b/src/lib/itertools.hpp
@@ -331,13 +331,6 @@ namespace lib {
       FilterIter (IT const& src, PRED filterPredicate)
         : _Impl(_Filter(src,filterPredicate))
         { }
-      
-      FilterIter&
-      operator++ ()
-        {
-          _Impl::operator++();
-          return *this;
-        }
     };
   
   
@@ -478,13 +471,6 @@ namespace lib {
       TransformIter (IT const& src, FUN trafoFunc)
         : _IteratorImpl(_Trafo(src,trafoFunc))
         { }
-      
-      TransformIter&
-      operator++ ()
-        {
-          _IteratorImpl::operator++();
-          return *this;
-        }
     };
   
   
diff --git a/src/lib/meta/duck-detector.hpp b/src/lib/meta/duck-detector.hpp
index a559ed27a..f2b36c069 100644
--- a/src/lib/meta/duck-detector.hpp
+++ b/src/lib/meta/duck-detector.hpp
@@ -188,22 +188,37 @@
 
 
 
-/** Detector for a prefix increment operator. Works like function detection */
-#define META_DETECT_OPERATOR_INC()                         \
-    template                                   \
-    class HasOperator_inc                                    \
-      {                                                       \
-        template                  \
-        struct Probe                                            \
-          { };                                                   \
-                                                                  \
-        template                                          \
-        static Yes_t check(Probe * );            \
-        template                                              \
-        static No_t  check(...);                                      \
-                                                                       \
-      public:                                                           \
-        static const bool value = (sizeof(Yes_t)==sizeof(check(0))); \
+/** Detector for a prefix increment operator.
+ * @note there is a twist: because of the prefix and postfix version of increment,
+ *       detection through member-check will fail when both are present (ambiguity).
+ *       OTOH, when using function signature detection, the return type must match.
+ *       The latter fails in the common situation, when the increment operator was
+ *       mixed in through some base class. As a pragmatic solution, we do both kinds
+ *       of tests; so either remove one of the operators (typically postfix), or
+ *       add an forwarding override in the class to be checked */
+#define META_DETECT_OPERATOR_INC()                    \
+    template                              \
+    class HasOperator_inc                               \
+      {                                                  \
+        template             \
+        struct Probe_1                                     \
+          { };                                              \
+        template \
+        struct Probe_2                                        \
+          { };                                                 \
+                                                                \
+        template                                        \
+        static Yes_t check1(Probe_1 * );       \
+        template                                            \
+        static No_t  check1(...);                                   \
+        template                                            \
+        static Yes_t check2(Probe_2 * );                           \
+        template                                                \
+        static No_t  check2(...);                                       \
+                                                                         \
+      public:                                                             \
+        static const bool value = (sizeof(Yes_t)==sizeof(check1(0))    \
+                                 ||sizeof(Yes_t)==sizeof(check2(0)));   \
       };
 
 

From 75df58360748f73a04be5c7b6ac4d15a9a144a6e Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 4 Jun 2012 05:01:42 +0200
Subject: [PATCH 472/496] raw version of ChainedIter passes unit test

---
 tests/lib/iter-explorer-test.cpp | 67 ++++++++++++--------------------
 1 file changed, 25 insertions(+), 42 deletions(-)

diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index 02db3beeb..ad7aaf01e 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -25,10 +25,10 @@
 #include "lib/test/run.hpp"
 #include "lib/test/test-helper.hpp"
 #include "lib/util.hpp"
-#include "lib/util-foreach.hpp"
+//#include "lib/util-foreach.hpp"
+#include "lib/iter-adapter-stl.hpp"
 
 #include "lib/iter-explorer.hpp"
-#include "lib/linked-elements.hpp"
 
 #include 
 #include 
@@ -36,6 +36,7 @@
 #include 
 #include 
 
+#include "lib/meta/trait.hpp"
 
 
 namespace lib {
@@ -51,7 +52,7 @@ namespace test{
   using std::cout;
   using std::endl;
   using std::string;
-  using lib::LinkedElements;
+  using lib::iter_stl::eachElm;
   using lib::iter_explorer::ChainedIters;
   using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
   
@@ -112,6 +113,10 @@ namespace test{
           NumberSequence(uint start, uint end)
             : IterStateWrapper (State(start,end))
             { }
+          
+          /** allow using NumberSequence in LinkedElements
+           * (intrusive single linked list) */
+          NumberSequence* next;
       };
     
     inline NumberSequence
@@ -306,9 +311,8 @@ namespace test{
       void
       verifyRawChainedIterators ()
         {
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
-          typedef LinkedElements IterContainer;
-          typedef IterContainer::const_iterator IterIter;
+          typedef std::vector IterContainer;
+          typedef RangeIter IterIter;
           
           typedef ChainedIters Chain;
           
@@ -321,60 +325,39 @@ namespace test{
           CHECK (8 == *s9);
           
           IterContainer srcIters;
-          srcIters.push (s9);
-          srcIters.push (s7);
-          srcIters.push (s5);
+          srcIters.push_back (s5);
+          srcIters.push_back (s7);
+          srcIters.push_back (s9);
           
-          IterIter iti(srcIters.begin());
+          IterIter iti = eachElm(srcIters);
           CHECK (!isnil (iti));
-          CHECK (isSameObject (s5, *iti));
+          
+          // note: iterator has been copied
+          CHECK ( isSameObject (srcIters[0], *iti));
+          CHECK (!isSameObject (s5,          *iti));
           
           Chain chain(iti);
           CHECK (!isnil (iti));
           CHECK (1 == *chain);
-          
+
           ++chain;
           CHECK (2 == *chain);
-          CHECK (2 == *s5);
+          
+          CHECK (1 == *s5);    // unaffected of course...
           CHECK (5 == *s7);
           CHECK (8 == *s9);
           
           ++++chain;
           CHECK (4 == *chain);
-          CHECK (4 == *s5);
-          CHECK (5 == *s7);
-          CHECK (8 == *s9);
-          
-          ++chain;
-          CHECK (5 == *chain);
-          CHECK (isnil(s5));
-          CHECK (5 == *s7);
-          CHECK (8 == *s9);
-          
-          ++chain;
-          CHECK (6 == *chain);
-          CHECK (isnil(s5));
-          CHECK (6 == *s7);
-          CHECK (8 == *s9);
-          
-          ++++chain;
-          CHECK (8 == *chain);
-          CHECK (isnil(s5));
-          CHECK (isnil(s7));
-          CHECK (8 == *s9);
-          
           ++chain;
+          CHECK (5 == *chain); // switch over to contents of 2nd iterator
+          ++++++++chain;
           CHECK (9 == *chain);
-          CHECK (isnil(s5));
-          CHECK (isnil(s7));
-          CHECK (9 == *s9);
           
           ++chain;
           CHECK (isnil(chain));
-          CHECK (isnil(s5));
-          CHECK (isnil(s7));
-          CHECK (isnil(s9));
-#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
+          VERIFY_ERROR (ITER_EXHAUST, *chain );
+          VERIFY_ERROR (ITER_EXHAUST, ++chain );
         }
       
       

From 41180eb99cb991107b1dc09cd8e66750e444e44b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 12 Jun 2012 05:22:08 +0200
Subject: [PATCH 473/496] rework to allow for recursive evaluation

this enables expansion of a (functional) data structure
until exhaustion -- which is what we need to
build job functors by traversing and expanding
an arbitrarily nested job definition structure
---
 src/lib/iter-explorer.hpp        | 129 +++++++++++++++++++++++--------
 tests/lib/iter-explorer-test.cpp |  25 +++---
 2 files changed, 107 insertions(+), 47 deletions(-)

diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index 1df055788..569f9050b 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -225,28 +225,17 @@ namespace lib {
           typedef _COM_         Combinator;       // instantiation of the combinator strategy
           typedef IterExplorer ResultsIter;      // result IterExplorer using that instance as state core
           
-          Combinator combinator(explorer);                          // build a new iteration state core
-          if (this->isValid())                                      // if source iterator isn't empty...
-            {
-              combinator.startWith( explorer(accessHeadElement())); // immediately apply the function to the first element
-              combinator.followUp (accessRemainingElements());      // and provide state to allow for continued exploration later
-            }
-          return ResultsIter (combinator);                                    
+          return ResultsIter (
+                  Combinator (explorer                              // build a new iteration state core
+                             ,accessRemainingElements()));          // based on a copy of this iterator / sequence
         }
       
       
       
     private:
-      reference
-      accessHeadElement()
-        {
-          return this->operator* ();
-        }
-      
       IterExplorer const&
       accessRemainingElements()
         {
-          this->operator++ ();
           return *this;
         }
     };
@@ -336,12 +325,6 @@ namespace lib {
         // using standard copy operations
         
         
-        void
-        setResultSequence (ResultIter firstExplorationResult)
-          {
-            results_ = firstExplorationResult;
-          }
-        
         void
         setSourceSequence (SRC const& followUpSourceElements)
           {
@@ -402,21 +385,10 @@ namespace lib {
       public:
         DefaultCombinator() { }
         
-        DefaultCombinator(FUN explorerFunction)
+        DefaultCombinator(FUN explorerFunction, SRC const& sourceElements)
           : CombinedIteratorEvaluation(explorerFunction)
-          { }
-        
-        
-        void
-        startWith (ResultIter firstExplorationResult)
-          {
-            this->setResultSequence (firstExplorationResult);
-          }
-        
-        void
-        followUp (SRC const& followUpSourceElements)
-          {
-            this->setSourceSequence (followUpSourceElements);
+          { 
+            this->setSourceSequence (sourceElements);
           }
       };
     
@@ -499,6 +471,95 @@ namespace lib {
         ChainedIters() { }
       };
     
+    
+    
+    /** 
+     * A "Combinator strategy" allowing to expand and evaluate a
+     * (functional) data structure successively and recursively.
+     * Contrary to the DefaultCombinator, here the explorer is evaluated
+     * repeatedly, feeding back the results until exhaustion. The concrete
+     * exploration function needs to embody some kind of termination condition,
+     * e.g. by returning an empty sequence at some point, otherwise infinite
+     * recursion might happen. Another consequence of this repeated re-evaluation
+     * is the requirement of the source sequence's element type to be compatible
+     * to the result sequence's element type -- we can't \em transform the contents
+     * of the source sequence into another data type, just explore and expand those
+     * contents into sub-sequences based on the same data type.
+     * 
+     * To build a concrete combinator a special strategy template is required to define
+     * the actual implementation logic how to proceed with the evaluation (i.e. how to
+     * find the feed of the "next elements" and how to re-integrate the results of an
+     * evaluation step into the already expanded sequence of intermediary results.
+     * Moreover, these implementation strategy pattern is used as a data buffer
+     * to hold those intermediary results. Together, this allows to create
+     * various expansion patterns, e.g. depth-first or breadth-first.
+     */
+    template class _BUF_
+            >
+    class RecursiveExhaustingEvaluation
+      {
+        typedef typename _Fun::Ret   ResultIter;
+        typedef typename SRC::value_type  Val;
+        typedef typename SRC::reference   reference;
+        typedef function Explorer;
+        typedef _BUF_                Buffer;
+        
+        SRC   srcSequence_;
+        Buffer  resultBuf_;
+        Explorer  explore_;
+        
+        
+      public:
+        RecursiveExhaustingEvaluation() { };
+        
+        RecursiveExhaustingEvaluation (Explorer fun, SRC const& src)
+          : srcSequence_(src)
+          , resultBuf_()
+          , explore_(fun)
+          { }
+        
+        // standard copy operations
+        
+        
+      private:
+        ResultIter &
+        feed()
+          {
+            return resultBuf_.getFeed (srcSequence_);
+          }
+        
+        void
+        iterate ()
+          {
+            ResultIter nextStep = explore_(*feed());
+            ++ feed();
+            resultBuf_.feedBack (nextStep);
+          }
+        
+        
+        /* === Iteration control API for IterStateWrapper== */
+        
+        friend bool
+        checkPoint (RecursiveExhaustingEvaluation const& seq)
+        {
+          return bool(unConst(seq).feed());
+        }
+        
+        friend reference
+        yield (RecursiveExhaustingEvaluation const& seq)
+        {
+          return *(unConst(seq).feed());
+        }
+        
+        friend void
+        iterNext (RecursiveExhaustingEvaluation & seq)
+        {
+          seq.iterate();
+        }
+      };
+    
+    
     /**
      * Helper template to bootstrap a chain of IterExplorers.
      * This is a "state core", which basically just wraps a given
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index ad7aaf01e..150a3c050 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -368,15 +368,14 @@ namespace test{
        * \par Test data structure
        * We build a functional datastructure here, on the fly, while exploring it.
        * The \c exploreChildren(m) function generates this tree like datastructure:
-       * For a given node with number value i, it returns the children (2*i-1) and 2*i
-       * -- but only up to the given maximum value m.
+       * For a given number, it tries to divide by 5, 3 and 2 respectively, possibly
+       * generating multiple decimation sequences.
        * 
-       * If we start such a tree structure with a root node 1, and maximum = 8, this yields:
+       * If we start such a tree structure e.g. with a root node 30, this scheme yields:
        * \code
-       * (       1       )
-       * (   1       2   )
-       * ( 1   2   3   4 )
-       * (1 2 3 4 5 6 7 8)
+       * (       30       )
+       * (   6   10   15  )
+       * ( 2 3  2  5  3 5 )
        * \endcode
        * This tree has no meaning in itself, beyond being an easy testbed for tree exploration schemes.
        * 
@@ -401,10 +400,10 @@ namespace test{
         {
 #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
           typedef DepthFirstExplorer DepthFirst;
-          DepthFirst root (seq(1));
+          DepthFirst root (seq(30));
           
-          string explorationResult = materialise(root >>= exploreChildren(8));
-          CHECK (explorationResult == "1-1-1-1-2-2-3-4-2-3-5-6-4-7-8");
+          string explorationResult = materialise(root >>= exploreChildren);
+          CHECK (explorationResult == "30-6-2-3-10-2-5-15-3-5");
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
         }
       
@@ -421,10 +420,10 @@ namespace test{
         {
 #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
           typedef BreadthFirstExplorer BreadthFirst;
-          BreadthFirst root (seq(1));
+          BreadthFirst root (seq(30));
           
-          string explorationResult = materialise(root >>= exploreChildren(8));
-          CHECK (explorationResult == "1-1-2-1-2-3-4-1-2-3-4-5-6-7-8");
+          string explorationResult = materialise(root >>= exploreChildren);
+          CHECK (explorationResult == "30-6-10-15-2-3-2-5-3-5");
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
         }
       

From 59c5627bbe278850d33253eb8ff557d0b76c6ec9 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 24 Jun 2012 19:28:19 +0200
Subject: [PATCH 474/496] change Implementation class for IterStack

now using a std::deque directly; this allows
for a later extension for queue-like behaviour
---
 src/lib/iter-stack.hpp | 25 +++++++++++++------------
 1 file changed, 13 insertions(+), 12 deletions(-)

diff --git a/src/lib/iter-stack.hpp b/src/lib/iter-stack.hpp
index 607f43bf2..7370947e4 100644
--- a/src/lib/iter-stack.hpp
+++ b/src/lib/iter-stack.hpp
@@ -30,7 +30,7 @@
 #include "lib/iter-adapter.hpp"
 #include "lib/util.hpp"
 
-#include 
+#include 
 
 
 namespace lib {
@@ -44,28 +44,29 @@ namespace lib {
      * as "state core" within lib::IterStateWrapper
      */
     template
-    struct IterStackStorage
-      : std::stack
+    struct IterDequeStorage
+      : std::deque
       {
         /* === Iteration control API for IterStateWrapper == */
         
         friend bool
-        checkPoint (IterStackStorage const& elements)
+        checkPoint (IterDequeStorage const& elements)
         {
           return !elements.empty();
         }
         
         friend TY &
-        yield (IterStackStorage const& elements)
+        yield (IterDequeStorage const& elements)
         {
-          return unConst(elements).top();
+          REQUIRE (!elements.empty());
+          return unConst(elements).back();
         }
         
         friend void
-        iterNext (IterStackStorage & elements)
+        iterNext (IterDequeStorage & elements)
         {
           REQUIRE (!elements.empty());
-          elements.pop();
+          elements.pop_back();
         }
       };
   }//(End) Wrapper/Helper
@@ -85,7 +86,7 @@ namespace lib {
    */
   template
   struct IterStack
-    : IterStateWrapper >
+    : IterStateWrapper >
     {
       
       // using default create and copy operations
@@ -93,15 +94,15 @@ namespace lib {
       void
       push (TY const& elm)
         {
-          this->stateCore().push (elm);
+          this->stateCore().push_back (elm);
         }
       
       TY
       pop()
         {
           this->__throw_if_empty();
-          TY frontElement (this->stateCore().top());
-          this->stateCore().pop();
+          TY frontElement (this->stateCore().back());
+          this->stateCore().pop_back();
           return frontElement;
         }
     };

From 8ced6758fbd95b39618f57e400d6d57723e5cfdf Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 24 Jun 2012 19:30:25 +0200
Subject: [PATCH 475/496] initial draft for recursive evaluating iterator monad

tricky design problem, because nothing is known about
the source and result sequences to be built.
---
 src/lib/iter-explorer.hpp        | 153 +++++++++++++++++++++++++++----
 tests/lib/iter-explorer-test.cpp |  33 ++++++-
 2 files changed, 165 insertions(+), 21 deletions(-)

diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index 569f9050b..d62cbe793 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -68,6 +68,7 @@
 #include "lib/iter-adapter.hpp"
 #include "lib/iter-stack.hpp"
 #include "lib/meta/trait.hpp"
+#include "lib/null-value.hpp"
 #include "lib/util.hpp"
 
 //#include 
@@ -393,7 +394,7 @@ namespace lib {
       };
     
     
-     
+    /** Metafunction to detect an iterator yielding an iterator sequence */
     template
     struct _is_iterator_of_iterators
       {
@@ -473,7 +474,7 @@ namespace lib {
     
     
     
-    /** 
+    /**
      * A "Combinator strategy" allowing to expand and evaluate a
      * (functional) data structure successively and recursively.
      * Contrary to the DefaultCombinator, here the explorer is evaluated
@@ -484,8 +485,12 @@ namespace lib {
      * is the requirement of the source sequence's element type to be compatible
      * to the result sequence's element type -- we can't \em transform the contents
      * of the source sequence into another data type, just explore and expand those
-     * contents into sub-sequences based on the same data type.
+     * contents into sub-sequences based on the same data type. (While this contradicts
+     * the full requirements for building a Monad, we can always work around that kind
+     * of restriction by producing the element type of the target sequence by implicit
+     * type conversion)
      * 
+     * \par strategy requirements
      * To build a concrete combinator a special strategy template is required to define
      * the actual implementation logic how to proceed with the evaluation (i.e. how to
      * find the feed of the "next elements" and how to re-integrate the results of an
@@ -493,6 +498,10 @@ namespace lib {
      * Moreover, these implementation strategy pattern is used as a data buffer
      * to hold those intermediary results. Together, this allows to create
      * various expansion patterns, e.g. depth-first or breadth-first.
+     * - \c Strategy::getFeed() accesses the point from where
+     *   to pull the next element to be expanded. This function must not
+     *   yield an empty sequence, \em unless the overall exploration is exhausted
+     * - \c Strategy::feedBack() re-integrates the results of an expansion step
      */
     template class _BUF_
@@ -501,37 +510,65 @@ namespace lib {
       {
         typedef typename _Fun::Ret   ResultIter;
         typedef typename SRC::value_type  Val;
-        typedef typename SRC::reference   reference;
         typedef function Explorer;
-        typedef _BUF_                Buffer;
+        typedef _BUF_         Buffer;
         
-        SRC   srcSequence_;
         Buffer  resultBuf_;
         Explorer  explore_;
         
         
       public:
-        RecursiveExhaustingEvaluation() { };
+        typedef typename ResultIter::value_type value_type;
+        typedef typename ResultIter::reference reference;
+        typedef typename ResultIter::pointer  pointer;
         
         RecursiveExhaustingEvaluation (Explorer fun, SRC const& src)
-          : srcSequence_(src)
-          , resultBuf_()
+          : resultBuf_()
           , explore_(fun)
-          { }
+          { 
+            resultBuf_.feedBack(
+                initEvaluation (src));
+          }
+        
+        RecursiveExhaustingEvaluation() { };
         
         // standard copy operations
         
         
       private:
-        ResultIter &
-        feed()
+        /** Extension point: build the initial evaluation state
+         * based on the source sequence (typically an IterExplorer).
+         * This is a tricky problem, since the source sequence is not
+         * necessarily assignment compatible to the ResultIter type
+         * and there is no general method to build a ResultIter.
+         * The solution is to rely on a "builder trait", which
+         * needs to be defined alongside with the concrete
+         * ResultIter type. The actual builder trait will
+         * be picked up through a free function (ADL). */
+        ResultIter
+        initEvaluation (SRC const& initialElements)
           {
-            return resultBuf_.getFeed (srcSequence_);
+            ResultIter startSet;
+            return build(startSet).usingSequence(initialElements);
+          }                                   // extension point: free function build (...)
+        
+        
+        /** @note \c _BUF_::getFeed is required to yield a non-empty sequence,
+         * until everything is exhausted. Basically the buffer- and re-integrated
+         * result sequences can be expected to be pulled until finding the next
+         * non-empty supply. This is considered hidden internal state and thus
+         * concealed within this \em const and \em idempotent function.
+         */
+        ResultIter &
+        feed()  const
+          {
+            return unConst(this)->resultBuf_.getFeed();
           }
         
         void
         iterate ()
           {
+            REQUIRE (feed());
             ResultIter nextStep = explore_(*feed());
             ++ feed();
             resultBuf_.feedBack (nextStep);
@@ -543,13 +580,13 @@ namespace lib {
         friend bool
         checkPoint (RecursiveExhaustingEvaluation const& seq)
         {
-          return bool(unConst(seq).feed());
+          return bool(seq.feed());
         }
         
         friend reference
         yield (RecursiveExhaustingEvaluation const& seq)
         {
-          return *(unConst(seq).feed());
+          return *(seq.feed());
         }
         
         friend void
@@ -560,6 +597,70 @@ namespace lib {
       };
     
     
+    /**
+     * Strategy for recursive exhausting \em depth-first evaluation.
+     * Implemented using a heap-allocated stack of partially evaluated iterators.
+     * The next evaluation step will happen at the iterator returned by #getFeed,
+     * which is the most recently pushed intermediary result. 
+     * @warning uses an empty-iterator marker object to signal exhaustion
+     *          - this marker \c IT() may be re-initialised concurrently
+     *          - accessing this marker during app shutdown might access
+     *            an already defunct object 
+     */
+    template
+    class DepthFirstEvaluationBuffer
+      {
+        IterStack intermediaryResults_;
+        
+        
+        /** @return default constructed (=empty) iterator
+         * @remarks casting away const is safe here, since all
+         * you can do with an NIL iterator is to test for emptiness.  
+         */
+        IT &
+        emptySequence()
+          {
+            return unConst(NullValue::get());
+          }                            // unsafe during shutdown 
+        
+        
+      public:
+        IT &
+        getFeed ()
+          {
+            // fast forward to find the next non-empty result sequence
+            while (intermediaryResults_ && ! *intermediaryResults_)
+              ++intermediaryResults_;
+            
+            if (intermediaryResults_)
+              return *intermediaryResults_;
+            else
+              return emptySequence();
+          }
+        
+        void
+        feedBack (IT const& newEvaluationResults)
+          {
+            intermediaryResults_.push (newEvaluationResults);
+          }
+      };
+    
+    
+    /**
+     * preconfigured IterExplorer "state core" resulting in depth-first exhaustive evaluation 
+     */
+    template
+    struct DepthFirstEvaluationConbinator
+      : RecursiveExhaustingEvaluation
+      {
+        DepthFirstEvaluationConbinator() { }
+        
+        DepthFirstEvaluationConbinator(FUN explorerFunction, SRC const& sourceElements)
+          : RecursiveExhaustingEvaluation (explorerFunction,sourceElements)
+          {  }
+      };
+    
+    
     /**
      * Helper template to bootstrap a chain of IterExplorers.
      * This is a "state core", which basically just wraps a given
@@ -603,7 +704,19 @@ namespace lib {
           ++sequence;
         }
       };
-      
+    
+    
+    template
+    struct DepthFirst
+      : IterExplorer, DepthFirstEvaluationConbinator>
+      {
+        DepthFirst() { };
+        DepthFirst(SRC const& srcSeq)
+          : IterExplorer, DepthFirstEvaluationConbinator> (srcSeq)
+          { }
+      };
+    
+    
   }//(End) namespace iter_explorer : predefined policies and configurations
   
   
@@ -617,6 +730,14 @@ namespace lib {
   }
   
   
+  template
+  inline iter_explorer::DepthFirst
+  depthFirst (IT const& srcSeq)
+  {
+    return iter_explorer::DepthFirst (srcSeq);
+  }
+  
+  
   
   template
   inline iter_explorer::ChainedIters
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index 150a3c050..4cf15ae23 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -134,6 +134,31 @@ namespace test{
     NumberSequence NIL_Sequence;
     
     
+
+    struct NumberSequenceBuilder
+      {
+        template
+        NumberSequence
+        usingSequence (IT const& src)
+          {
+            uint justOne = src? *src : 0;
+            return NumberSequence(justOne);
+          }
+      };
+          
+    inline NumberSequenceBuilder
+    build (NumberSequence const&)
+    {
+      return NumberSequenceBuilder();
+    }
+    
+    
+    inline NumberSequence
+    exploreChildren (uint top)
+    {
+      return seq(0,top/2);
+    }
+
     
     /** Diagnostic helper: "squeeze out" the given iterator
      * and join all the elements yielded into a string
@@ -398,14 +423,12 @@ namespace test{
       void
       verifyDepthFirstExploration ()
         {
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
-          typedef DepthFirstExplorer DepthFirst;
-          DepthFirst root (seq(30));
-          
-          string explorationResult = materialise(root >>= exploreChildren);
+          string explorationResult = materialise(depthFirst(seq(4,5)) >>= exploreChildren);
           CHECK (explorationResult == "30-6-2-3-10-2-5-15-3-5");
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
         }
+
       
       
       

From 2c330003468d350cb0cdbe96f4cc17fd50b1f0e3 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 25 Jun 2012 05:00:56 +0200
Subject: [PATCH 476/496] complement IterStack by a similar wrapper for
 queue-like access

---
 src/lib/iter-stack.hpp        | 159 ++++++++++++++++++++++++++++++++--
 tests/40components.tests      |   4 +
 tests/lib/iter-queue-test.cpp | 115 ++++++++++++++++++++++++
 tests/lib/iter-stack-test.cpp |   2 +-
 4 files changed, 272 insertions(+), 8 deletions(-)
 create mode 100644 tests/lib/iter-queue-test.cpp

diff --git a/src/lib/iter-stack.hpp b/src/lib/iter-stack.hpp
index 7370947e4..29f20b8a0 100644
--- a/src/lib/iter-stack.hpp
+++ b/src/lib/iter-stack.hpp
@@ -20,6 +20,28 @@
 
 */
 
+/** @file iter-stack.hpp
+ ** Conveniently iterable stack and queue containers.
+ ** Implemented as thin wrapper on top of std::deque
+ ** In addition to providing a simplified interface, these wrappers comply
+ ** to the "Lumiera Forward Iterator" concept, allowing to retrieve and discharge the
+ ** contents of the container in a standard fashion, especially by using util::for_each,
+ ** or by feeding them into a iterator based pipeline.
+ ** 
+ ** @remarks contrary to the usual (STL) containers, this wrapper \em is an iterator
+ **          and a container at the same time. Which opens the interesting possibility
+ **          to intermix retrieval and feeding of new elements. It can be seen as a
+ **          zero overhead adapter to ease the use within Lumiera's library framework.
+ ** @note    EX_STRONG, since std::deque gives this guarantee for push and pop operations    
+ ** @warning STL containers aren't thread safe. 
+ ** 
+ ** @see IterStack_test
+ ** @see IterQueue_test
+ ** @see iter-adapter.hpp
+ ** @see itertools.hpp
+ ** @see IterSource (completely opaque iterator)
+ **
+ */
 
 
 #ifndef LIB_ITER_STACK_H
@@ -39,9 +61,10 @@ namespace lib {
   namespace { 
     using util::unConst;
     
-    /** 
-     * Wrapper to mark a std::stack instance for use
-     * as "state core" within lib::IterStateWrapper
+    /**
+     * Wrapper to mark a std::deque instance for use
+     * as "state core" within lib::IterStateWrapper.
+     * Iteration will pop elements from the back side.
      */
     template
     struct IterDequeStorage
@@ -75,7 +98,8 @@ namespace lib {
   
   /** 
    * A Stack which can be popped by iterating.
-   * This is a simple helper built on top of std::stack.
+   * This is a simple helper built by wrapping up
+   * STL's double ended queue (deque).
    * Thus, each instance holds the full state, which is
    * actually kept in heap allocated storage. Pushing of
    * new elements and iterator use may be mixed.
@@ -91,22 +115,143 @@ namespace lib {
       
       // using default create and copy operations
       
-      void
+      IterStack&
       push (TY const& elm)
         {
           this->stateCore().push_back (elm);
+          return *this;
         }
       
       TY
       pop()
         {
           this->__throw_if_empty();
-          TY frontElement (this->stateCore().back());
+          TY topElement (this->stateCore().back());
           this->stateCore().pop_back();
-          return frontElement;
+          return topElement;
         }
     };
   
   
+  
+  
+  /** 
+   * A Queue which can be pulled by iterating.
+   * As a variation of the IterStack frontend, here the
+   * exposed additional interface works like a queue:
+   * New elements can be fed, and the iteration will
+   * consume the contents first-in first-out. The
+   * implementation is based on std::deque, providing
+   * both feed and the iterating operation in amortised
+   * constant time, using chunk wise heap allocations.
+   * Feeding of new elements into the queue and
+   * retrieving old elements by iteration 
+   * may be mixed freely.
+   */
+  template
+  struct IterQueue
+    : IterStateWrapper >
+    {
+      
+      // using default create and copy operations
+      
+      IterQueue&
+      feed (TY const& elm)
+        {
+          this->stateCore().push_front (elm);
+          return *this;
+        }
+      
+      TY
+      pop()
+        {
+          this->__throw_if_empty();
+          TY firstElement (this->stateCore().back());
+          this->stateCore().pop_back();
+          return firstElement;
+        }
+      
+      
+      /** 
+       * Adapter for use as opaque sequence.
+       * This builder exposes generic operations
+       * to prepare and pre-fill a sequence
+       */
+      struct Builder
+        {
+          Builder() { }
+          Builder(IterQueue const& initialElements)
+            : queue_(initialElements)
+            { }
+           // standard copy operations allowed
+          
+          template
+          IterQueue
+          usingSequence (IT src)
+            {
+              for ( ; src; ++src )
+                queue_.feed (*src);
+              return queue_;
+            }
+          
+        private:
+          IterQueue queue_;
+        };
+      
+      /** Extension point to be picked up by ADL.
+       *  The exposed Builder object allows client code
+       *  to prepare and pre-fill the queue, without
+       *  being tied to the implementation type
+       * @param initial the initial contents of the queue
+       *        to start with, maybe empty. This parameter
+       *        also acts as type tag to pick this \c build()
+       *        function by ADL 
+       */
+      friend IterQueue::Builder
+      build (IterQueue const& initial)
+      {
+        return Builder(initial);
+      }
+    };
+  
+  
+  /** convenience free function to build an iterable sequence */
+  template
+  inline IterQueue
+  elements (T const& elm)
+  {
+    return IterQueue().feed(elm);
+  }
+  
+  template
+  inline IterQueue
+  elements (T const& e0, T const& e1)
+  {
+    return IterQueue().feed(e0).feed(e1);
+  }
+  
+  template
+  inline IterQueue
+  elements (T const& e0, T const& e1, T const& e2)
+  {
+    return IterQueue().feed(e0).feed(e1).feed(e2);
+  }
+  
+  template
+  inline IterQueue
+  elements (T const& e0, T const& e1, T const& e2, T const& e3)
+  {
+    return IterQueue().feed(e0).feed(e1).feed(e2).feed(e3);
+  }
+  
+  template
+  inline IterQueue
+  elements (T const& e0, T const& e1, T const& e2, T const& e3, T const& e4)
+  {
+    return IterQueue().feed(e0).feed(e1).feed(e2).feed(e3).feed(e4);
+  }
+  
+  
+  
 } // namespace lib
 #endif
diff --git a/tests/40components.tests b/tests/40components.tests
index 3b901d8d3..856084940 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -512,6 +512,10 @@ TEST "Stack-like iterator" IterStack_test
 END
 
 
+TEST "Queue-like iterator" IterQueue_test
+END
+
+
 TEST "Iterable type detection" IterableClassification_test <	 : Yes
 out: can_STL_ForEach	 : Yes
diff --git a/tests/lib/iter-queue-test.cpp b/tests/lib/iter-queue-test.cpp
new file mode 100644
index 000000000..157a8b2c8
--- /dev/null
+++ b/tests/lib/iter-queue-test.cpp
@@ -0,0 +1,115 @@
+/*
+  IterQueue(Test)  -  verify queue-like iterator and builder function
+
+  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.
+
+* *****************************************************/
+
+
+
+#include "lib/test/run.hpp"
+#include "lib/test/test-helper.hpp"
+#include "lib/util.hpp"
+
+#include "lib/iter-stack.hpp"
+
+
+
+namespace lib {
+namespace test{
+  
+  using ::Test;
+  using util::isnil;
+  using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
+  
+  
+  
+  
+  
+  
+  /*********************************************************************
+   * @test cover an easy to use queue, built as convenience wrapper
+   *       on top of \c std::deque and allowing element retrieval
+   *       by iteration.
+   *       - iterable according to the Lumiera Forward Iterator concept
+   *       - can enqueue and dequeue elements repeatedly
+   *       - can be loaded through an generic builder API.
+   * 
+   * @see IterExplorer
+   * @see IterAdapter
+   */
+  class IterQueue_test : public Test
+    {
+      
+      virtual void
+      run (Arg)
+        {
+          IterQueue queue;
+          CHECK (isnil (queue));
+          
+          VERIFY_ERROR (ITER_EXHAUST,  *queue );
+          VERIFY_ERROR (ITER_EXHAUST, ++queue );
+          
+          queue.feed (1);
+          queue.feed (3);
+          queue.feed (5);
+          
+          CHECK (!isnil (queue));
+          CHECK (1 == *queue);
+          
+          ++queue;
+          CHECK (3 == *queue);
+          
+          CHECK (3 == queue.pop());
+          CHECK (5 == *queue);
+          
+          ++queue;
+          CHECK (isnil (queue));
+          VERIFY_ERROR (ITER_EXHAUST,  *queue );
+          VERIFY_ERROR (ITER_EXHAUST, ++queue );
+          VERIFY_ERROR (ITER_EXHAUST, queue.pop() );
+          
+          
+          // use the generic builder API to feed
+          // the contents of another iterator into the queue
+          queue = build(queue).usingSequence (elements (23,45));
+          
+          int i = queue.pop();
+          CHECK (i == 23);
+          CHECK (45 == *queue);
+          
+          // feeding new elements and pulling / iteration can be mixed
+          queue.feed(67);
+          CHECK (45 == *queue);
+          ++queue;
+          CHECK (67 == *queue);
+          ++queue;
+          CHECK (isnil (queue));
+          queue.feed(89);
+          CHECK (89 == *queue);
+          queue.pop();
+          VERIFY_ERROR (ITER_EXHAUST,  *queue );
+        }
+      
+    };
+  
+  
+  LAUNCHER (IterQueue_test, "unit common");
+  
+  
+}} // namespace lib::test
diff --git a/tests/lib/iter-stack-test.cpp b/tests/lib/iter-stack-test.cpp
index 82d93b4b5..9802b84f8 100644
--- a/tests/lib/iter-stack-test.cpp
+++ b/tests/lib/iter-stack-test.cpp
@@ -47,7 +47,7 @@ namespace test{
   
   /*********************************************************************
    * @test cover behaviour of a convenience class built by using a
-   *       std::stack as "state core" of an Lumiera IterStateWrapper.
+   *       \c std::deque as "state core" of an Lumiera IterStateWrapper.
    *       - iterable according to the Lumiera Forward Iterator concept
    *       - can push and pop values repeatedly
    *       - iterating and push / pop can be mixed

From 3d0d59915866dd0c1e0dfb426c60e04017feb177 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 26 Jun 2012 03:16:39 +0200
Subject: [PATCH 477/496] get the depth-first exploration test to work

...using the IterQueue for intermediary results
---
 src/lib/iter-explorer.hpp        |  6 +--
 tests/lib/iter-explorer-test.cpp | 90 ++++++++++++++++----------------
 2 files changed, 49 insertions(+), 47 deletions(-)

diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index d62cbe793..ef2e1456f 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -50,7 +50,7 @@
  ** - and this evaluation needs to be done asynchronously and in parallel (no locking, immutable data)
  ** - and a partial evaluation needs to be stored as continuation (not relying on the stack for partial results) 
  ** 
- ** @see IterExplorer_test.cpp
+ ** @see IterExplorer_test
  ** @see iter-adapter.hpp
  ** @see itertools.hpp
  ** @see IterSource (completely opaque iterator)
@@ -368,8 +368,8 @@ namespace lib {
     
     /**
      * a generic "Combinator strategy" for IterExplorer.
-     * This fallback solution doesn't assume anything beyond the source
-     * and the intermediary result(s) being a Lumiera Forward Iterators.
+     * This default / fallback solution doesn't assume anything beyond the
+     * source and the intermediary result(s) being Lumiera Forward Iterators.
      * @note the implementation stores the functor into a std::function object,
      *       which might cause heap allocations, depending on given function.
      *       Besides, the implementation holds one instance of the (intermediary)
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index 4cf15ae23..e8bcbbeff 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -24,9 +24,8 @@
 
 #include "lib/test/run.hpp"
 #include "lib/test/test-helper.hpp"
-#include "lib/util.hpp"
-//#include "lib/util-foreach.hpp"
 #include "lib/iter-adapter-stl.hpp"
+#include "lib/util.hpp"
 
 #include "lib/iter-explorer.hpp"
 
@@ -43,12 +42,8 @@ namespace lib {
 namespace test{
   
   using ::Test;
-//  using boost::lexical_cast;
-//  using util::for_each;
-//  using util::isnil;
   using util::isnil;
   using util::isSameObject;
-//  using std::vector;
   using std::cout;
   using std::endl;
   using std::string;
@@ -57,12 +52,11 @@ namespace test{
   using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
   
   
-  namespace { // test dummy source iterator
-  
-//    uint NUM_ELMS = 10;
+  namespace { // test material: simple number sequence iterator
+    
     
     /**
-     * This iteration state type describes
+     * This iteration "state core" type describes
      * a sequence of numbers yet to be delivered.
      */
     class State
@@ -98,7 +92,7 @@ namespace test{
     
     
     /** 
-     * A straight number sequence as basic test iterator.
+     * A straight ascending number sequence as basic test iterator.
      * The tests will dress up this source sequence in various ways.
      */
     class NumberSequence
@@ -134,31 +128,30 @@ namespace test{
     NumberSequence NIL_Sequence;
     
     
-
-    struct NumberSequenceBuilder
-      {
-        template
-        NumberSequence
-        usingSequence (IT const& src)
-          {
-            uint justOne = src? *src : 0;
-            return NumberSequence(justOne);
-          }
-      };
-          
-    inline NumberSequenceBuilder
-    build (NumberSequence const&)
-    {
-      return NumberSequenceBuilder();
-    }
+    /** 
+     * an arbitrary series of numbers
+     * @note deliberately this is another type
+     * and not equivalent to a NumberSequence,
+     * while both do share the same value type
+     */
+    typedef IterQueue NumberSeries;
     
     
-    inline NumberSequence
-    exploreChildren (uint top)
+    /** "exploration function" to generate a functional datastructure.
+     * Divide the given number by 5, 3 and 2, if possible. Repeatedly
+     * applying this function yields a tree of decimation sequences,
+     * each leading down to 1
+     */
+    inline NumberSeries
+    exploreChildren (uint node)
     {
-      return seq(0,top/2);
+      NumberSeries results;
+      if (0 == node % 5 && node/5 > 0) results.feed (node/5);
+      if (0 == node % 3 && node/3 > 0) results.feed (node/3);
+      if (0 == node % 2 && node/2 > 0) results.feed (node/2);
+      return results;
     }
-
+    
     
     /** Diagnostic helper: "squeeze out" the given iterator
      * and join all the elements yielded into a string
@@ -331,7 +324,7 @@ namespace test{
       
       /** @test variation of the iterator chaining facility.
        * This is the "raw" version without any convenience shortcuts.
-       * The source iterators are given as iterator yielding other iterators. 
+       * The source iterators are given as iterator yielding other iterators.
        */
       void
       verifyRawChainedIterators ()
@@ -364,7 +357,7 @@ namespace test{
           Chain chain(iti);
           CHECK (!isnil (iti));
           CHECK (1 == *chain);
-
+          
           ++chain;
           CHECK (2 == *chain);
           
@@ -401,6 +394,7 @@ namespace test{
        * (       30       )
        * (   6   10   15  )
        * ( 2 3  2  5  3 5 )
+       * ( 1 1  1  1  1 1 )
        * \endcode
        * This tree has no meaning in itself, beyond being an easy testbed for tree exploration schemes.
        * 
@@ -419,16 +413,25 @@ namespace test{
        * The result of applying this \c >>= operation is a \em transformed version of the source iterator,
        * i.e. it is again an iterator, which yields the results of the exploration function, combined together
        * in the order as defined by the built-in exploration strategy (here: depth first)
+       * 
+       * @note technical detail: the result type of the exploration function (here \c exploreChildren() ) determines
+       *       the iterator type used within IterExplorer and to drive the evaluation. The source sequence used to
+       *       seed the evaluation process actually can be any iterator yielding assignment compatible values: The
+       *       second example uses a NumberSequence with unsigned int values 0..6, while the actual expansion and
+       *       evaluation is based on NumberSeries using signed int values.
        */
       void
       verifyDepthFirstExploration ()
         {
-          string explorationResult = materialise(depthFirst(seq(4,5)) >>= exploreChildren);
-          CHECK (explorationResult == "30-6-2-3-10-2-5-15-3-5");
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
-#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
+          NumberSeries root = elements(30);
+          string explorationResult = materialise (depthFirst(root) >>= exploreChildren);
+          CHECK (explorationResult == "30-6-2-1-3-1-10-2-1-5-1-15-3-1-5-1");
+          
+          NumberSequence to7 = seq(7);
+          explorationResult = materialise (depthFirst(to7) >>= exploreChildren);
+          CHECK (explorationResult == "0-1-2-1-3-1-4-2-1-5-1-6-2-1-3-1");
         }
-
+      
       
       
       
@@ -442,11 +445,9 @@ namespace test{
       verifyBreadthFirstExploration ()
         {
 #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
-          typedef BreadthFirstExplorer BreadthFirst;
-          BreadthFirst root (seq(30));
-          
-          string explorationResult = materialise(root >>= exploreChildren);
-          CHECK (explorationResult == "30-6-10-15-2-3-2-5-3-5");
+          NumberSeries root = elements(30);
+          string explorationResult = materialise (breadthFirst(root) >>= exploreChildren);
+          CHECK (explorationResult == "30-6-10-15-2-3-2-5-3-5-1-1-1-1-1-1");
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
         }
       
@@ -511,6 +512,7 @@ namespace test{
     };
   
   
+  
   LAUNCHER (IterExplorer_test, "unit common");
   
   

From e6a105fbc1195b6950b849b1232714ec6d7f50e3 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 27 Jun 2012 02:50:36 +0200
Subject: [PATCH 478/496] iterator exploring monad finished, passes unit test

---
 src/lib/iter-explorer.hpp        | 124 ++++++++++++++++++++++++++-----
 src/lib/iter-stack.hpp           |  13 ++++
 tests/40components.tests         |  10 +++
 tests/lib/iter-explorer-test.cpp |   5 +-
 4 files changed, 130 insertions(+), 22 deletions(-)

diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index ef2e1456f..6379cf6ea 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -50,6 +50,24 @@
  ** - and this evaluation needs to be done asynchronously and in parallel (no locking, immutable data)
  ** - and a partial evaluation needs to be stored as continuation (not relying on the stack for partial results) 
  ** 
+ ** \par preconfigured solutions
+ ** This header provides some preconfigured applications of this pattern
+ ** - the DefaultCombinator processes the source elements on demand, feeding them through
+ **   the given functor and using the resulting iterator to deliver the result elements
+ ** - Chained iterator uses similar building blocks just to get the "flattening" of
+ **   a sequence of iterators into a single result iterator
+ ** - the RecursiveExhaustingEvaluation is another kind of combination strategy,
+ **   which recursively evaluates the given function and combines the results
+ **   such as to produce classical depth-first and breadth-first search orders.
+ **   
+ ** Alternatively, just the basic IterExplorer template can be used together with a custom
+ ** "combinator strategy" to implement very specific and optimised data structure evaluation patterns.
+ ** This strategy needs to define some way to hold onto the original source elements, feed them through
+ ** the functor on demand and recombine the result sets into a new sequence to be delivered on demand.
+ ** Actually this is what we utilise for the continuous render job generation within the scheduler.
+ ** The preconfigured variants where created as proof-of-concept, to document and verify this
+ ** implementation technique as such.
+ ** 
  ** @see IterExplorer_test
  ** @see iter-adapter.hpp
  ** @see itertools.hpp
@@ -63,7 +81,6 @@
 
 
 #include "lib/error.hpp"
-//#include "lib/bool-checkable.hpp"
 #include "lib/meta/function.hpp"
 #include "lib/iter-adapter.hpp"
 #include "lib/iter-stack.hpp"
@@ -71,7 +88,6 @@
 #include "lib/null-value.hpp"
 #include "lib/util.hpp"
 
-//#include 
 #include 
 #include 
 
@@ -240,10 +256,16 @@ namespace lib {
           return *this;
         }
     };
-      
-      
-    
-    
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
   namespace iter_explorer { ///< predefined policies and configurations
     
     using util::unConst;
@@ -286,6 +308,7 @@ namespace lib {
       };
     
     
+    
     /**
      * Building block: evaluate and combine a sequence of iterators.
      * This implementation helper provides two kinds of "buffers" (actually implemented
@@ -313,7 +336,7 @@ namespace lib {
         typedef typename ResultIter::value_type value_type;
         typedef typename ResultIter::reference  reference;
         typedef typename ResultIter::pointer    pointer;
-  
+        
         
         CombinedIteratorEvaluation() { }
         
@@ -474,6 +497,9 @@ namespace lib {
     
     
     
+    
+    
+    
     /**
      * A "Combinator strategy" allowing to expand and evaluate a
      * (functional) data structure successively and recursively.
@@ -598,24 +624,29 @@ namespace lib {
     
     
     /**
-     * Strategy for recursive exhausting \em depth-first evaluation.
-     * Implemented using a heap-allocated stack of partially evaluated iterators.
-     * The next evaluation step will happen at the iterator returned by #getFeed,
-     * which is the most recently pushed intermediary result. 
+     * Strategy building block for recursive exhausting evaluation.
+     * Allows to create depth-fist or breadth-first evaluation patters, just
+     * by using a suitably intermediary storage container to hold the partially
+     * evaluated iterators created at each evaluation step. Using a stack and
+     * pushing results will create a depth-first pattern, while using a queue
+     * will evaluate in layers (breadth-first). In both cases, the next
+     * evaluation step will happen at the iterator returned by #getFeed. 
      * @warning uses an empty-iterator marker object to signal exhaustion
      *          - this marker \c IT() may be re-initialised concurrently
      *          - accessing this marker during app shutdown might access
      *            an already defunct object 
      */
-    template
-    class DepthFirstEvaluationBuffer
+    template< class IT
+            , template class _QUEUE_   ///< the actual container to use for storage of intermediary results
+            >
+    class EvaluationBufferStrategy
       {
-        IterStack intermediaryResults_;
+        _QUEUE_ intermediaryResults_;
         
         
         /** @return default constructed (=empty) iterator
          * @remarks casting away const is safe here, since all
-         * you can do with an NIL iterator is to test for emptiness.  
+         * you can do with an NIL iterator is to test for emptiness.
          */
         IT &
         emptySequence()
@@ -641,13 +672,35 @@ namespace lib {
         void
         feedBack (IT const& newEvaluationResults)
           {
-            intermediaryResults_.push (newEvaluationResults);
+            intermediaryResults_.insert (newEvaluationResults);
           }
       };
     
     
+    
     /**
-     * preconfigured IterExplorer "state core" resulting in depth-first exhaustive evaluation 
+     * concrete strategy for recursive \em depth-first evaluation.
+     * Using heap allocated storage in a STL Deque (used stack-like)
+     */
+    template
+    struct DepthFirstEvaluationBuffer
+      : EvaluationBufferStrategy
+      { };
+    
+    /**
+     * concrete strategy for recursive \em breadth-first evaluation.
+     * Using heap allocated storage in a STL Deque (used queue-like)
+     */
+    template
+    struct BreadthFirstEvaluationBuffer
+      : EvaluationBufferStrategy
+      { };
+    
+    
+    
+    /**
+     * preconfigured IterExplorer "state core" resulting in
+     * depth-first exhaustive evaluation 
      */
     template
     struct DepthFirstEvaluationConbinator
@@ -660,6 +713,21 @@ namespace lib {
           {  }
       };
     
+    /**
+     * preconfigured IterExplorer "state core" resulting in
+     * breadth-first exhaustive evaluation 
+     */
+    template
+    struct BreadthFirstEvaluationConbinator
+      : RecursiveExhaustingEvaluation
+      {
+        BreadthFirstEvaluationConbinator() { }
+        
+        BreadthFirstEvaluationConbinator(FUN explorerFunction, SRC const& sourceElements)
+          : RecursiveExhaustingEvaluation (explorerFunction,sourceElements)
+          {  }
+      };
+    
     
     /**
      * Helper template to bootstrap a chain of IterExplorers.
@@ -685,7 +753,7 @@ namespace lib {
         
         
         /* === Iteration control API for IterStateWrapper == */
-  
+        
         friend bool
         checkPoint (WrappedSequence const& sequence)
         {
@@ -716,12 +784,24 @@ namespace lib {
           { }
       };
     
+    template
+    struct BreadthFirst
+      : IterExplorer, BreadthFirstEvaluationConbinator>
+      {
+        BreadthFirst() { };
+        BreadthFirst(SRC const& srcSeq)
+          : IterExplorer, BreadthFirstEvaluationConbinator> (srcSeq)
+          { }
+      };
+    
     
   }//(End) namespace iter_explorer : predefined policies and configurations
   
   
   
   
+  /* ==== convenient builder free functions ==== */
+  
   template
   inline IterExplorer >
   exploreIter (IT const& srcSeq)
@@ -738,6 +818,14 @@ namespace lib {
   }
   
   
+  template
+  inline iter_explorer::BreadthFirst
+  breadthFirst (IT const& srcSeq)
+  {
+    return iter_explorer::BreadthFirst (srcSeq);
+  }
+  
+  
   
   template
   inline iter_explorer::ChainedIters
diff --git a/src/lib/iter-stack.hpp b/src/lib/iter-stack.hpp
index 29f20b8a0..35206e91e 100644
--- a/src/lib/iter-stack.hpp
+++ b/src/lib/iter-stack.hpp
@@ -122,6 +122,13 @@ namespace lib {
           return *this;
         }
       
+      IterStack&
+      insert (TY const& elm)
+        {
+          return push(elm);
+        }
+      
+      
       TY
       pop()
         {
@@ -162,6 +169,12 @@ namespace lib {
           return *this;
         }
       
+      IterQueue&
+      insert (TY const& elm)
+        {
+          return feed(elm);
+        }
+      
       TY
       pop()
         {
diff --git a/tests/40components.tests b/tests/40components.tests
index 856084940..cae759715 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -508,11 +508,21 @@ out: values_4_key(::[0-9]{1,2})+
 END
 
 
+TEST "Iterator monad variations" IterExplorer_test
+out: 1-2-3-4-5-6-7-8
+out: 0-1-2-3-4-0-1-2-3-4-5-6-0-1-2-3-4-5-6-7-8
+out: 0-1-2-3-4-5-6-7-8
+out: 0-1-2-3-4-5-6-7-8
+END
+
+
 TEST "Stack-like iterator" IterStack_test
+return: 0
 END
 
 
 TEST "Queue-like iterator" IterQueue_test
+return: 0
 END
 
 
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index e8bcbbeff..60f8ddce3 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -52,8 +52,7 @@ namespace test{
   using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
   
   
-  namespace { // test material: simple number sequence iterator
-    
+  namespace { // test substrate: simple number sequence iterator
     
     /**
      * This iteration "state core" type describes
@@ -444,11 +443,9 @@ namespace test{
       void
       verifyBreadthFirstExploration ()
         {
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
           NumberSeries root = elements(30);
           string explorationResult = materialise (breadthFirst(root) >>= exploreChildren);
           CHECK (explorationResult == "30-6-10-15-2-3-2-5-3-5-1-1-1-1-1-1");
-#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #892
         }
       
       

From 0e3821abebf0f2d5c7e5cc9dd1749a58f42f3d48 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 28 Jun 2012 03:42:48 +0200
Subject: [PATCH 479/496] refatcor function meta programming headers

the template _Fun started as an internal helper
for function-closure, but seems to be of
general use. Thus move it into meta/function.hpp
(function-closure.hpp is heavyweight)
---
 src/lib/iter-explorer.hpp         |  48 ++--------
 src/lib/meta/function-closure.hpp | 151 +-----------------------------
 src/lib/meta/function.hpp         | 151 ++++++++++++++++++++++++++++++
 3 files changed, 161 insertions(+), 189 deletions(-)

diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index 6379cf6ea..50eaa6da4 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -93,54 +93,15 @@
 
 
 namespace lib {
-
   
-  
-  namespace { // internal helpers
-    
-    using std::tr1::function;
-    using meta::FunctionSignature;
-    
-    /**
-     * Helper to dissect an arbitrary function signature,
-     * irrespective if the parameter is given as function reference,
-     * function pointer, member function pointer or functor object.
-     * The base case assumes a (language) function reference.
-     */
-    template
-    struct _Fun
-      {
-        typedef typename FunctionSignature >::Ret Ret;
-        typedef typename FunctionSignature >::Args Args;
-      };
-    /** Specialisation for using a function pointer */
-    template
-    struct _Fun
-      {
-        typedef typename FunctionSignature >::Ret Ret;
-        typedef typename FunctionSignature >::Args Args;
-      };
-    /** Specialisation for passing a functor */
-    template
-    struct _Fun >
-      {
-        typedef typename FunctionSignature >::Ret Ret;
-        typedef typename FunctionSignature >::Args Args;
-      };
-    
-    
-    
-    
-  }
-
   namespace iter_explorer {
     
     template
     class DefaultCombinator;
   }
-
   
-
+  
+  
   /**
    * Adapter for using an Iterator in the way of a Monad
    * This allows to "bind" or "flatMap" a functor, thereby creating
@@ -194,7 +155,7 @@ namespace lib {
    *          an instance of that strategy becomes the new SRC for the created new
    *          IterExplorer. This instantiation of the strategy gets as type parameters
    *          - this IterExplorer's instance type
-   *          - the type of the function bound with \c >>= 
+   *          - the type of the function bound with \c >>=
    */
   template class _COM_ = iter_explorer::DefaultCombinator
@@ -271,6 +232,9 @@ namespace lib {
     using util::unConst;
     using boost::enable_if;
     using boost::disable_if;
+    using std::tr1::function;    
+    using meta::_Fun;
+
     
     /**
      * Building block: evaluating source elements.
diff --git a/src/lib/meta/function-closure.hpp b/src/lib/meta/function-closure.hpp
index 4b75a2f54..081e9160f 100644
--- a/src/lib/meta/function-closure.hpp
+++ b/src/lib/meta/function-closure.hpp
@@ -67,149 +67,6 @@ namespace func{
     
     using tuple::element;
     
-    /**
-     * Helper to dissect an arbitrary function signature,
-     * irrespective if the parameter is given as function reference,
-     * function pointer, member function pointer or functor object.
-     * The base case assumes a (language) function reference.
-     */
-    template
-    struct _Fun
-      {
-        typedef typename FunctionSignature >::Ret Ret;
-        typedef typename FunctionSignature >::Args Args;
-      };
-    /** Specialisation for using a function pointer */
-    template
-    struct _Fun
-      {
-        typedef typename FunctionSignature >::Ret Ret;
-        typedef typename FunctionSignature >::Args Args;
-      };
-    /** Specialisation for passing a functor */
-    template
-    struct _Fun >
-      {
-        typedef typename FunctionSignature >::Ret Ret;
-        typedef typename FunctionSignature >::Args Args;
-      };
-    
-    /** Specialisations for member function pointers */
-    template
-    struct _Fun
-      {
-        typedef RET Ret;
-        typedef Types Args;
-      };
-    
-    template< typename RET, class CLASS
-            , typename A1
-            >
-    struct _Fun
-      {
-        typedef RET Ret;
-        typedef Types Args;
-      };
-    
-    template< typename RET, class CLASS
-            , typename A1
-            , typename A2
-            >
-    struct _Fun
-      {
-        typedef RET Ret;
-        typedef Types Args;
-      };
-    
-    template< typename RET, class CLASS
-            , typename A1
-            , typename A2
-            , typename A3
-            >
-    struct _Fun
-      {
-        typedef RET Ret;
-        typedef Types Args;
-      };
-    
-    template< typename RET, class CLASS
-            , typename A1
-            , typename A2
-            , typename A3
-            , typename A4
-            >
-    struct _Fun
-      {
-        typedef RET Ret;
-        typedef Types Args;
-      };
-    
-    template< typename RET, class CLASS
-            , typename A1
-            , typename A2
-            , typename A3
-            , typename A4
-            , typename A5
-            >
-    struct _Fun
-      {
-        typedef RET Ret;
-        typedef Types Args;
-      };
-    
-    template< typename RET, class CLASS
-            , typename A1
-            , typename A2
-            , typename A3
-            , typename A4
-            , typename A5
-            , typename A6
-            >
-    struct _Fun
-      {
-        typedef RET Ret;
-        typedef Types Args;
-      };
-    
-    template< typename RET, class CLASS
-            , typename A1
-            , typename A2
-            , typename A3
-            , typename A4
-            , typename A5
-            , typename A6
-            , typename A7
-            >
-    struct _Fun
-      {
-        typedef RET Ret;
-        typedef Types Args;
-      };
-    
-    template< typename RET, class CLASS
-            , typename A1
-            , typename A2
-            , typename A3
-            , typename A4
-            , typename A5
-            , typename A6
-            , typename A7
-            , typename A8
-            >
-    struct _Fun
-      {
-        typedef RET Ret;
-        typedef Types Args;
-      };
-    
-    
-    template
-    struct is_Functor                 { static const bool value = false; };
-    template
-    struct is_Functor > { static const bool value = true;  };
-    
-    
-    
     /**
      * this Helper with repetitive specialisations for up to nine arguments
      * is used either to apply a function to arguments given as a tuple, or
@@ -584,8 +441,8 @@ namespace func{
   template
   class FunctionClosure
     {
-      typedef typename func::_Fun::Args Args;
-      typedef typename func::_Fun::Ret  Ret;
+      typedef typename _Fun::Args Args;
+      typedef typename _Fun::Ret  Ret;
       
       function closure_;
       
@@ -790,8 +647,8 @@ namespace func{
   template
   class FunctionComposition
     {
-      typedef typename func::_Fun::Args Args;
-      typedef typename func::_Fun::Ret  Ret1;
+      typedef typename _Fun::Args Args;
+      typedef typename _Fun::Ret  Ret1;
       
       typedef Types ArgsF2;
       typedef typename FunctionTypedef::Sig SigF2;
diff --git a/src/lib/meta/function.hpp b/src/lib/meta/function.hpp
index 543a22b19..7bb4ecffa 100644
--- a/src/lib/meta/function.hpp
+++ b/src/lib/meta/function.hpp
@@ -209,6 +209,157 @@ namespace meta{
   
   
   
+  
+  
+  /**
+   * Helper to dissect an arbitrary function signature,
+   * irrespective if the parameter is given as function reference,
+   * function pointer, member function pointer or functor object.
+   * The base case assumes a (language) function reference.
+   */
+  template
+  struct _Fun
+    {
+      typedef typename FunctionSignature >::Ret Ret;
+      typedef typename FunctionSignature >::Args Args;
+    };
+  /** Specialisation for using a function pointer */
+  template
+  struct _Fun
+    {
+      typedef typename FunctionSignature >::Ret Ret;
+      typedef typename FunctionSignature >::Args Args;
+    };
+  /** Specialisation for passing a functor */
+  template
+  struct _Fun >
+    {
+      typedef typename FunctionSignature >::Ret Ret;
+      typedef typename FunctionSignature >::Args Args;
+    };
+  
+  /** Specialisations for member function pointers */
+  template
+  struct _Fun
+    {
+      typedef RET Ret;
+      typedef Types Args;
+    };
+  
+  template< typename RET, class CLASS
+          , typename A1
+          >
+  struct _Fun
+    {
+      typedef RET Ret;
+      typedef Types Args;
+    };
+  
+  template< typename RET, class CLASS
+          , typename A1
+          , typename A2
+          >
+  struct _Fun
+    {
+      typedef RET Ret;
+      typedef Types Args;
+    };
+  
+  template< typename RET, class CLASS
+          , typename A1
+          , typename A2
+          , typename A3
+          >
+  struct _Fun
+    {
+      typedef RET Ret;
+      typedef Types Args;
+    };
+  
+  template< typename RET, class CLASS
+          , typename A1
+          , typename A2
+          , typename A3
+          , typename A4
+          >
+  struct _Fun
+    {
+      typedef RET Ret;
+      typedef Types Args;
+    };
+  
+  template< typename RET, class CLASS
+          , typename A1
+          , typename A2
+          , typename A3
+          , typename A4
+          , typename A5
+          >
+  struct _Fun
+    {
+      typedef RET Ret;
+      typedef Types Args;
+    };
+  
+  template< typename RET, class CLASS
+          , typename A1
+          , typename A2
+          , typename A3
+          , typename A4
+          , typename A5
+          , typename A6
+          >
+  struct _Fun
+    {
+      typedef RET Ret;
+      typedef Types Args;
+    };
+  
+  template< typename RET, class CLASS
+          , typename A1
+          , typename A2
+          , typename A3
+          , typename A4
+          , typename A5
+          , typename A6
+          , typename A7
+          >
+  struct _Fun
+    {
+      typedef RET Ret;
+      typedef Types Args;
+    };
+  
+  template< typename RET, class CLASS
+          , typename A1
+          , typename A2
+          , typename A3
+          , typename A4
+          , typename A5
+          , typename A6
+          , typename A7
+          , typename A8
+          >
+  struct _Fun
+    {
+      typedef RET Ret;
+      typedef Types Args;
+    };
+  
+  
+  
+  template
+  struct is_Functor                 { static const bool value = false; };
+  template
+  struct is_Functor > { static const bool value = true;  };
+  
+  
+  
+  
+  
+  
+  
+  
   /**
    * Build function types from given Argument types.
    * As embedded typedefs, you'll find a tr1 functor #Func

From e76b85fcfacdbec3dfcfd5dd9981c62dba6cc3ca Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 28 Jun 2012 03:43:44 +0200
Subject: [PATCH 480/496] fix test broken by #410

---
 tests/40components.tests        | 14 +++++++-------
 tests/lib/iter-adapter-test.cpp |  3 ++-
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/tests/40components.tests b/tests/40components.tests
index cae759715..24a262f81 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -508,20 +508,20 @@ out: values_4_key(::[0-9]{1,2})+
 END
 
 
-TEST "Iterator monad variations" IterExplorer_test
-out: 1-2-3-4-5-6-7-8
-out: 0-1-2-3-4-0-1-2-3-4-5-6-0-1-2-3-4-5-6-7-8
-out: 0-1-2-3-4-5-6-7-8
-out: 0-1-2-3-4-5-6-7-8
+TEST "Iterator monad variations" IterExplorer_test <
         friend void
-        iterNext (const TestContainer*, ITER& pos)
+        iterNext (const TestContainer* src, ITER& pos)
           {
             ++pos;
+            checkPoint (src,pos);
           }
         
         /** Implementation of Iteration-logic: detect iteration end.

From 016a739a5c314f6054b49b9c162b07790b5b8230 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 1 Jul 2012 03:42:50 +0200
Subject: [PATCH 481/496] WIP back to the original problem: how to dispatch
 jobs...

brainstorming how to implement the job planning stage

the idea is to built on top of the IterExplorer,
but have the "stack" of re-evaluation integrated
into a custom type, which exploits the static
node network structure to avoid heap allocations

solution idea: again use a builder function?
---
 src/lib/iter-explorer.hpp                     | 121 +++++++++++
 src/lib/iter-stack.hpp                        |   7 +
 src/proc/engine/dispatcher.hpp                |   3 +-
 src/proc/engine/engine-service.hpp            |   6 +-
 src/proc/engine/job-planning.hpp              | 204 ++++++++++++++++++
 src/proc/engine/job-ticket.hpp                |  18 +-
 src/proc/play/timings.cpp                     |   2 +-
 .../proc/engine/dispatcher-interface-test.cpp |  14 +-
 tests/lib/iter-explorer-test.cpp              |  20 ++
 wiki/renderengine.html                        |   6 +-
 10 files changed, 382 insertions(+), 19 deletions(-)
 create mode 100644 src/proc/engine/job-planning.hpp

diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index 50eaa6da4..b12862507 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -170,6 +170,14 @@ namespace lib {
       typedef typename SRC::reference reference;
       typedef typename SRC::pointer  pointer;
       
+      /** Metafunction: the resulting type when binding a functor of type FUN */
+      template
+      struct Binding
+        {
+          typedef IterExplorer<_COM_, _COM_> Type;
+        };
+      
+      
       
       /** by default create an empty iterator */
       IterExplorer() { }
@@ -693,6 +701,119 @@ namespace lib {
       };
     
     
+    
+    
+    /** 
+     * IterExplorer "state core" for progressively expanding
+     * an initial result set. This is a partially reduced and hard-coded
+     * variation on the #RecursiveExhaustingEvaluation in depth-first configuration.
+     * This setup works in conjunction with a special result sequence type,
+     * with the ability to re-integrate results yielded by partial evaluation.
+     * But the working pattern is more similar to the #CombinedIteratorEvaluation,
+     * where the focal point of further expansion is always at the front end of
+     * further items yet to be consumed and expanded; thus the evaluation is
+     * bound to proceed depth-first (i.e. it is \em not possible to "integrate"
+     * any intermediary result with the \em whole result set, only with the part
+     * reachable immediately at the evaluation front)
+     * 
+     * \par usage considerations
+     * There needs to be a specific sequence or iterator type, which is used to
+     * hold the result set(s). This custom type together with the Explorer function
+     * are performing the actual expansion and re-integration steps. The latter is
+     * accessed through the free function \c build(sequence) -- which is expected
+     * to yield a "builder trait" for manipulating the element set yielded by
+     * the custom iterator type returned by the Explorer function.
+     * 
+     * @param SRC the initial result set sequence; this iterator needs to yield
+     *            values of the special ResultIter type, which are then expanded
+     *            until exhaustion by repeated calls to the Explorer function
+     * @param FUN the Explorer function of type ResultIter -> ResultIter
+     * @note the ResultIter type is defined implicitly through the result type
+     *            of the Explorer function. Similarly the result value type
+     *            is defined through the typedef \c ResultIter::value_type
+     */
+    template
+    class RecursiveSelfIntegration
+      {
+          typedef typename _Fun::Ret   ResultIter;
+          typedef typename SRC::value_type  Val;
+          typedef function Explorer;
+          
+          SRC        srcSeq_;
+          ResultIter outSeq_;
+          Explorer  explore_;
+          
+        public:
+          typedef typename ResultIter::value_type value_type;
+          typedef typename ResultIter::reference reference;
+          typedef typename ResultIter::pointer  pointer;
+          
+          RecursiveSelfIntegration (Explorer fun, SRC const& src)
+            : srcSeq_(src)
+            , outSeq_()
+            , explore_(fun)
+            { }
+          
+          RecursiveSelfIntegration() { };
+          
+          // standard copy operations
+          
+          
+        private:
+          /** assure the next elements to be processed
+           * will appear at outSeq_ head. When outSeq_
+           * is still empty after this function,
+           * we're done.
+           * @note \em not calling this function after
+           *       construction, because the typical user
+           *       of this template will do that implicitly
+           *       through invocation of #checkPoint */
+          bool
+          findNextResultElement()
+            {
+              while (!outSeq_ && srcSeq_)
+                {
+                  Val& nextElement(*srcSeq_);
+                  build(outSeq_).wrapping(nextElement);    // extension point: free function build (...).wrapping(...)
+                  ++srcSeq_;
+                }
+              return bool(outSeq_);
+            }
+          
+          void
+          iterate ()
+            {
+              REQUIRE (outSeq_);
+              ResultIter nextStep = explore_(*outSeq_);
+              ++ outSeq_;
+              build(outSeq_).usingSequence(nextStep);      // extension point: free function build (...).usingSequence(...)
+            }
+          
+          
+          /* === Iteration control API for IterStateWrapper== */
+          
+          friend bool
+          checkPoint (RecursiveSelfIntegration const& seq)
+          {
+            return unConst(seq).findNextResultElement();
+          }
+          
+          friend reference
+          yield (RecursiveSelfIntegration const& seq)
+          {
+            return *(seq.outSeq_);
+          }
+          
+          friend void
+          iterNext (RecursiveSelfIntegration & seq)
+          {
+            seq.iterate();
+          }
+      };
+    
+    
+    
+    
     /**
      * Helper template to bootstrap a chain of IterExplorers.
      * This is a "state core", which basically just wraps a given
diff --git a/src/lib/iter-stack.hpp b/src/lib/iter-stack.hpp
index 35206e91e..d4e239e95 100644
--- a/src/lib/iter-stack.hpp
+++ b/src/lib/iter-stack.hpp
@@ -207,6 +207,13 @@ namespace lib {
               return queue_;
             }
           
+          IterQueue
+          wrapping (TY elm)
+            {
+              queue_.feed (elm);
+              return queue_;
+            }
+          
         private:
           IterQueue queue_;
         };
diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp
index e7409be4a..806f49cd4 100644
--- a/src/proc/engine/dispatcher.hpp
+++ b/src/proc/engine/dispatcher.hpp
@@ -30,6 +30,7 @@
 #include "proc/engine/time-anchor.hpp"
 #include "proc/engine/frame-coord.hpp"
 #include "proc/engine/job-ticket.hpp"
+#include "proc/engine/job-planning.hpp"
 #include "lib/time/timevalue.hpp"
 
 #include 
@@ -65,7 +66,7 @@ namespace engine {
           
           JobBuilder& prepareContinuation (function delayedAction);
           
-          operator JobTicket::JobsPlanning()
+          operator JobPlanningSequence()
             {
               UNIMPLEMENTED ("how to represent the closure for defining and scheduling jobs");
               
diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp
index 2de5ff56b..98d775419 100644
--- a/src/proc/engine/engine-service.hpp
+++ b/src/proc/engine/engine-service.hpp
@@ -87,9 +87,9 @@ namespace engine{
    * 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.
+   * backend layer. The implementation of this service is
+   * responsible for creating the right job entries in the
+   * correct order and to enqueue these into the scheduler.
    */
   class EngineService
     : boost::noncopyable
diff --git a/src/proc/engine/job-planning.hpp b/src/proc/engine/job-planning.hpp
new file mode 100644
index 000000000..eed86c6a6
--- /dev/null
+++ b/src/proc/engine/job-planning.hpp
@@ -0,0 +1,204 @@
+/*
+  JOB-PLANNING.hpp  -  steps to prepare and build render jobs
+
+  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.
+
+*/
+
+
+#ifndef PROC_ENGINE_JOB_PLANNING_H
+#define PROC_ENGINE_JOB_PLANNING_H
+
+#include "proc/common.hpp"
+//#include "proc/state.hpp"
+#include "proc/engine/job.hpp"
+#include "proc/engine/job-ticket.hpp"
+//#include "proc/engine/frame-coord.hpp"
+//#include "lib/time/timevalue.hpp"
+//#include "lib/time/timequant.hpp"
+//#include "lib/meta/function.hpp"
+#include "lib/iter-adapter.hpp"
+#include "lib/iter-explorer.hpp"
+//#include "lib/linked-elements.hpp"
+#include "lib/util.hpp"
+
+//#include 
+
+
+namespace proc {
+namespace engine {
+  
+//using lib::time::TimeSpan;
+//using lib::time::Duration;
+//using lib::time::FSecs;
+//using lib::time::Time;
+//using lib::LinkedElements;
+  using util::isnil;
+//  
+//class ExitNode;
+  
+  class JobPlanning;
+
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #827
+  class Evaluation
+    {
+      JobTicket::ExplorationStack focusPoint_;
+      FUN& explore_;
+      
+    public:
+      /** inactive evaluation */
+      Evaluation()
+        {
+          UNIMPLEMENTED ("how to represent an inactive evaluation");
+        }
+      
+      
+      JobPlanning &
+      getFeed ()
+        {
+          while (focusPoint_ && focusPoint_.currentLevelExhausted())
+            focusPoint_.pop();
+            
+          return dressUp(this); /////////////TODO: bad smell
+        }
+      
+      void
+      recurse ()
+        {
+          focusPoint_.visitPrerequisites();
+        }
+
+      
+      /* === Iteration control API for IterStateWrapper== */
+      
+      friend bool
+      checkPoint (Evaluation const& eval)
+      {
+        return bool(seq.feed());
+      }
+      
+      friend JobPlanning&
+      yield (Evaluation const& eval)
+      {
+        return *(seq.feed());
+      }
+      
+      friend void
+      iterNext (Evaluation & eval)
+      {
+        seq.iterate();
+      }      
+    };
+  
+  
+  /** 
+   */ 
+  class JobPlanning
+    : public lib::IterStateWrapper
+    {
+      typedef lib::IterStateWrapper _Iter;
+      
+    public:
+      /** by default create the bottom element of job planning,
+       *  which happens to to plan no job at all. It is represented
+       *  using an inactive state core (default constructed)
+       */
+      JobPlanning()
+        {
+          UNIMPLEMENTED ("create the bottom job planning");
+        }
+      
+      /** further job planning can be initiated by continuing
+       * off a given previous planning state. This is how
+       * the forks are created, expanding into a multitude
+       * of prerequisites of a given job
+       */
+      JobPlanning (Evaluation const& startingPoint)
+        : _Iter(startingPoint)
+        { }
+      
+//      operator Evaluation&()
+//        {
+//          return stateCore();
+//        }
+      
+      operator Job()
+        {
+          UNIMPLEMENTED ("yield a Job descriptor to reflect this planning information");
+        }
+    };
+  
+  
+  /**
+   * Generate a sequence of starting points for Job planning,
+   * based on the underlying frame grid. This sequence will be
+   * used to seed a JobPlanningSequence for generating a chunk
+   * of frame render jobs within a given CalcStream in the player.
+   * Evaluation of that seed will then expand each starting point,
+   * until all prerequisites for those frames are discovered,
+   * resulting in a sequence of Jobs ready to be handed over
+   * to the scheduler for time-bound activation.
+   */
+  class PlanningStepGenerator
+    {
+      /* === Iteration control API for IterStateWrapper== */
+      
+      friend bool
+      checkPoint (PlanningStepGenerator const& gen)
+      {
+        return bool(seq.feed());
+      }
+      
+      friend JobPlanning&
+      yield (PlanningStepGenerator const& gen)
+      {
+        return *(seq.feed());
+      }
+      
+      friend void
+      iterNext (PlanningStepGenerator & gen)
+      {
+        seq.iterate();
+      }      
+    };
+  
+  
+  inline JobPlanning
+  explorePrerequisites(JobPlanning step)
+  {
+    Evaluation planningState_(step);
+    return planningState_.explorePrerequisites();
+  }
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #827
+  
+  
+  
+  /**
+   * @todo 6/12 WIP-WIP-WIP how to prepare jobs for scheduling
+   */
+  class JobPlanningSequence
+    {
+      
+    public:
+      
+    };
+  
+  
+  
+}} // namespace proc::engine
+#endif
diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index 7eb78a715..7369c5918 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -31,7 +31,6 @@
 //#include "lib/time/timevalue.hpp"
 //#include "lib/time/timequant.hpp"
 #include "lib/linked-elements.hpp"
-#include "lib/iter-source.hpp"
 
 #include 
 
@@ -103,18 +102,21 @@ using lib::LinkedElements;
           Prerequisite* next;
         };
       
-      struct Prerequisites
+      struct Prerequisites ///< per channel
         {
           Prerequisites* next;
           LinkedElements preparations_;
         };
       
       
-      LinkedElements channelConfig_;
+      LinkedElements   channelConfig_;
       LinkedElements requirement_;
       
     public:
-      typedef lib::IterSource::iterator  JobsPlanning;
+      
+      class ExplorationStack;
+      
+      friend class ExplorationStack;
       
       JobTicket()
         {
@@ -132,6 +134,14 @@ using lib::LinkedElements;
         }
     };
   
+    class JobTicket::ExplorationStack
+      {
+        JobTicket top_;
+        
+      public:
+        
+      };
+  
   
   
 }} // namespace proc::engine
diff --git a/src/proc/play/timings.cpp b/src/proc/play/timings.cpp
index 5e50878e7..6e57fe6ad 100644
--- a/src/proc/play/timings.cpp
+++ b/src/proc/play/timings.cpp
@@ -99,7 +99,7 @@ namespace play {
   /** @remarks the purpose of this function is to support scheduling
    *           and frame handling even in case the frame rate isn't constant.
    *           To indicate the case the frame rate is changing right now,
-   *           this function might returns Duration::NIL
+   *           this function might return Duration::NIL
    *  @todo implement real support for variable frame rates      
    */                                  ////////////////////////////////////////////////////////TICKET #236
   Duration
diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp
index a26740e15..86d19e1bb 100644
--- a/tests/components/proc/engine/dispatcher-interface-test.cpp
+++ b/tests/components/proc/engine/dispatcher-interface-test.cpp
@@ -179,11 +179,12 @@ namespace test  {
           
           TimeAnchor refPoint = TimeAnchor::build (timings, startFrame);
           
-          JobTicket::JobsPlanning jobs = dispatcher.onCalcStream(modelPort,channel)
-                                                   .establishNextJobs(refPoint);
+          JobPlanningSequence jobs = dispatcher.onCalcStream(modelPort,channel)
+                                               .establishNextJobs(refPoint);
           
           // Verify the planned Jobs
           
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
           CHECK (!isnil (jobs));
           vector plannedChunk;
           lib::append_all (jobs, plannedChunk);
@@ -203,7 +204,6 @@ namespace test  {
               prevInvocationID = thisJob.getInvocationInstanceID();
               nextFrameStart += expectedTimeIncrement;
             }
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
         }
       
@@ -229,10 +229,11 @@ namespace test  {
           function testFunc = verify_invocation_of_Continuation;
           
           TimeAnchor refPoint = TimeAnchor::build (timings, startFrame);
-          JobTicket::JobsPlanning jobs = dispatcher.onCalcStream(modelPort,channel)
-                                                   .establishNextJobs(refPoint)
-                                                   .prepareContinuation(testFunc);
+          JobPlanningSequence jobs = dispatcher.onCalcStream(modelPort,channel)
+                                               .establishNextJobs(refPoint)
+                                               .prepareContinuation(testFunc);
           
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
           // an additional "continuation" Job has been prepared....
           Job continuation = lib::pull_last(jobs);
           CHECK (META_JOB == continuation.getKind());
@@ -247,7 +248,6 @@ namespace test  {
           // Since we passed testFunc as action for the continuation, we expect the invocation
           // of the function verify_invocation_of_Continuation()
           continuation.triggerJob();
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
         }
       
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index 60f8ddce3..51c2d6920 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -234,6 +234,7 @@ namespace test{
           
           verifyDepthFirstExploration();
           verifyBreadthFirstExploration();
+          verifyRecursiveSelfIntegration();
         }
       
       
@@ -450,6 +451,25 @@ namespace test{
       
       
       
+      /** @test a variation of depth-first exploration, this time directly
+       * relying on the result set iterator type to provide the re-integration
+       * of intermediary results.
+       */
+      void
+      verifyRecursiveSelfIntegration ()
+        {
+          typedef NumberSeries Seq;
+          typedef IterExplorer
+                              ,iter_explorer::RecursiveSelfIntegration> SelfIntegratingExploration;
+          
+          Seq root = elements(5,10,20);
+          SelfIntegratingExploration exploration(root);
+          string explorationResult = materialise (exploration >>= exploreChildren);
+          CHECK (explorationResult == "30-6-10-15-2-3-2-5-3-5-1-1-1-1-1-1");
+        }
+      
+      
+      
       /** @test cover the basic monad bind operator,
        * which is used to build all the specialised Iterator flavours.
        * The default implementation ("Combinator strategy") just joins and flattens
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 9d219506c..ca637dc40 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -2138,11 +2138,11 @@ 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 [[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.
 
 !defining the dispatcher interface
-The purpose of this interface is to support the planning of new jobs, for a given CalcStream. From time to time, a chunk of new RenderJob entries will be preapred for the [[Scheduler]]. Each job knows his frame number and the actual ProcNode to operate. So, to start planning jobs, we need to translate time &rarr; frame number &rarr; segment &rarr; real exit node.
+The purpose of this interface is to support the planning of new jobs, for a given CalcStream. From time to time, a chunk of new RenderJob entries will be prepared for the [[Scheduler]]. Each job knows his frame number and the actual ProcNode to operate. So, to start planning jobs, we need to translate time &rarr; frame number &rarr; segment &rarr; real exit node.
 
 !!!Invocation situation
 * our //current starting point//&nbsp; is given as ''time anchor'' closure
@@ -2159,7 +2159,7 @@ The purpose of this interface is to support the planning of new jobs, for a give
 In fact, the whole process of playback or rendering is a continued series of exploration and evaluation. The outline of what needs to be calculated is determined continuously, proceeding in chunks of evaluation. The evaluation structure of the render engine is quite similar to the //fork-join//-pattern, just with the addition of timing constraints. This leads to an invocation pattern, where a partial evaluation happens from time to time. Each of those evaluations establishes a breaking point in time: everything //before// this point is settled and planned thus far. So, this point is an ''anchor'' or closure to root the next partial evaluation. More specifically, this ~TimeAnchor closure is the definitive binding between the abstract logical time of the session timeline, and the real wall-clock time forming the deadline for render evaluation.
 
 !!!related timelines and the frame grid
-The frame dispatch step joins and combines multiple time axes. Through the process of //scheduling,// the output generation is linked to real ''wall clock time'' and the dispatch step establishes the deadlines, taking the ''engine latency'' into account. As such, any render or playback process establishes an ''output time grid'', linking frame numbers to nominal output time or timecode, based on the ''output frame rate'' -- and both the framerate and the actual progression (speed) might be changed dynamically. But beyond all of this there is a third, basically independent temporal structure involved: the actual content to render, the ''effective session timeline''. While encoded in nominal, absolute, internal time values not necessarily grid aligned, in practice at least the //breaking points,// the temporal location where the content or structure of the pipeline changes, are aligned //to a grid used while creating the edit.// Yet this session timing structure need to be related in any way to the playback grid, nor is it necessarily the same as the ''source grid'' defined by the media data used to feed the pipeline.
+The frame dispatch step joins and combines multiple time axes. Through the process of //scheduling,// the output generation is linked to real ''wall clock time'' and the dispatch step establishes the deadlines, taking the ''engine latency'' into account. As such, any render or playback process establishes an ''output time grid'', linking frame numbers to nominal output time or timecode, based on the ''output frame rate'' -- and both the framerate and the actual progression (speed) might be changed dynamically. But beyond all of this there is a third, basically independent temporal structure involved: the actual content to render, the ''effective session timeline''. While encoded in nominal, absolute, internal time values not necessarily grid aligned, in practice at least the //breaking points,// the temporal location where the content or structure of the pipeline changes, are aligned //to a grid used while creating the edit.// Yet this session timing structure need not be related in any way to the playback grid, nor is it necessarily the same as the ''source grid'' defined by the media data used to feed the pipeline.
 
 These complex relationships are reflected in the invocation structure leading to an individual frame job. The [[calculation stream|CalcStream]] provides the [[render/playback timings|Timings]], while the actual implementation of the dispatcher, backed by the [[Fixture]] and thus linked to the session models, gets to relate the effective nominal time, the frame number, the exit node and the //processing function.//
 

From e073d05f5824808a9f6c4e14721c503dd900ba5e Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 12 Jul 2012 03:00:12 +0200
Subject: [PATCH 482/496] new header to define some class partially noncopyable

---
 src/lib/iter-stack.hpp                      |  2 +-
 src/lib/nocopy.hpp                          | 59 +++++++++++++++++++++
 src/proc/mobject/session/query-resolver.hpp | 12 +----
 3 files changed, 62 insertions(+), 11 deletions(-)
 create mode 100644 src/lib/nocopy.hpp

diff --git a/src/lib/iter-stack.hpp b/src/lib/iter-stack.hpp
index d4e239e95..e6fa35015 100644
--- a/src/lib/iter-stack.hpp
+++ b/src/lib/iter-stack.hpp
@@ -50,6 +50,7 @@
 
 #include "lib/error.hpp"
 #include "lib/iter-adapter.hpp"
+#include "lib/nocopy.hpp"
 #include "lib/util.hpp"
 
 #include 
@@ -57,7 +58,6 @@
 
 namespace lib {
   
-  
   namespace { 
     using util::unConst;
     
diff --git a/src/lib/nocopy.hpp b/src/lib/nocopy.hpp
new file mode 100644
index 000000000..3f8fc8b36
--- /dev/null
+++ b/src/lib/nocopy.hpp
@@ -0,0 +1,59 @@
+/*
+  NOCOPY.hpp  -  some flavours of non-copyable entities
+
+  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.
+
+*/
+
+
+#ifndef LIB_NOCOPY_H
+#define LIB_NOCOPY_H
+
+#include 
+
+
+
+namespace util {
+  
+  /**
+   * any copy and copy construction prohibited
+   */
+  class no_copy
+    : boost::noncopyable
+    { };
+  
+  /**
+   * classes inheriting from this mixin
+   * may be created by copy-construction,
+   * but any copy-assignment is prohibited.
+   * @note especially this allows returning
+   *       by-value from a builder function,
+   *       while prohibiting any further copy
+   */
+  class no_copy_by_client
+    {
+     protected:
+       ~no_copy_by_client() {}
+        no_copy_by_client() {}
+        no_copy_by_client (no_copy_by_client const&) {}
+        no_copy_by_client const&
+        operator=(no_copy_by_client const&) { return *this; }
+    };
+  
+} // namespace util
+#endif /*LIB_NOCOPY_H*/
diff --git a/src/proc/mobject/session/query-resolver.hpp b/src/proc/mobject/session/query-resolver.hpp
index c09ee9d9d..43a290549 100644
--- a/src/proc/mobject/session/query-resolver.hpp
+++ b/src/proc/mobject/session/query-resolver.hpp
@@ -29,6 +29,7 @@
 #include "lib/bool-checkable.hpp"
 #include "lib/typed-counter.hpp"
 #include "lib/iter-adapter.hpp"
+#include "lib/nocopy.hpp"
 #include "lib/util.hpp"
 
 #include 
@@ -52,15 +53,6 @@ namespace session {
   using std::tr1::function;
   using std::string;
   
-  class no_copy_by_client
-    {
-     protected:
-       ~no_copy_by_client() {}
-        no_copy_by_client() {}
-        no_copy_by_client (no_copy_by_client const&) {}
-        no_copy_by_client const&
-        operator=(no_copy_by_client const&) { return *this; }
-    };
   
   class Goal;
   class Resolution;
@@ -77,7 +69,7 @@ namespace session {
    * Query ABC
    */
   class Goal
-    : no_copy_by_client
+    : util::no_copy_by_client
     {
     public:
       virtual ~Goal() ;

From 88f433f43391036cfb34b851133e225bb101f9ce Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 12 Jul 2012 03:21:47 +0200
Subject: [PATCH 483/496] successfully implemented another combinator strategy

DOH!
this thime hopefully I've actually succeedd to
created what is actually required in the Dispatcher
---
 src/lib/iter-explorer.hpp        | 19 ++++++++++++-------
 src/lib/iter-stack.hpp           | 22 +++++++++++++++-------
 tests/lib/iter-explorer-test.cpp | 27 +++++++++++++++++++--------
 3 files changed, 46 insertions(+), 22 deletions(-)

diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index b12862507..f95e0fa26 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -59,14 +59,19 @@
  ** - the RecursiveExhaustingEvaluation is another kind of combination strategy,
  **   which recursively evaluates the given function and combines the results
  **   such as to produce classical depth-first and breadth-first search orders.
- **   
+ ** - the more low-level RecursiveSelfIntegration combinator strategy actually
+ **   delegates to the result set iterator implementation to perform the collecting
+ **   and re-integrating of intermediary results. This approach is what we actually
+ **   use in the proc::engine::Dispatcher
+ ** 
  ** Alternatively, just the basic IterExplorer template can be used together with a custom
- ** "combinator strategy" to implement very specific and optimised data structure evaluation patterns.
- ** This strategy needs to define some way to hold onto the original source elements, feed them through
- ** the functor on demand and recombine the result sets into a new sequence to be delivered on demand.
+ ** "combinator strategy" and typically even a specific iterator or sequence to implement very specific
+ ** and optimised data structure evaluation patterns. This strategy needs to define some way to hold onto
+ ** the original source elements, feed them through the functor on demand and recombine the result sets
+ ** into a new sequence to be delivered on demand.
  ** Actually this is what we utilise for the continuous render job generation within the scheduler.
- ** The preconfigured variants where created as proof-of-concept, to document and verify this
- ** implementation technique as such.
+ ** All the other preconfigured variants defined here where created as proof-of-concept, to document
+ ** and verify this implementation technique as such.
  ** 
  ** @see IterExplorer_test
  ** @see iter-adapter.hpp
@@ -153,7 +158,7 @@ namespace lib {
    * @param SRC the source sequence or iterator to wrap
    * @param _COM_ "Combinator" strategy template. When binding (\c >>= ) a function,
    *          an instance of that strategy becomes the new SRC for the created new
-   *          IterExplorer. This instantiation of the strategy gets as type parameters
+   *          IterExplorer. This instantiation of the strategy gets as type parameters:
    *          - this IterExplorer's instance type
    *          - the type of the function bound with \c >>=
    */
diff --git a/src/lib/iter-stack.hpp b/src/lib/iter-stack.hpp
index e6fa35015..b67adce35 100644
--- a/src/lib/iter-stack.hpp
+++ b/src/lib/iter-stack.hpp
@@ -28,6 +28,14 @@
  ** contents of the container in a standard fashion, especially by using util::for_each,
  ** or by feeding them into a iterator based pipeline.
  ** 
+ ** \par the builder extension point
+ ** while Lumiera Forward Iterators only allow to retrieve some elements once,
+ ** the special extension point \c build(sequence) allows abstracted access to some
+ ** limited manipulation of the sequence, without tying the client to a specific
+ ** sequence or iterator implementation. This is intended for library templates,
+ ** which typically can't make any assumptions regarding the iterator types
+ ** used for instantiation.
+ ** 
  ** @remarks contrary to the usual (STL) containers, this wrapper \em is an iterator
  **          and a container at the same time. Which opens the interesting possibility
  **          to intermix retrieval and feeding of new elements. It can be seen as a
@@ -191,15 +199,15 @@ namespace lib {
        * to prepare and pre-fill a sequence
        */
       struct Builder
+        : util::no_copy_by_client
         {
-          Builder() { }
-          Builder(IterQueue const& initialElements)
+          Builder(IterQueue& initialElements)
             : queue_(initialElements)
             { }
            // standard copy operations allowed
           
           template
-          IterQueue
+          IterQueue &
           usingSequence (IT src)
             {
               for ( ; src; ++src )
@@ -207,15 +215,15 @@ namespace lib {
               return queue_;
             }
           
-          IterQueue
-          wrapping (TY elm)
+          IterQueue &
+          wrapping (TY const& elm)
             {
               queue_.feed (elm);
               return queue_;
             }
           
         private:
-          IterQueue queue_;
+          IterQueue& queue_;
         };
       
       /** Extension point to be picked up by ADL.
@@ -228,7 +236,7 @@ namespace lib {
        *        function by ADL 
        */
       friend IterQueue::Builder
-      build (IterQueue const& initial)
+      build (IterQueue& initial)
       {
         return Builder(initial);
       }
diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp
index 51c2d6920..a98ae02e2 100644
--- a/tests/lib/iter-explorer-test.cpp
+++ b/tests/lib/iter-explorer-test.cpp
@@ -203,8 +203,9 @@ namespace test{
    * combination, the goal is to separate and isolate the mechanics
    * of combination, so we can focus on the actual computation steps:
    * The mechanics of combination are embedded into the Monad type,
-   * which acts as a kind of container, holding elements to be processed.
-   * The actual processing steps are then fed to the monad as parameters.
+   * which acts as a kind of container, holding some entities
+   * to be processed. The actual processing steps are then
+   * fed to the monad as "function object" parameters.
    * 
    * Using the monad pattern is well suited when both the mechanics of
    * combination and the individual computation steps tend to be complex.
@@ -451,21 +452,31 @@ namespace test{
       
       
       
-      /** @test a variation of depth-first exploration, this time directly
+      /** @test a variation of recursive exploration, this time directly
        * relying on the result set iterator type to provide the re-integration
-       * of intermediary results.
+       * of intermediary results. Since our \c exploreChildren() function returns
+       * a NumberSeries, which basically is a IterQueue, the re-integration of expanded
+       * elements will happen at the end, resulting in breadth-first visitation order --
+       * but contrary to the dedicated \c breadthFirst(..) explorer, this expansion is done
+       * separately for each element in the initial seed sequence. Note for example how the
+       * expansion series for number 30, which is also generated in
+       * \link #verifyBreadthFirstExploration \endlink, appears here at the end of the
+       * explorationResult sequence
+       * @remarks this "combinator strategy" is really intended for use with custom sequences,
+       *          where the "Explorer" function works together with a specific implementation.
+       *          Actually this is what we use in the proc::engine::Dispatcher to generate a
+       *          series of frame render jobs, including all prerequisite jobs
        */
       void
       verifyRecursiveSelfIntegration ()
         {
-          typedef NumberSeries Seq;
-          typedef IterExplorer
+          typedef IterExplorer
                               ,iter_explorer::RecursiveSelfIntegration> SelfIntegratingExploration;
           
-          Seq root = elements(5,10,20);
+          NumberSeries root = elements(10,20,30);
           SelfIntegratingExploration exploration(root);
           string explorationResult = materialise (exploration >>= exploreChildren);
-          CHECK (explorationResult == "30-6-10-15-2-3-2-5-3-5-1-1-1-1-1-1");
+          CHECK (explorationResult == "10-2-5-1-1-20-4-10-2-2-5-1-1-1-30-6-10-15-2-3-2-5-3-5-1-1-1-1-1-1");
         }
       
       

From a3bc69cc77341bc5aaa1938a8df665420c55609b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 21 Jul 2012 20:27:52 +0200
Subject: [PATCH 484/496] maybe a breakthrough on the job planning design
 quest?

why the hell is getting this design about right
such a chellenging task. Anyway, seems to be the
first step ahead after numerous failed attempts
---
 src/proc/engine/job-planning.hpp | 204 ++++++++++++++++++++-----------
 src/proc/engine/job-ticket.hpp   |  40 +++++-
 2 files changed, 172 insertions(+), 72 deletions(-)

diff --git a/src/proc/engine/job-planning.hpp b/src/proc/engine/job-planning.hpp
index eed86c6a6..e7b023b51 100644
--- a/src/proc/engine/job-planning.hpp
+++ b/src/proc/engine/job-planning.hpp
@@ -43,75 +43,25 @@
 namespace proc {
 namespace engine {
   
+  namespace error = lumiera::error;
+  
 //using lib::time::TimeSpan;
 //using lib::time::Duration;
 //using lib::time::FSecs;
 //using lib::time::Time;
 //using lib::LinkedElements;
   using util::isnil;
+  using util::unConst;
 //  
 //class ExitNode;
   
-  class JobPlanning;
 
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #827
-  class Evaluation
-    {
-      JobTicket::ExplorationStack focusPoint_;
-      FUN& explore_;
-      
-    public:
-      /** inactive evaluation */
-      Evaluation()
-        {
-          UNIMPLEMENTED ("how to represent an inactive evaluation");
-        }
-      
-      
-      JobPlanning &
-      getFeed ()
-        {
-          while (focusPoint_ && focusPoint_.currentLevelExhausted())
-            focusPoint_.pop();
-            
-          return dressUp(this); /////////////TODO: bad smell
-        }
-      
-      void
-      recurse ()
-        {
-          focusPoint_.visitPrerequisites();
-        }
-
-      
-      /* === Iteration control API for IterStateWrapper== */
-      
-      friend bool
-      checkPoint (Evaluation const& eval)
-      {
-        return bool(seq.feed());
-      }
-      
-      friend JobPlanning&
-      yield (Evaluation const& eval)
-      {
-        return *(seq.feed());
-      }
-      
-      friend void
-      iterNext (Evaluation & eval)
-      {
-        seq.iterate();
-      }      
-    };
-  
-  
   /** 
    */ 
   class JobPlanning
-    : public lib::IterStateWrapper
     {
-      typedef lib::IterStateWrapper _Iter;
+      JobTicket::iterator plannedOperations_;
+      FrameCoord point_to_calculate_;
       
     public:
       /** by default create the bottom element of job planning,
@@ -119,31 +69,149 @@ namespace engine {
        *  using an inactive state core (default constructed)
        */
       JobPlanning()
-        {
-          UNIMPLEMENTED ("create the bottom job planning");
-        }
+        { }
       
       /** further job planning can be initiated by continuing
        * off a given previous planning state. This is how
        * the forks are created, expanding into a multitude
        * of prerequisites of a given job
        */
-      JobPlanning (Evaluation const& startingPoint)
-        : _Iter(startingPoint)
+      JobPlanning (JobTicket::iterator const& startingPoint, FrameCoord requestedFrame)
+        : plannedOperations_(startingPoint)
+        , point_to_calculate_(requestedFrame)
         { }
       
-//      operator Evaluation&()
-//        {
-//          return stateCore();
-//        }
+      // using the standard copy operations 
       
+      
+      /** cast and explicate this job planning information
+       *  to create a frame job descriptor, ready to be scheduled
+       */
       operator Job()
         {
-          UNIMPLEMENTED ("yield a Job descriptor to reflect this planning information");
+          if (isnil (plannedOperations_))
+            throw error::Logic("Attempt to plan a frame-Job based on a missing, "
+                               "unspecified, exhausted or superseded job description"
+                              ,error::LUMIERA_ERROR_BOTTOM_VALUE);
+          
+          return plannedOperations_->createJobFor (point_to_calculate_);
         }
+      
+      
+      /** build a new JobPlanning object,
+       * set to explore the prerequisites 
+       * at the given planning situation
+       */
+      JobPlanning
+      discoverPrerequisites()  const
+        {
+          if (isnil (plannedOperations_))
+            return JobPlanning();
+          else
+            return JobPlanning (plannedOperations_->discoverPrerequisites()
+                               ,this->point_to_calculate_);
+        }
+      
+      
+      /* === Iteration control API for IterStateWrapper== */
+      
+      friend bool
+      checkPoint (JobPlanning const& plan)
+      {
+        return !isnil (plan.plannedOperations_);
+      }
+      
+      friend JobPlanning&
+      yield (JobPlanning const& plan)
+      {
+        REQUIRE (checkPoint (plan));
+        return unConst(plan);
+      }
+      
+      friend void
+      iterNext (JobPlanning & plan)
+      {
+        ++plan.plannedOperations_;
+      }      
     };
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #827
+  
+    
+  class PlanningState
+    : public lib::IterStateWrapper
+    {
+      typedef lib::IterStateWrapper _Iter;
+      
+      JobTicket::ExplorationStack explorationStack_;
+      
+    public:
+      /** inactive evaluation */
+      PlanningState()
+        : _Iter()
+        , explorationStack_()
+        { }
+      
+      explicit
+      PlanningState (JobPlanning const& startingPoint)
+        : _Iter(startingPoint)   // note: invoking copy ctor on state core
+        , explorationStack_()
+        { }
+      
+      // using the standard copy operations 
+      
+      
+      
+      /** attach and integrate the given planning details into this planning state.
+       *  Actually the evaluation proceeds depth-first with the other state, returning
+       *  later on to the current position for further evaluation */
+      PlanningState &
+      wrapping (JobPlanning const& startingPoint)
+        {
+          explorationStack_.push (stateCore());
+          stateCore() = startingPoint;
+          return *this;
+        }
+
+      PlanningState &
+      usingSequence (PlanningState const& prerequisites)
+        {
+          if (isnil (prerequisites))
+            return *this;
+          else
+            return this->wrapping(*prerequisites);
+        }
+      
+      ///TODO what elements are accepted by the explorationStack? provide conversion operator to extract those from stateCore()
+      
+      
+      
+      /** Extension point to be picked up by ADL.
+       *  Provides access for the JobPlanningSequence
+       *  for combining and expanding partial results. 
+       */
+      friend PlanningState&
+      build (PlanningState& attachmentPoint)
+      {
+        return attachmentPoint;
+      }
+
+
+    };
+    
   
   
+  inline PlanningState
+  expandPrerequisites (JobPlanning const& calculationStep)
+  {
+    PlanningState newSubEvaluation(
+                    calculationStep.discoverPrerequisites());
+    return newSubEvaluation;
+  }
+  
+  
+
+    
+    
   /**
    * Generate a sequence of starting points for Job planning,
    * based on the underlying frame grid. This sequence will be
@@ -178,12 +246,6 @@ namespace engine {
     };
   
   
-  inline JobPlanning
-  explorePrerequisites(JobPlanning step)
-  {
-    Evaluation planningState_(step);
-    return planningState_.explorePrerequisites();
-  }
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #827
   
   
diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index 7369c5918..12f74c5d6 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -31,6 +31,7 @@
 //#include "lib/time/timevalue.hpp"
 //#include "lib/time/timequant.hpp"
 #include "lib/linked-elements.hpp"
+#include "lib/iter-adapter.hpp"
 
 #include 
 
@@ -105,10 +106,38 @@ using lib::LinkedElements;
       struct Prerequisites ///< per channel
         {
           Prerequisites* next;
-          LinkedElements preparations_;
+          LinkedElements requiredJobs_;
         };
       
       
+      struct PrerequisitesDiscovery
+        {
+          /////////////TODO
+          
+          /* === Iteration control API for IterStateWrapper== */
+      
+          friend bool
+          checkPoint (PrerequisitesDiscovery const& plan)
+          {
+            UNIMPLEMENTED ("detect end of prerequisites");
+          }
+          
+          friend JobTicket&
+          yield (PrerequisitesDiscovery const& plan)
+          {
+            UNIMPLEMENTED ("access prerequisite and follow up to prerequisite JobTicket");
+          }
+          
+          friend void
+          iterNext (PrerequisitesDiscovery & plan)
+          {
+            UNIMPLEMENTED ("step ahead to next prerequisite");
+          }      
+          
+        };
+      
+      
+      
       LinkedElements   channelConfig_;
       LinkedElements requirement_;
       
@@ -117,6 +146,9 @@ using lib::LinkedElements;
       class ExplorationStack;
       
       friend class ExplorationStack;
+
+      typedef lib::IterStateWrapper iterator;
+      
       
       JobTicket()
         {
@@ -124,6 +156,12 @@ using lib::LinkedElements;
         }
       
       
+      iterator
+      discoverPrerequisites()  const
+        {
+          UNIMPLEMENTED("delve into the requirement_ datastructure");
+        }
+      
       Job createJobFor (FrameCoord coordinates);
       
       

From 66defdc0cbdef5b8b772df328174f18df4bc73d1 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 22 Jul 2012 03:11:01 +0200
Subject: [PATCH 485/496] refactor JobPlanning to encapsulate all state
 mechanics into a "state core"

especially the exploration stack is pushed down
first successful definition of all the JobPlanning classes

just the framework of classes necessary to pass the compiler;
all implementation is still stubbed
---
 src/lib/iter-explorer.hpp        |   4 +-
 src/proc/engine/job-planning.hpp |  52 ++++++++++-----
 src/proc/engine/job-ticket.hpp   | 107 ++++++++++++++++++-------------
 3 files changed, 100 insertions(+), 63 deletions(-)

diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index f95e0fa26..f89a9e992 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -789,9 +789,9 @@ namespace lib {
           iterate ()
             {
               REQUIRE (outSeq_);
-              ResultIter nextStep = explore_(*outSeq_);
+              ResultIter nextSteps = explore_(*outSeq_);
               ++ outSeq_;
-              build(outSeq_).usingSequence(nextStep);      // extension point: free function build (...).usingSequence(...)
+              build(outSeq_).usingSequence(nextSteps);     // extension point: free function build (...).usingSequence(...)
             }
           
           
diff --git a/src/proc/engine/job-planning.hpp b/src/proc/engine/job-planning.hpp
index e7b023b51..e4f6203ed 100644
--- a/src/proc/engine/job-planning.hpp
+++ b/src/proc/engine/job-planning.hpp
@@ -62,7 +62,7 @@ namespace engine {
     {
       JobTicket::iterator plannedOperations_;
       FrameCoord point_to_calculate_;
-      
+     
     public:
       /** by default create the bottom element of job planning,
        *  which happens to to plan no job at all. It is represented
@@ -112,6 +112,12 @@ namespace engine {
                                ,this->point_to_calculate_);
         }
       
+      friend void
+      integrate (JobPlanning const& newStartingPoint, JobPlanning existingPlan)
+      {
+        existingPlan.plannedOperations_.push (newStartingPoint.plannedOperations_);
+      }
+      
       
       /* === Iteration control API for IterStateWrapper== */
       
@@ -131,10 +137,12 @@ namespace engine {
       friend void
       iterNext (JobPlanning & plan)
       {
-        ++plan.plannedOperations_;
+        ++(plan.plannedOperations_);
       }      
     };
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #827
+  
+  
+  
   
     
   class PlanningState
@@ -142,33 +150,32 @@ namespace engine {
     {
       typedef lib::IterStateWrapper _Iter;
       
-      JobTicket::ExplorationStack explorationStack_;
       
     public:
       /** inactive evaluation */
       PlanningState()
         : _Iter()
-        , explorationStack_()
         { }
       
       explicit
       PlanningState (JobPlanning const& startingPoint)
         : _Iter(startingPoint)   // note: invoking copy ctor on state core
-        , explorationStack_()
         { }
       
       // using the standard copy operations 
       
       
       
+      
+      /* === API for JobPlanningSequence to expand the tree of prerequisites === */
+      
       /** attach and integrate the given planning details into this planning state.
-       *  Actually the evaluation proceeds depth-first with the other state, returning
-       *  later on to the current position for further evaluation */
+       *  Actually the evaluation proceeds depth-first with the other state,
+       *  returning later on to the current position for further evaluation */
       PlanningState &
       wrapping (JobPlanning const& startingPoint)
         {
-          explorationStack_.push (stateCore());
-          stateCore() = startingPoint;
+          integrate (startingPoint, this->stateCore());
           return *this;
         }
 
@@ -179,6 +186,10 @@ namespace engine {
             return *this;
           else
             return this->wrapping(*prerequisites);
+               //  explanation: PlanningState represents a sequence of successive planning points.
+              //                actually this is implemented by switching an embedded JobPlanning element
+             //                 through a sequence of states. Thus the initial state of the investigation
+            //                  (which is a JobPlanning) can stand-in for the sequence of prerequisites
         }
       
       ///TODO what elements are accepted by the explorationStack? provide conversion operator to extract those from stateCore()
@@ -194,17 +205,15 @@ namespace engine {
       {
         return attachmentPoint;
       }
-
-
     };
     
   
   
   inline PlanningState
-  expandPrerequisites (JobPlanning const& calculationStep)
+  expandPrerequisites (PlanningState const& calculationStep)
   {
     PlanningState newSubEvaluation(
-                    calculationStep.discoverPrerequisites());
+                    calculationStep->discoverPrerequisites());
     return newSubEvaluation;
   }
   
@@ -224,28 +233,35 @@ namespace engine {
    */
   class PlanningStepGenerator
     {
+    public:
+      typedef JobPlanning value_type;
+      typedef JobPlanning& reference;
+      typedef JobPlanning *  pointer;
+      
+      
       /* === Iteration control API for IterStateWrapper== */
       
       friend bool
       checkPoint (PlanningStepGenerator const& gen)
       {
-        return bool(seq.feed());
+        UNIMPLEMENTED ("determine planing chunk size"); /// return bool(seq.feed());
       }
       
       friend JobPlanning&
       yield (PlanningStepGenerator const& gen)
       {
-        return *(seq.feed());
+        UNIMPLEMENTED ("generate a single frame job planning tree");  ///return *(seq.feed());
       }
       
       friend void
       iterNext (PlanningStepGenerator & gen)
       {
-        seq.iterate();
+        UNIMPLEMENTED ("proceed to next frame");  ///////seq.iterate();
       }      
     };
   
   
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #827
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #827
   
   
@@ -254,6 +270,8 @@ namespace engine {
    * @todo 6/12 WIP-WIP-WIP how to prepare jobs for scheduling
    */
   class JobPlanningSequence
+    : public lib::IterExplorer
     {
       
     public:
diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index 12f74c5d6..6a13df71f 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -34,6 +34,7 @@
 #include "lib/iter-adapter.hpp"
 
 #include 
+#include 
 
 
 namespace proc {
@@ -101,6 +102,7 @@ using lib::LinkedElements;
       struct Prerequisite
         {
           Prerequisite* next;
+          JobTicket* descriptor;
         };
       
       struct Prerequisites ///< per channel
@@ -110,44 +112,19 @@ using lib::LinkedElements;
         };
       
       
-      struct PrerequisitesDiscovery
-        {
-          /////////////TODO
-          
-          /* === Iteration control API for IterStateWrapper== */
-      
-          friend bool
-          checkPoint (PrerequisitesDiscovery const& plan)
-          {
-            UNIMPLEMENTED ("detect end of prerequisites");
-          }
-          
-          friend JobTicket&
-          yield (PrerequisitesDiscovery const& plan)
-          {
-            UNIMPLEMENTED ("access prerequisite and follow up to prerequisite JobTicket");
-          }
-          
-          friend void
-          iterNext (PrerequisitesDiscovery & plan)
-          {
-            UNIMPLEMENTED ("step ahead to next prerequisite");
-          }      
-          
-        };
-      
-      
       
       LinkedElements   channelConfig_;
       LinkedElements requirement_;
       
+      
+      class ExplorationState;
+      friend class ExplorationState;
+      
+      struct TraversalOfPrerequisites;
+        
     public:
-      
-      class ExplorationStack;
-      
-      friend class ExplorationStack;
-
-      typedef lib::IterStateWrapper iterator;
+        
+      typedef TraversalOfPrerequisites iterator;
       
       
       JobTicket()
@@ -156,11 +133,7 @@ using lib::LinkedElements;
         }
       
       
-      iterator
-      discoverPrerequisites()  const
-        {
-          UNIMPLEMENTED("delve into the requirement_ datastructure");
-        }
+      iterator discoverPrerequisites()  const;
       
       Job createJobFor (FrameCoord coordinates);
       
@@ -172,13 +145,59 @@ using lib::LinkedElements;
         }
     };
   
-    class JobTicket::ExplorationStack
+  
+  class JobTicket::ExplorationState
+    {
+      typedef LinkedElements::iterator SubTicketSeq;
+      typedef std::stack SubTicketStack;               //////////////////////////TODO use a custom container to avoid heap allocations
+      
+      SubTicketStack toExplore_;
+      
+    public:
+      // using default ctor and copy operations
+      
+      
+      
+      /* === Iteration control API for IterStateWrapper== */
+  
+      friend bool
+      checkPoint (ExplorationState const& plan)
       {
-        JobTicket top_;
-        
-      public:
-        
-      };
+        UNIMPLEMENTED ("detect end of prerequisites");
+      }
+      
+      friend JobTicket&
+      yield (ExplorationState const& plan)
+      {
+        UNIMPLEMENTED ("access prerequisite and follow up to prerequisite JobTicket");
+      }
+      
+      friend void
+      iterNext (ExplorationState & plan)
+      {
+        UNIMPLEMENTED ("step ahead to next prerequisite");
+      }      
+    };
+    
+  
+  struct JobTicket::TraversalOfPrerequisites
+    : lib::IterStateWrapper
+    {
+      void
+      push (TraversalOfPrerequisites subExploration)
+        {
+          UNIMPLEMENTED ("integrate a branch of prerequisites");
+        }
+      
+    };
+  
+  
+  inline JobTicket::iterator
+  JobTicket::discoverPrerequisites()  const
+  {
+    UNIMPLEMENTED("delve into the requirement_ datastructure");
+  }
+
   
   
   

From b52a1a8366325f538cc39caf5163c008cc19ced6 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 22 Aug 2012 10:43:55 +0200
Subject: [PATCH 486/496] refactor JobTicket to remove the double layering

basically we had two lines of doubly nested capsules, due to
using the IterAdaptor template. Actually, the evaluation stack
within JobTicket can be considered an implementation detail and
thus doesn't require an iterator interface; the intention is to
use this through JobPlanning solely.

Thus this reworking removes the special iterator within JobTicket,
but retains the idea of exposing the "current" JobTicket through
a smart pointer or  operator->()

work done during the FrOSCon travel
---
 src/proc/engine/job-planning.hpp | 20 +++++------
 src/proc/engine/job-ticket.hpp   | 58 ++++++++++++++------------------
 2 files changed, 35 insertions(+), 43 deletions(-)

diff --git a/src/proc/engine/job-planning.hpp b/src/proc/engine/job-planning.hpp
index e4f6203ed..dbdcc8c52 100644
--- a/src/proc/engine/job-planning.hpp
+++ b/src/proc/engine/job-planning.hpp
@@ -60,8 +60,8 @@ namespace engine {
    */ 
   class JobPlanning
     {
-      JobTicket::iterator plannedOperations_;
-      FrameCoord point_to_calculate_;
+      JobTicket::ExplorationState plannedOperations_;
+      FrameCoord                 point_to_calculate_;
      
     public:
       /** by default create the bottom element of job planning,
@@ -71,12 +71,11 @@ namespace engine {
       JobPlanning()
         { }
       
-      /** further job planning can be initiated by continuing
-       * off a given previous planning state. This is how
-       * the forks are created, expanding into a multitude
-       * of prerequisites of a given job
+      /** further job planning can be initiated by continuing off a given previous planning state.
+       *  This is how the forks are created, expanding into a multitude of prerequisites for
+       *  the job in question.
        */
-      JobPlanning (JobTicket::iterator const& startingPoint, FrameCoord requestedFrame)
+      JobPlanning (JobTicket::ExplorationState const& startingPoint, FrameCoord requestedFrame)
         : plannedOperations_(startingPoint)
         , point_to_calculate_(requestedFrame)
         { }
@@ -112,6 +111,9 @@ namespace engine {
                                ,this->point_to_calculate_);
         }
       
+      /** integrate another chain of prerequisites into the current evaluation line.
+       *  Further evaluation will start to visit prerequisites from the new starting point,
+       *  and return to the current evaluation chain later on exhaustion of the side chain. */
       friend void
       integrate (JobPlanning const& newStartingPoint, JobPlanning existingPlan)
       {
@@ -137,7 +139,7 @@ namespace engine {
       friend void
       iterNext (JobPlanning & plan)
       {
-        ++(plan.plannedOperations_);
+        plan.plannedOperations_.pullNext();
       }      
     };
   
@@ -192,8 +194,6 @@ namespace engine {
             //                  (which is a JobPlanning) can stand-in for the sequence of prerequisites
         }
       
-      ///TODO what elements are accepted by the explorationStack? provide conversion operator to extract those from stateCore()
-      
       
       
       /** Extension point to be picked up by ADL.
diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index 6a13df71f..56148907d 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -117,15 +117,10 @@ using lib::LinkedElements;
       LinkedElements requirement_;
       
       
+    public:
       class ExplorationState;
       friend class ExplorationState;
       
-      struct TraversalOfPrerequisites;
-        
-    public:
-        
-      typedef TraversalOfPrerequisites iterator;
-      
       
       JobTicket()
         {
@@ -133,7 +128,7 @@ using lib::LinkedElements;
         }
       
       
-      iterator discoverPrerequisites()  const;
+      ExplorationState discoverPrerequisites()  const;
       
       Job createJobFor (FrameCoord coordinates);
       
@@ -157,42 +152,39 @@ using lib::LinkedElements;
       // using default ctor and copy operations
       
       
+      bool
+      empty()  const
+        {
+          UNIMPLEMENTED ("detect empty/no prerequisites");
+        }
       
-      /* === Iteration control API for IterStateWrapper== */
-  
-      friend bool
-      checkPoint (ExplorationState const& plan)
-      {
-        UNIMPLEMENTED ("detect end of prerequisites");
-      }
       
-      friend JobTicket&
-      yield (ExplorationState const& plan)
-      {
-        UNIMPLEMENTED ("access prerequisite and follow up to prerequisite JobTicket");
-      }
-      
-      friend void
-      iterNext (ExplorationState & plan)
-      {
-        UNIMPLEMENTED ("step ahead to next prerequisite");
-      }      
-    };
-    
-  
-  struct JobTicket::TraversalOfPrerequisites
-    : lib::IterStateWrapper
-    {
       void
-      push (TraversalOfPrerequisites subExploration)
+      pullNext()
+        {
+          UNIMPLEMENTED ("step ahead to next prerequisite, maybe pop");
+        }
+      
+      void
+      push (ExplorationState subExploration)
         {
           UNIMPLEMENTED ("integrate a branch of prerequisites");
         }
       
+      
+      JobTicket*
+      operator->() const
+        {
+          UNIMPLEMENTED ("yield the current job ticket");
+          // access prerequisite and follow up to prerequisite JobTicket
+        }
+      
     };
+    
   
   
-  inline JobTicket::iterator
+  
+  inline JobTicket::ExplorationState
   JobTicket::discoverPrerequisites()  const
   {
     UNIMPLEMENTED("delve into the requirement_ datastructure");

From 062e1bbdc1fb8ad3d1c62d98596bf870b88f84b7 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 1 Sep 2012 17:33:42 +0200
Subject: [PATCH 487/496] explicate the JobPlanningSequence definition

..causing the compiler to instantiate the
involved templates, i.e. get the complete
pipeline definition through the compiler
---
 src/lib/iter-explorer.hpp        | 10 +++++----
 src/lib/meta/function.hpp        |  7 +++++++
 src/proc/engine/job-planning.hpp | 35 +++++++++++++++++++++++++-------
 wiki/renderengine.html           |  8 ++++----
 4 files changed, 45 insertions(+), 15 deletions(-)

diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp
index f89a9e992..7c9868f4f 100644
--- a/src/lib/iter-explorer.hpp
+++ b/src/lib/iter-explorer.hpp
@@ -175,9 +175,11 @@ namespace lib {
       typedef typename SRC::reference reference;
       typedef typename SRC::pointer  pointer;
       
-      /** Metafunction: the resulting type when binding a functor of type FUN */
+      /** Metafunction: the resulting type when binding ("flat mapping")
+       *  a functor of type FUN. Basically the result of binding a function
+       *  is again an IterExplorer (with an "expanded" state core type) */
       template
-      struct Binding
+      struct FlatMapped
         {
           typedef IterExplorer<_COM_, _COM_> Type;
         };
@@ -210,11 +212,11 @@ namespace lib {
        *  of the result iterator. 
        */
       template
-      IterExplorer<_COM_, _COM_>
+      typename FlatMapped::Type
       operator >>= (FUN explorer)
         {
           typedef _COM_         Combinator;       // instantiation of the combinator strategy
-          typedef IterExplorer ResultsIter;      // result IterExplorer using that instance as state core
+          typedef typename FlatMapped::Type ResultsIter;       // result IterExplorer using that instance as state core
           
           return ResultsIter (
                   Combinator (explorer                              // build a new iteration state core
diff --git a/src/lib/meta/function.hpp b/src/lib/meta/function.hpp
index 7bb4ecffa..87f7379df 100644
--- a/src/lib/meta/function.hpp
+++ b/src/lib/meta/function.hpp
@@ -230,6 +230,13 @@ namespace meta{
       typedef typename FunctionSignature >::Ret Ret;
       typedef typename FunctionSignature >::Args Args;
     };
+  /** Specialisation when using a function reference */
+  template
+  struct _Fun
+    {
+      typedef typename FunctionSignature >::Ret Ret;
+      typedef typename FunctionSignature >::Args Args;
+    };
   /** Specialisation for passing a functor */
   template
   struct _Fun >
diff --git a/src/proc/engine/job-planning.hpp b/src/proc/engine/job-planning.hpp
index dbdcc8c52..87ea861c7 100644
--- a/src/proc/engine/job-planning.hpp
+++ b/src/proc/engine/job-planning.hpp
@@ -28,6 +28,7 @@
 //#include "proc/state.hpp"
 #include "proc/engine/job.hpp"
 #include "proc/engine/job-ticket.hpp"
+#include "proc/engine/time-anchor.hpp"
 //#include "proc/engine/frame-coord.hpp"
 //#include "lib/time/timevalue.hpp"
 //#include "lib/time/timequant.hpp"
@@ -190,7 +191,7 @@ namespace engine {
             return this->wrapping(*prerequisites);
                //  explanation: PlanningState represents a sequence of successive planning points.
               //                actually this is implemented by switching an embedded JobPlanning element
-             //                 through a sequence of states. Thus the initial state of the investigation
+             //                 through a sequence of states. Thus the initial state of an investigation
             //                  (which is a JobPlanning) can stand-in for the sequence of prerequisites
         }
       
@@ -210,10 +211,10 @@ namespace engine {
   
   
   inline PlanningState
-  expandPrerequisites (PlanningState const& calculationStep)
+  expandPrerequisites (JobPlanning const& calculationStep)
   {
     PlanningState newSubEvaluation(
-                    calculationStep->discoverPrerequisites());
+                    calculationStep.discoverPrerequisites());
     return newSubEvaluation;
   }
   
@@ -233,11 +234,18 @@ namespace engine {
    */
   class PlanningStepGenerator
     {
+      engine::TimeAnchor anchor_;
+      
     public:
       typedef JobPlanning value_type;
       typedef JobPlanning& reference;
       typedef JobPlanning *  pointer;
       
+//      PlanningStepGenerator() { }
+      
+      PlanningStepGenerator(engine::TimeAnchor startPoint)
+        : anchor_(startPoint)
+        { }
       
       /* === Iteration control API for IterStateWrapper== */
       
@@ -261,8 +269,14 @@ namespace engine {
     };
   
   
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #827
-#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #827
+  
+  
+  typedef PlanningState (*SIG_expandPrerequisites) (JobPlanning const&);
+  
+  typedef lib::IterExplorer      JobPlanningChunkStartPoint;
+
+  typedef JobPlanningChunkStartPoint::FlatMapped::Type  ExpandedPlanningSequence;
   
   
   
@@ -270,11 +284,18 @@ namespace engine {
    * @todo 6/12 WIP-WIP-WIP how to prepare jobs for scheduling
    */
   class JobPlanningSequence
-    : public lib::IterExplorer
+    : public ExpandedPlanningSequence
     {
       
     public:
+//    JobPlanningSequence() { }
+      
+      JobPlanningSequence(engine::TimeAnchor startPoint)
+        : ExpandedPlanningSequence(
+            JobPlanningChunkStartPoint(
+                PlanningStepGenerator(startPoint))
+            >>= expandPrerequisites)
+        { }
       
     };
   
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index ca637dc40..399e8e00d 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -2797,15 +2797,15 @@ From experiences with other middle scale projects, I prefer having the test code
 [img[Example: Interfaces/Namespaces of the ~Session-Subsystems|uml/fig130053.png]]
 
-
-
The actual media data is rendered by [[individually scheduled render jobs|RenderJob]]. When preparing such jobs, in order to [[dispatch|FrameDispatcher]] the individual frames calculations required to make a given [[stream of calculations|CalcStream]] happen, a ''node planning phase'' is performed. The goal is to find out
+
+
The actual media data is rendered by [[individually scheduled render jobs|RenderJob]]. All these calculations together implement a [[stream of calculations|CalcStream]], as demanded and directed by the PlayProcess. During the preparation of playback, a ''node planning phase'' is performed, to arrange for [[dispatching|FrameDispatcher]] the individual calculations per frame.  The goal of these //preparations//&nbsp; is to find out
 * what channel(s) to pull
 * what prerequisites to prepare
 * what parameters to provide
 The result of this planning phase is the {{{JobTicket}}}, a complete ''execution plan''.
-This planning is uniform for each segment and treated for all channels together, resulting in a nested tree structure of sub job tickets, allocated and stored alongside with the processing nodes and wiring descriptors to form the segment's data and descriptor network. Job tickets are //higher order functions:// entering a concrete frame number and channel into a given job ticket will produce an actual job descriptor, which in itself is again a function, to be invoked through the scheduler when it's time to trigger the actual calculations.
+This planning is uniform for each [[segment|Segmentation]] and treated for all channels together, resulting in a nested tree structure of sub job tickets, allocated and stored alongside with the processing nodes and wiring descriptors to form the segment's data and descriptor network. Job tickets are //higher order functions:// entering a concrete frame number and channel into a given job ticket will produce an actual job descriptor, which in itself is again a function, to be invoked through the scheduler when it's time to trigger the actual calculations.
 
-!Structure of the render Jobs created
+!Structure of the Render Jobs created
 To be more precise: in the general case, invoking the ~JobTicket with a given frame number will produce //multiple jobs//  -- typically each frame rendering will require at least one further source media frame; and because Lumiera render jobs will //never block waiting on IO,// this source media access will be packaged as a separate [[resource retrieving job|ResourceJob]], to be treated specifically by the scheduler.
 
 To support the generation of multiple dependent jobs, a ~JobTicket might refer to further ~JobTickets corresponding to the prerequisites. These prerequisite ~JobTickets are the result of a classical recursive descent call into the top level ProcNode, to perform the planning step. There is always an 1:1 relation between actual jobs generated and the corresponding tickets. More precisely, for each possible job to generate there is a suitable ''job closure'', representing exactly the context information necessary to get that job started. To stress that point: a ProcNode might be configured such as to perform a series of recursive invocations of other prerequisite nodes right away (especially when those recursive invocations correspond just to further CPU bound calculations) -- yet still there is only //one//&nbsp; ~JobTicket, because there will be only one Job to invoke the whole sequence. On the other hand, when there is a prerequisite requiring an operation to be scheduled separately, a corresponding separate {{{JobClosure}}} will be referred.

From 30cf0b5718b2840a9b70d4aa667938fa64f0ca5d Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 3 Sep 2012 01:49:14 +0200
Subject: [PATCH 488/496] implementation of JobTicket::ExplorationState

---
 src/lib/linked-elements.hpp      |  2 +
 src/proc/engine/job-planning.hpp |  4 +-
 src/proc/engine/job-ticket.hpp   | 71 ++++++++++++++++++++++++++++----
 3 files changed, 66 insertions(+), 11 deletions(-)

diff --git a/src/lib/linked-elements.hpp b/src/lib/linked-elements.hpp
index c12342dc0..0e654c65e 100644
--- a/src/lib/linked-elements.hpp
+++ b/src/lib/linked-elements.hpp
@@ -62,6 +62,7 @@
 
 #include "lib/error.hpp"
 #include "lib/iter-adapter.hpp"
+#include "lib/util.hpp"
 
 #include 
 #include 
@@ -72,6 +73,7 @@ namespace lib {
   
   namespace error = lumiera::error;
   using error::LUMIERA_ERROR_INDEX_BOUNDS;
+  using util::unConst;
   
   
   namespace linked_elements { ///< allocation policies for the LinkedElements list container
diff --git a/src/proc/engine/job-planning.hpp b/src/proc/engine/job-planning.hpp
index 87ea861c7..859258aab 100644
--- a/src/proc/engine/job-planning.hpp
+++ b/src/proc/engine/job-planning.hpp
@@ -108,7 +108,7 @@ namespace engine {
           if (isnil (plannedOperations_))
             return JobPlanning();
           else
-            return JobPlanning (plannedOperations_->discoverPrerequisites()
+            return JobPlanning (plannedOperations_->discoverPrerequisites (point_to_calculate_.channelNr)
                                ,this->point_to_calculate_);
         }
       
@@ -116,7 +116,7 @@ namespace engine {
        *  Further evaluation will start to visit prerequisites from the new starting point,
        *  and return to the current evaluation chain later on exhaustion of the side chain. */
       friend void
-      integrate (JobPlanning const& newStartingPoint, JobPlanning existingPlan)
+      integrate (JobPlanning const& newStartingPoint, JobPlanning& existingPlan)
       {
         existingPlan.plannedOperations_.push (newStartingPoint.plannedOperations_);
       }
diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index 56148907d..d50efbc2f 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -32,6 +32,7 @@
 //#include "lib/time/timequant.hpp"
 #include "lib/linked-elements.hpp"
 #include "lib/iter-adapter.hpp"
+#include "lib/util.hpp"
 
 #include 
 #include 
@@ -45,6 +46,7 @@ namespace engine {
 //using lib::time::FSecs;
 //using lib::time::Time;
 using lib::LinkedElements;
+using util::isnil;
 //  
 //class ExitNode;
   
@@ -128,7 +130,7 @@ using lib::LinkedElements;
         }
       
       
-      ExplorationState discoverPrerequisites()  const;
+      ExplorationState discoverPrerequisites (uint channelNr)  const;
       
       Job createJobFor (FrameCoord coordinates);
       
@@ -136,6 +138,9 @@ using lib::LinkedElements;
       bool
       isValid()  const
         {
+          if (channelConfig_.size() != requirement_.size())
+            return false;
+          
           UNIMPLEMENTED ("validity self check");
         }
     };
@@ -155,39 +160,87 @@ using lib::LinkedElements;
       bool
       empty()  const
         {
-          UNIMPLEMENTED ("detect empty/no prerequisites");
+          return toExplore_.empty();
         }
       
       
       void
       pullNext()
         {
-          UNIMPLEMENTED ("step ahead to next prerequisite, maybe pop");
+          if (empty())
+            throw lumiera::error::Logic ("Exploration of Job prerequisites floundered. "
+                                         "Attempt to iterate beyond the end of prerequisite list"
+                                        ,lumiera::error::LUMIERA_ERROR_ITER_EXHAUST);
+          ASSERT (toExplore_.top().isValid());
+          
+          ++(toExplore_.top());
+          while ( !toExplore_.empty()
+                && toExplore_.top().empty())
+            toExplore_.pop();
+          
+          ENSURE (empty() || toExplore_.top().isValid());
         }
       
+      
       void
       push (ExplorationState subExploration)
         {
-          UNIMPLEMENTED ("integrate a branch of prerequisites");
+          if (subExploration.empty()) return;
+          
+          pushAllPrerequisites (subExploration.toExplore_);
+        }
+      
+      void
+      push (Prerequisites& prerequisites)
+        {
+          if (prerequisites.requiredJobs_.empty()) return;
+          toExplore_.push (prerequisites.requiredJobs_.begin());
         }
       
       
       JobTicket*
       operator->() const
         {
-          UNIMPLEMENTED ("yield the current job ticket");
-          // access prerequisite and follow up to prerequisite JobTicket
+          REQUIRE (!empty() && toExplore_.top().isValid());
+          REQUIRE (toExplore_.top()->descriptor);
+          REQUIRE (toExplore_.top()->descriptor->isValid());
+          
+          return toExplore_.top()->descriptor;
         }
       
+      
+    private:
+      void
+      pushAllPrerequisites (SubTicketStack& furtherPrerequisites)
+        {
+          REQUIRE (!isnil (furtherPrerequisites));
+          
+          if (1 == furtherPrerequisites.size())
+            {
+              this->toExplore_.push (furtherPrerequisites.top());
+            }
+          else 
+            { // pathological case: several levels of prerequisites
+              // --> push recursively to retain level ordering
+              SubTicketSeq deepestLevel (furtherPrerequisites.top());
+              furtherPrerequisites.pop();
+              pushAllPrerequisites (furtherPrerequisites);
+              this->toExplore_.push (deepestLevel);
+            }
+        }
     };
-    
+  
   
   
   
   inline JobTicket::ExplorationState
-  JobTicket::discoverPrerequisites()  const
+  JobTicket::discoverPrerequisites (uint channelNr)  const
   {
-    UNIMPLEMENTED("delve into the requirement_ datastructure");
+    REQUIRE (channelNr < requirement_.size());
+    
+    ExplorationState explorer;
+    explorer.push (requirement_[channelNr]);
+    return explorer;
   }
 
   

From 14e6086488dd818a82a269a3e70aa32d5d67d362 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 4 Sep 2012 00:17:28 +0200
Subject: [PATCH 489/496] better do initialisation by ctor

---
 src/proc/engine/job-ticket.hpp | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index d50efbc2f..a6e9e6e52 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -154,7 +154,15 @@ using util::isnil;
       SubTicketStack toExplore_;
       
     public:
-      // using default ctor and copy operations
+      ExplorationState() { }
+      
+      ExplorationState (Prerequisites& prerequisites)
+        {
+          if (!isnil (prerequisites.requiredJobs_))
+            toExplore_.push (prerequisites.requiredJobs_.begin());
+        }
+      
+      // using default copy operations
       
       
       bool
@@ -190,13 +198,6 @@ using util::isnil;
           pushAllPrerequisites (subExploration.toExplore_);
         }
       
-      void
-      push (Prerequisites& prerequisites)
-        {
-          if (prerequisites.requiredJobs_.empty()) return;
-          toExplore_.push (prerequisites.requiredJobs_.begin());
-        }
-      
       
       JobTicket*
       operator->() const
@@ -238,9 +239,7 @@ using util::isnil;
   {
     REQUIRE (channelNr < requirement_.size());
     
-    ExplorationState explorer;
-    explorer.push (requirement_[channelNr]);
-    return explorer;
+    return ExplorationState (requirement_[channelNr]);
   }
 
   

From ce5b940d390198dc4eec4d0939df1df39e67bf1d Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 4 Sep 2012 01:55:11 +0200
Subject: [PATCH 490/496] WIP consider how to seed the evaluation of a
 JobTicket

---
 src/proc/engine/job-planning.hpp |  8 +++++++-
 src/proc/engine/job-ticket.hpp   | 12 ++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/src/proc/engine/job-planning.hpp b/src/proc/engine/job-planning.hpp
index 859258aab..abe77c9d8 100644
--- a/src/proc/engine/job-planning.hpp
+++ b/src/proc/engine/job-planning.hpp
@@ -114,10 +114,16 @@ namespace engine {
       
       /** integrate another chain of prerequisites into the current evaluation line.
        *  Further evaluation will start to visit prerequisites from the new starting point,
-       *  and return to the current evaluation chain later on exhaustion of the side chain. */
+       *  and return to the current evaluation chain later on exhaustion of the side chain.
+       *  Especially in case the current evaluation is empty or already exhausted, the
+       *  new starting point effectively replaces the current evaluation point */
       friend void
       integrate (JobPlanning const& newStartingPoint, JobPlanning& existingPlan)
       {
+        if (isnil (existingPlan.plannedOperations_))
+          { // current evaluation is exhausted: switch to new starting point
+            existingPlan.point_to_calculate_ = newStartingPoint.point_to_calculate_;
+          }
         existingPlan.plannedOperations_.push (newStartingPoint.plannedOperations_);
       }
       
diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index a6e9e6e52..b38df691c 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -130,6 +130,7 @@ using util::isnil;
         }
       
       
+      ExplorationState startExploration()                      const;
       ExplorationState discoverPrerequisites (uint channelNr)  const;
       
       Job createJobFor (FrameCoord coordinates);
@@ -234,6 +235,17 @@ using util::isnil;
   
   
   
+  inline JobTicket::ExplorationState
+  JobTicket::startExploration()  const
+  {
+    UNIMPLEMENTED ("somehow build a self-referential pseudo-prerequisite, and seed an ExplorationState with that");
+    /////////////////////TODO problem is: we need an JobTicket::Prerequisite instance, where the descriptor points to "self" (this JobTicket)
+    /////////////////////TODO           : but this instance needs to reside somewhere at a safe location, since we want to embed an LinkedElements-iterator
+    /////////////////////TODO           : into the ExplorationState. And obviously we do not want that instance in each JobTicket, only in the top level ones
+  }
+  
+  
+  
   inline JobTicket::ExplorationState
   JobTicket::discoverPrerequisites (uint channelNr)  const
   {

From 3300d00cc848eb9714e8a5ef77a0ff3b17c08b47 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 6 Oct 2012 02:29:19 +0200
Subject: [PATCH 491/496] WIP working towards a solution for generating frame
 location sequences

..the Idea is to rely on some kind of service,
to break the cyclic dependency with the Dispatcher.
But I seem unable to find a natural location or
concept to house that service.
---
 src/proc/engine/dispatcher.hpp   | 12 ++++++++----
 src/proc/engine/frame-coord.hpp  | 28 ++++++++++++++++++++++++++++
 src/proc/engine/job-planning.hpp | 31 ++++++++++++++++++++++++++++++-
 src/proc/engine/job-ticket.hpp   |  3 +++
 src/proc/engine/time-anchor.hpp  | 19 ++++++++-----------
 5 files changed, 77 insertions(+), 16 deletions(-)

diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp
index 806f49cd4..b084f6c83 100644
--- a/src/proc/engine/dispatcher.hpp
+++ b/src/proc/engine/dispatcher.hpp
@@ -60,6 +60,8 @@ namespace engine {
           ModelPort modelPort_;
           uint channel_;
           
+          /////TODO need storage for the continuation
+          
           FrameCoord relativeFrameLocation (TimeAnchor refPoint, uint frameCountOffset);
           
           JobBuilder& establishNextJobs (TimeAnchor refPoint);
@@ -70,8 +72,9 @@ namespace engine {
             {
               UNIMPLEMENTED ("how to represent the closure for defining and scheduling jobs");
               
-              ////////TODO: use a closure based on the JobTicket (which can be accessed through the dispatcher backlink)
-              ////////////  Because this closure is what backs the IterSource, it needs to have a reliable storage though. 
+              ////////TODO: use a closure based on FrameCoord plus a back-reference to the Dispatcher.
+              ////////////  Thus actually we need a *generator* for a sequence of FrameCoord
+              ////////////  This generator is then wrapped up into an evaluation-Monad (IterExplorer) 
             }
 
         };
@@ -83,8 +86,9 @@ namespace engine {
       
       JobTicket& accessJobTicket (FrameCoord const&);
       
-    protected:      
-      virtual FrameCoord locateFrameNext (uint frameCountOffset, TimeAnchor refPoint)   =0;
+    protected:
+      virtual FrameCoord locateRelative (FrameCoord, uint frameCountOffset)   =0;
+      virtual FrameCoord locateRelative (TimeAnchor, uint frameCountOffset)   =0;
     };
   
   
diff --git a/src/proc/engine/frame-coord.hpp b/src/proc/engine/frame-coord.hpp
index c211b1193..a73be8d96 100644
--- a/src/proc/engine/frame-coord.hpp
+++ b/src/proc/engine/frame-coord.hpp
@@ -90,5 +90,33 @@ namespace engine {
   
   
   
+  /**
+   * Facility for producing a sequence of FrameCoord.
+   * This interface describes the essence of generating
+   * a series of frame locations, which is necessary for
+   * planning render jobs. To implement it, actually some
+   * kind of \link lib::time::Quantiser frame grid \endlink
+   * is necessary -- in practice we use a Dispatcher, which is
+   * backed by the Segmentation (i.e. the render nodes network).
+   */
+  class FrameSequencer
+    : boost::noncopyable
+    {
+      
+    public:
+      virtual ~FrameSequencer();  ///< this is an interface
+      
+      FrameCoord
+      getNextFrame (FrameCoord refPoint)
+        {
+          return locateRelative (refPoint, +1 );
+        }
+      
+    protected:
+      virtual FrameCoord locateRelative (FrameCoord, uint frameCountOffset)   =0;
+    };
+  
+  
+  
 }} // namespace proc::engine
 #endif
diff --git a/src/proc/engine/job-planning.hpp b/src/proc/engine/job-planning.hpp
index abe77c9d8..f0268b289 100644
--- a/src/proc/engine/job-planning.hpp
+++ b/src/proc/engine/job-planning.hpp
@@ -240,7 +240,8 @@ namespace engine {
    */
   class PlanningStepGenerator
     {
-      engine::TimeAnchor anchor_;
+      engine::FrameSequencer* locationGenerator_;
+      engine::FrameCoord      current_;
       
     public:
       typedef JobPlanning value_type;
@@ -252,6 +253,12 @@ namespace engine {
       PlanningStepGenerator(engine::TimeAnchor startPoint)
         : anchor_(startPoint)
         { }
+      //////////////////////////////////////////////////////////////TODO actually we need two distinct services
+      //////////////////////////////////////////////////////////////TODO - getting the next FrameCoord
+      //////////////////////////////////////////////////////////////TODO - getting the JobTicket for this location
+      //////////////////////////////////////////////////////////////TODO Actually, the Dispatcher would provide exactly those services,
+      //////////////////////////////////////////////////////////////TODO but depending on the Dispatcher constitutes a cyclic dependency.
+      //////////////////////////////////////////////////////////////TODO There seems to be a problem hidden somewhere in this design.
       
       /* === Iteration control API for IterStateWrapper== */
       
@@ -287,6 +294,28 @@ namespace engine {
   
   
   /**
+   * This iterator represents a pipeline to pull planned jobs from.
+   * For dispatching individual frame jobs for rendering, this pipeline is
+   * generated and internally wired such as to interpret the render node definitions.
+   * 
+   * \par Explanation of the structure
+   * 
+   * The JobPlanningSequence is constructed from several nested layers of functionality
+   * - for the client, it is an iterator, exposing a sequence of JobPlanning elements
+   * - a JobPlanning element allows to add a frame render job to the scheduler
+   * - actually such an element can even be \em converted directly into a Job (descriptor)
+   * - the sequence of such JobPlanning elements (that is, the iterator) is called a PlanningState,
+   *   since evaluating this iterator effectively drives the process of job planning ahead
+   * - this planning process is \em implemented as a recursive evaluation and exploration of
+   *   a tree of prerequisites; these prerequisites are defined in the JobTicket datastructure
+   * - there is an underlying grid of evaluation starting points, each corresponding to a
+   *   single frame. Typically, each frame generates at least two jobs, one for fetching
+   *   data, and one for the actual calculations. Depending on the actual render network,
+   *   a lot of additional jobs might be necessary
+   * - this basic frame grid is generated by the PlanningStepGenerator, which is
+   *   effectively backed by the Dispatcher and thus the render node model. 
+   * 
+   * 
    * @todo 6/12 WIP-WIP-WIP how to prepare jobs for scheduling
    */
   class JobPlanningSequence
diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp
index b38df691c..45ff53b47 100644
--- a/src/proc/engine/job-ticket.hpp
+++ b/src/proc/engine/job-ticket.hpp
@@ -242,6 +242,9 @@ using util::isnil;
     /////////////////////TODO problem is: we need an JobTicket::Prerequisite instance, where the descriptor points to "self" (this JobTicket)
     /////////////////////TODO           : but this instance needs to reside somewhere at a safe location, since we want to embed an LinkedElements-iterator
     /////////////////////TODO           : into the ExplorationState. And obviously we do not want that instance in each JobTicket, only in the top level ones
+    
+    /////////////////////TODO : on second thought -- better have a top-level entry point to the evaluation of a frame
+    /////////////////////TODO                        basically this inherits from Prerequisite and lives somehow in the dispatcher-table or segment
   }
   
   
diff --git a/src/proc/engine/time-anchor.hpp b/src/proc/engine/time-anchor.hpp
index f944a5f86..7d2b5cc63 100644
--- a/src/proc/engine/time-anchor.hpp
+++ b/src/proc/engine/time-anchor.hpp
@@ -46,17 +46,14 @@ namespace engine {
 //  class ExitNode;
   
   /**
-   * The process of playback or rendering is a continued series of
-   * exploration and evaluation. The outline of what needs to be calculated
-   * is determined continuously, proceeding in chunks of evaluation.
-   * Each of these continued partial evaluations establishes a distinct
-   * anchor or breaking point in time: everything before this point
-   * is settled and planned thus far. Effectively, this time point
-   * acts as a evaluation closure, to be picked up on the
-   * next partial evaluation. More specifically, the TimeAnchor
-   * closure is the definitive binding between the abstract
-   * logical time of the session timeline, and the real
-   * wall-clock time forming the deadline for rendering.
+   * The process of playback or rendering is a continued series of exploration and evaluation.
+   * The outline of what needs to be calculated is determined continuously, proceeding in
+   * chunks of evaluation. Each of these continued partial evaluations establishes a distinct
+   * anchor or breaking point in time: everything before this point can be considered settled
+   * and planned thus far. Effectively, this time point acts as a evaluation closure,
+   * to be picked up on the next partial evaluation. More specifically, the TimeAnchor closure
+   * is the definitive binding between the abstract logical time of the session timeline, and
+   * the real wall-clock time forming the deadline for rendering.
    * 
    * \par internals
    * The time anchor associates a nominal time, defined on the implicit time grid

From 44435fd1dba93a6244c3370ac74b1d116d439316 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 10 Oct 2012 04:35:56 +0200
Subject: [PATCH 492/496] clarify and settle the relation between Dispatcher
 and PlanningStepGenerator

the solution is to introduce a superinterface
and let Dispatcher augment that with the specific parts.
This way, the Job planning only has to rely on the
rather generic stuff (TimeAnchor, FrameCoord)

NOTE: this commit makes the whole JobPlanning machinery
compilable for the first time!
---
 src/proc/engine/dispatcher.cpp                | 12 +----
 src/proc/engine/dispatcher.hpp                | 38 ++++++++++----
 src/proc/engine/frame-coord.hpp               |  6 +--
 src/proc/engine/job-planning.hpp              | 49 +++++++++++++------
 src/proc/mobject/builder/segmentation.hpp     |  3 +-
 .../proc/engine/dispatcher-interface-test.cpp | 20 +++++++-
 6 files changed, 87 insertions(+), 41 deletions(-)

diff --git a/src/proc/engine/dispatcher.cpp b/src/proc/engine/dispatcher.cpp
index 07a22728c..37ded0273 100644
--- a/src/proc/engine/dispatcher.cpp
+++ b/src/proc/engine/dispatcher.cpp
@@ -30,7 +30,8 @@
 namespace proc {
 namespace engine {
 
-  Dispatcher::~Dispatcher() { }  // emit VTable here....
+  Dispatcher::~Dispatcher() { }  // emit VTables and Typeinfo here....
+  FrameSequencer::~FrameSequencer() { }
   
   
   /** */
@@ -65,17 +66,8 @@ namespace engine {
       UNIMPLEMENTED ("wrap already planned jobs, appending a continuation to pick up later");
       return *this;
     }
-
-
   
   
-  /** */
-  JobTicket&
-  Dispatcher::accessJobTicket (FrameCoord const& frameID)
-  {
-    UNIMPLEMENTED ("figure out and create the actual JobTicket, for the current segment and the given port");
-  }
-  
   
   
   
diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp
index b084f6c83..d329039d3 100644
--- a/src/proc/engine/dispatcher.hpp
+++ b/src/proc/engine/dispatcher.hpp
@@ -49,20 +49,40 @@ namespace engine {
 //  class ExitNode;
   
   /**
-   * @todo 11/11 extremely fuzzy at the moment
+   * Internal abstraction: a service within the engine
+   * for translating a logical calculation stream (corresponding to a PlayProcess)
+   * into a sequence of individual RenderJob entries for calculations and data access.
+   * The actual implementation of this service is tied to the low-level-model, i.e.
+   * the render nodes network. The Dispatcher service is used to implement the CalcStreams
+   * during playback and rendering; there will be a continuous, chunk-wise proceeding
+   * evaluation and planning of new jobs, which can then be handed over to the Scheduler
+   * for time-bound activation.
+   * 
+   * \par usage considerations
+   * the asynchronous and ongoing nature of the render process mandates to avoid a central
+   * instance for operating this planning process. Instead, each chunk of planned jobs
+   * contains a continuation job, which -- on activation -- will pick up the planning
+   * of the next chunk. The Dispatcher interface was shaped to support this process,
+   * with a local JobBuilder to be used within the continuation job, and a TimeAnchor
+   * to represent the continuation point. All the complexities of planning jobs are
+   * hidden within the JobPlanningSequence, which, for the purpose of dispatching
+   * a series of jobs just looks like a sequence of job descriptors
+   * 
+   * @todo 10/12 still WIP, but conceptually settled by now
    */
   class Dispatcher
-    : boost::noncopyable
+    : public FrameLocator
     {
       struct JobBuilder
         {
           Dispatcher& dispatcher_;
+          TimeAnchor refPoint_;
           ModelPort modelPort_;
           uint channel_;
           
           /////TODO need storage for the continuation
           
-          FrameCoord relativeFrameLocation (TimeAnchor refPoint, uint frameCountOffset);
+          FrameCoord relativeFrameLocation (TimeAnchor refPoint, uint frameCountOffset =0);
           
           JobBuilder& establishNextJobs (TimeAnchor refPoint);
           
@@ -70,11 +90,10 @@ namespace engine {
           
           operator JobPlanningSequence()
             {
-              UNIMPLEMENTED ("how to represent the closure for defining and scheduling jobs");
+              TODO ("build the continuation job if necessary, wrap the sequence");
               
-              ////////TODO: use a closure based on FrameCoord plus a back-reference to the Dispatcher.
-              ////////////  Thus actually we need a *generator* for a sequence of FrameCoord
-              ////////////  This generator is then wrapped up into an evaluation-Monad (IterExplorer) 
+              return JobPlanningSequence(
+                  relativeFrameLocation(refPoint_), dispatcher_); 
             }
 
         };
@@ -84,11 +103,12 @@ namespace engine {
       
       JobBuilder onCalcStream (ModelPort modelPort, uint channel);
       
-      JobTicket& accessJobTicket (FrameCoord const&);
       
     protected:
       virtual FrameCoord locateRelative (FrameCoord, uint frameCountOffset)   =0;
-      virtual FrameCoord locateRelative (TimeAnchor, uint frameCountOffset)   =0;
+      virtual FrameCoord locateRelative (TimeAnchor, uint frameCountOffset)   =0;     //////////TODO is this really an interface operation, or just a convenience shortcut?
+      
+      virtual JobTicket& accessJobTicket (ModelPort, TimeValue nominalTime)   =0;
     };
   
   
diff --git a/src/proc/engine/frame-coord.hpp b/src/proc/engine/frame-coord.hpp
index a73be8d96..e90a2bc7b 100644
--- a/src/proc/engine/frame-coord.hpp
+++ b/src/proc/engine/frame-coord.hpp
@@ -39,7 +39,7 @@ namespace engine {
 //using lib::time::TimeSpan;
   using lib::time::Duration;
 //using lib::time::FSecs;
-  using lib::time::Time;
+  using lib::time::TimeVar;
 //  
 //  class ExitNode;
   
@@ -50,7 +50,7 @@ namespace engine {
    * A frame render job can be characterised by
    * - the nominal (timeline) time of the frame
    * - the corresponding frame-number
-   * - the real wall-clock time of expected delivery
+   * - the real wall-clock time of expected delivery//////////////TODO : might be handy, but not sure if this information is substantial here
    * - timing constraints (e.g. latency to observe) //////////////TODO : not clear if we repeat this information here
    * - the actual node to pull data from
    * - the segment holding that node                //////////////TODO : is this information really required??
@@ -64,7 +64,7 @@ namespace engine {
     {
       
     public:
-      Time absoluteNominalTime;
+      TimeVar absoluteNominalTime;
       int64_t absoluteFrameNumber;
       
       ModelPort modelPort;
diff --git a/src/proc/engine/job-planning.hpp b/src/proc/engine/job-planning.hpp
index f0268b289..9a4e90344 100644
--- a/src/proc/engine/job-planning.hpp
+++ b/src/proc/engine/job-planning.hpp
@@ -28,8 +28,8 @@
 //#include "proc/state.hpp"
 #include "proc/engine/job.hpp"
 #include "proc/engine/job-ticket.hpp"
-#include "proc/engine/time-anchor.hpp"
-//#include "proc/engine/frame-coord.hpp"
+//#include "proc/engine/time-anchor.hpp"
+#include "proc/engine/frame-coord.hpp"
 //#include "lib/time/timevalue.hpp"
 //#include "lib/time/timequant.hpp"
 //#include "lib/meta/function.hpp"
@@ -226,7 +226,27 @@ namespace engine {
   
   
 
-    
+
+  /**
+   * Abstraction: a Facility to establish frame coordinates
+   * and identify and access the execution plan for this frame.
+   * @see Dispatcher the service interface actually used
+   */
+  class FrameLocator
+    : public FrameSequencer
+    {
+    public:
+      
+      JobTicket&
+      getJobTicketFor (FrameCoord location)
+        {
+          return accessJobTicket (location.modelPort, location.absoluteNominalTime);
+        }
+      
+    protected:
+      virtual JobTicket& accessJobTicket (ModelPort, TimeValue nominalTime)   =0;
+    };
+  
     
   /**
    * Generate a sequence of starting points for Job planning,
@@ -240,25 +260,22 @@ namespace engine {
    */
   class PlanningStepGenerator
     {
-      engine::FrameSequencer* locationGenerator_;
-      engine::FrameCoord      current_;
+      FrameLocator* locationGenerator_;
+      FrameCoord    currentLocation_;
       
     public:
       typedef JobPlanning value_type;
       typedef JobPlanning& reference;
       typedef JobPlanning *  pointer;
       
-//      PlanningStepGenerator() { }
       
-      PlanningStepGenerator(engine::TimeAnchor startPoint)
-        : anchor_(startPoint)
+      PlanningStepGenerator(FrameCoord startPoint, FrameLocator& locator)
+        : locationGenerator_(&locator)
+        , currentLocation_(startPoint)
         { }
-      //////////////////////////////////////////////////////////////TODO actually we need two distinct services
-      //////////////////////////////////////////////////////////////TODO - getting the next FrameCoord
-      //////////////////////////////////////////////////////////////TODO - getting the JobTicket for this location
-      //////////////////////////////////////////////////////////////TODO Actually, the Dispatcher would provide exactly those services,
-      //////////////////////////////////////////////////////////////TODO but depending on the Dispatcher constitutes a cyclic dependency.
-      //////////////////////////////////////////////////////////////TODO There seems to be a problem hidden somewhere in this design.
+      
+      // default copyable
+      
       
       /* === Iteration control API for IterStateWrapper== */
       
@@ -325,10 +342,10 @@ namespace engine {
     public:
 //    JobPlanningSequence() { }
       
-      JobPlanningSequence(engine::TimeAnchor startPoint)
+      JobPlanningSequence(engine::FrameCoord startPoint, FrameLocator& locator)
         : ExpandedPlanningSequence(
             JobPlanningChunkStartPoint(
-                PlanningStepGenerator(startPoint))
+                PlanningStepGenerator(startPoint,locator))
             >>= expandPrerequisites)
         { }
       
diff --git a/src/proc/mobject/builder/segmentation.hpp b/src/proc/mobject/builder/segmentation.hpp
index 0eed4ed78..617445c18 100644
--- a/src/proc/mobject/builder/segmentation.hpp
+++ b/src/proc/mobject/builder/segmentation.hpp
@@ -63,8 +63,9 @@ namespace builder {
     : boost::noncopyable
     {
       
+      /////////////////////////////////////////////////TODO: placeholder code
+      /////////////////////////////////////////////////TODO: see the planned structure at http://lumiera.org/wiki/renderengine.html#Fixture
       typedef ID   PID;
-//      typedef ID StID;
       
     public:
       
diff --git a/tests/components/proc/engine/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp
index 86d19e1bb..b7dfc1476 100644
--- a/tests/components/proc/engine/dispatcher-interface-test.cpp
+++ b/tests/components/proc/engine/dispatcher-interface-test.cpp
@@ -83,6 +83,22 @@ namespace test  {
           {
             UNIMPLEMENTED ("dummy implementation of the core dispatch operation");
           }
+        FrameCoord
+        locateRelative (FrameCoord, uint frameCountOffset)
+          {
+            UNIMPLEMENTED ("dummy implementation of the core dispatch operation");
+          }
+        FrameCoord
+        locateRelative (TimeAnchor, uint frameCountOffset)
+          {
+            UNIMPLEMENTED ("dummy implementation of the core dispatch operation");
+          }
+      
+        JobTicket&
+        accessJobTicket (ModelPort, TimeValue nominalTime)
+          {
+            UNIMPLEMENTED ("dummy implementation of the model backbone / segmentation");
+          }
 
       public:
         
@@ -153,7 +169,7 @@ namespace test  {
           CHECK (coordinates.modelPort == modelPort);
           CHECK (coordinates.channelNr == channel);
           
-          JobTicket& executionPlan = dispatcher.accessJobTicket (coordinates);
+          JobTicket& executionPlan = dispatcher.getJobTicketFor (coordinates);
           CHECK (executionPlan.isValid());
           
           Job frameJob = executionPlan.createJobFor (coordinates); //////////////////////////////TODO this is wrong: we never create a single job!
@@ -184,11 +200,11 @@ namespace test  {
           
           // Verify the planned Jobs
           
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
           CHECK (!isnil (jobs));
           vector plannedChunk;
           lib::append_all (jobs, plannedChunk);
           
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
           uint chunksize = plannedChunk.size();
           CHECK (chunksize == timings.getPlanningChunkSize());
           

From 954ebefc4c380d686fd13d5193589a2158966836 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 10 Oct 2012 05:09:03 +0200
Subject: [PATCH 493/496] draft implementation of the PlanningStepGenerator

---
 src/proc/engine/job-planning.hpp | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/src/proc/engine/job-planning.hpp b/src/proc/engine/job-planning.hpp
index 9a4e90344..c45bf1389 100644
--- a/src/proc/engine/job-planning.hpp
+++ b/src/proc/engine/job-planning.hpp
@@ -263,6 +263,23 @@ namespace engine {
       FrameLocator* locationGenerator_;
       FrameCoord    currentLocation_;
       
+      //////////////////////////////////////////TODO duplicated storage of a FrameCoord record
+      //////////////////////////////////////////TODO nextEvaluation_ is only needed to initialise the "current" sequence 
+      //////////////////////////////////////////TODO within the RecursiveSelfIntegration strategy. Maybe this storage could be collapsed?
+      mutable JobPlanning nextEvaluation_;
+      
+      JobPlanning&
+      use_current_location_as_planning_start()
+        {
+          JobTicket& processingPlan = locationGenerator_->getJobTicketFor (currentLocation_);
+          
+          nextEvaluation_ = JobPlanning(processingPlan.startExploration()
+                                       ,currentLocation_);
+          return nextEvaluation_;
+        }
+      
+      
+      
     public:
       typedef JobPlanning value_type;
       typedef JobPlanning& reference;
@@ -288,13 +305,13 @@ namespace engine {
       friend JobPlanning&
       yield (PlanningStepGenerator const& gen)
       {
-        UNIMPLEMENTED ("generate a single frame job planning tree");  ///return *(seq.feed());
+        return unConst(gen).use_current_location_as_planning_start();
       }
       
       friend void
       iterNext (PlanningStepGenerator & gen)
       {
-        UNIMPLEMENTED ("proceed to next frame");  ///////seq.iterate();
+        gen.currentLocation_ = gen.locationGenerator_->getNextFrame (gen.currentLocation_);
       }      
     };
   

From e267e0ad0b7d2bd658c4dd5b1cc22daf65c4cc9e Mon Sep 17 00:00:00 2001
From: Michael Fisher 
Date: Mon, 13 Aug 2012 12:08:18 -0500
Subject: [PATCH 494/496] GCC 4.7 compilation fixes

clarify binding of function (by specifying 'this')
---
 src/common/query/fake-configrules.hpp |  4 ++--
 src/lib/format-util.hpp               | 28 ++++++++++++---------------
 2 files changed, 14 insertions(+), 18 deletions(-)

diff --git a/src/common/query/fake-configrules.hpp b/src/common/query/fake-configrules.hpp
index 80ec54f74..37b7d8d07 100644
--- a/src/common/query/fake-configrules.hpp
+++ b/src/common/query/fake-configrules.hpp
@@ -143,7 +143,7 @@ namespace lumiera {
         virtual bool 
         resolve (Ret& solution, Query const& q)
           {
-            const any& entry = fetch_from_table_for (q.asKey());
+            const any& entry = this->fetch_from_table_for (q.asKey());
             if (!isnil (entry))
               {
                 Ret const& candidate (any_cast (entry));
@@ -166,7 +166,7 @@ namespace lumiera {
             if (is_defaults_query (newQuery))  // modified query..
               return solution = Session::current->defaults (newQuery);
                                              //   may cause recursion
-            if (detect_case (solution, newQuery))
+            if (this->detect_case (solution, newQuery))
               return resolve (solution, newQuery);
             
             return solution = Ret();     // fail: return default-constructed empty smart ptr
diff --git a/src/lib/format-util.hpp b/src/lib/format-util.hpp
index c5f1b76e8..c060299f7 100644
--- a/src/lib/format-util.hpp
+++ b/src/lib/format-util.hpp
@@ -102,8 +102,20 @@ namespace util {
   }//(End) guards/helpers
   
   
+  /** @return a string denoting the type. */
+  template
+  inline string
+  tyStr (const TY* =0)
+  {
+    return string("«")+typeid(TY).name()+"»";
+  }
   
+  template
+  inline string
+  tyStr (TY const& ref)
+  { return tyStr(&ref); }
   
+
   /** try to get an object converted to string.
    *  A custom/standard conversion to string is used,
    *  if applicable; otherwise, some standard types can be
@@ -126,21 +138,5 @@ namespace util {
                      : tyStr(val);
   }
   
-  
-  
-  /** @return a string denoting the type. */
-  template
-  inline string
-  tyStr (const TY* =0)
-  {
-    return string("«")+typeid(TY).name()+"»";
-  }
-  
-  template
-  inline string
-  tyStr (TY const& ref)
-  { return tyStr(&ref); }
-  
-  
 } // namespace util
 #endif

From f7a7c1c4166ae6ee1f121c16bbe60f696bb45813 Mon Sep 17 00:00:00 2001
From: Michael Fisher 
Date: Mon, 13 Aug 2012 12:55:36 -0500
Subject: [PATCH 495/496] GCC 4.7 compilation fixes. Now builds on debian
 wheezy beta2

---
 src/lib/element-tracker.hpp     | 4 ++--
 src/lib/multifact-arg.hpp       | 4 ++--
 src/lib/multifact.hpp           | 4 ++--
 src/lib/scoped-ptrvect.hpp      | 2 +-
 src/lib/time/control-impl.hpp   | 2 +-
 src/lib/time/control-policy.hpp | 2 +-
 6 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/lib/element-tracker.hpp b/src/lib/element-tracker.hpp
index 117f1855a..160c6b5a2 100644
--- a/src/lib/element-tracker.hpp
+++ b/src/lib/element-tracker.hpp
@@ -114,7 +114,7 @@ namespace lib {
         {
           REQUIRE (asset, "Attempt to track a NIL element");
           remove (*asset);
-          push_back (asset);
+          this->push_back (asset);
         }
       
       void
@@ -124,7 +124,7 @@ namespace lib {
                i != _Vec::end() ; ++i )
             if (asset == **i)       // _Vec contains smart-ptrs
               {                    //   ELM is required to define '=='
-                erase (i);
+                this->erase (i);
                 return;
               }
         }
diff --git a/src/lib/multifact-arg.hpp b/src/lib/multifact-arg.hpp
index eee802e67..5422e1d08 100644
--- a/src/lib/multifact-arg.hpp
+++ b/src/lib/multifact-arg.hpp
@@ -111,8 +111,8 @@ namespace lib {
         Product
         operator() (ID const& id, ARG arg)
           {
-            Creator& func = selectProducer (id);
-            return wrap (func(arg));
+            Creator& func = this->selectProducer (id);
+            return this->wrap (func(arg));
           }
         
       };
diff --git a/src/lib/multifact.hpp b/src/lib/multifact.hpp
index 29bef6bce..b09ca8e2f 100644
--- a/src/lib/multifact.hpp
+++ b/src/lib/multifact.hpp
@@ -199,8 +199,8 @@ namespace lib {
         Product
         operator() (ID const& id)
           {
-            Creator& func = selectProducer (id);
-            return wrap (func());
+            Creator& func = this->selectProducer (id);
+            return this->wrap (func());
           }
         
         
diff --git a/src/lib/scoped-ptrvect.hpp b/src/lib/scoped-ptrvect.hpp
index 37dff93dd..341f7c23b 100644
--- a/src/lib/scoped-ptrvect.hpp
+++ b/src/lib/scoped-ptrvect.hpp
@@ -117,7 +117,7 @@ namespace lib {
           REQUIRE (obj);
           try 
             {
-              push_back (obj);
+              this->push_back (obj);
               return *obj;
             }
           catch(...)
diff --git a/src/lib/time/control-impl.hpp b/src/lib/time/control-impl.hpp
index 25e96dc07..5ec429309 100644
--- a/src/lib/time/control-impl.hpp
+++ b/src/lib/time/control-impl.hpp
@@ -145,7 +145,7 @@ namespace mutation {
       void
       attach (SIG const& toNotify)
         {
-          ChangeSignal newListener (ref(toNotify));
+          ChangeSignal newListener (std::tr1::ref(toNotify));
           listeners_.push_back (newListener);
         }
       
diff --git a/src/lib/time/control-policy.hpp b/src/lib/time/control-policy.hpp
index a657fb7ff..d05bbbe62 100644
--- a/src/lib/time/control-policy.hpp
+++ b/src/lib/time/control-policy.hpp
@@ -243,7 +243,7 @@ namespace mutation {
       processValueChange (TAR& target, SRC const& change)  ///< standard case: plain value change
         {
           imposeChange (target, maybeMaterialise(change));
-          return buildChangedValue (maybeMaterialise(target));
+          return Link::buildChangedValue (maybeMaterialise(target));
         }
       
       static TI

From 2867dc1870fb25b09715a1ade274f2c510967265 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 10 Oct 2012 06:11:32 +0200
Subject: [PATCH 496/496] comment on the SemanticTags proposal

---
 doc/devel/rfc/SemanticTags.txt | 53 ++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/doc/devel/rfc/SemanticTags.txt b/doc/devel/rfc/SemanticTags.txt
index 363207705..1816d7bed 100644
--- a/doc/devel/rfc/SemanticTags.txt
+++ b/doc/devel/rfc/SemanticTags.txt
@@ -139,6 +139,59 @@ Comments
 --------
 //comments: append below
 
+//edit comment
+You may recall this proposal created some heated debate at the last developer meeting.
+After thinking it over some time, I can see now more clearly what irritated me.
+
+. for me, the proposal seems somewhat to lack focus. Right now we have some shortcomings at
+  rather basic operations when *authoring content* at the website. This proposal tends to be more
+  interested in some kind of automated content discovery.
+. the term ``tag'' in this proposal is overlayed with different meanings. For one it means an attached
+  textual property of some document, but also it denotes to some kind of inferred categorisation.
+  I'd rather propose to stick to the former meaning (which is common place) and treat the latter
+  as one _source for data_ within an categorisation algorithm. This way, such categorisation
+  sources can remain an implementation detail and don't need to be fixed in an universal way.
+. I have serious concerns against the _ontology_ part of the proposal. Not only is the syntax
+  unintuitive, but more importantly, this ontology is not well aligned with real world usage.
++
+To underpin the last diagnosis, just look at the existing tags in our Wiki:
+
+    * automation (3)
+    * Builder (20)
+    * classes (6)
+    * Concepts (9)
+    * decision (19)
+    * def (90)
+    * design (43)
+    * discuss (19)
+    * draft (55)
+    * example (3)
+    * excludeMissing (6)
+    * GuiIntegration (6)
+    * img (40)
+    * impl (36)
+    * Model (22)
+    * operational (19)
+    * overview (20)
+    * Player (12)
+    * plugin (2)
+    * Rendering (24)
+    * rewrite (3)
+    * Rules (8)
+    * SessionLogic (30)
+    * spec (76)
+    * systemConfig (9)
+    * Types (4)
+
+The absolute majority of these are neither _is-a_ nor _has-a_. The great thing with
+tags, why everyone seems to love them, is exactly that they are *not formalized*.
+You can just throw in some tags and keywords and use them for a plethora of
+unrelated and unstructured purposes and generally just assume that your
+reader will somehow ``get it''.
+
+Ichthyostega:: 'Mi 10 Okt 2012 05:36:35 CEST' ~~
+
+
 
 //endof_comments: