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/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/README b/README index 3d62663eb..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 @@ -66,8 +66,8 @@ or the local Copy of this page in the file INSTALL Debian Package -------------- -[verse] -Hermann Vosseler (aka Ichthyo) maintains a *Debian* packaging of the source tree, -which can be pulled from +git://git.lumiera.org/lumiera/debian+ -It can be built by +git-buildpackage+ +Hermann Vosseler (aka Ichthyo) maintains a *Debian* packaging of the source tree + +- the package definition can be pulled from +git://git.lumiera.org/lumiera/debian+ +- the package can be built by +git-buildpackage+ diff --git a/SConstruct b/SConstruct index 6419b5cb7..16fda2d2f 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 @@ -23,420 +23,63 @@ # 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 -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' - -localDefinitions = locals() -#-----------------------------------Configuration - - - -import os +# SCons plugins and extension modules +#------------------------------------------------ import sys +sys.path.append('./admin/scons') +#------------------------------------------------ -sys.path.append(TOOLDIR) -sys.path.append(SCRIPTDIR) + +import Setup +import Options +import Platform 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.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 ' - , 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 - 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_filesystem-mt','boost/filesystem.hpp','C++'): - problems.append('We need the boost::filesystem (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-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('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, artifacts): - """ 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, artifacts): - """ 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. - """ - - # 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) - lProc = env.SharedLibrary('lumieraproc', srcSubtree(env,'src/proc'), install=True) - - core = lLib+lApp+lBack+lProc - - 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') - + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') - ) - - # 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) - - objgui = srcSubtree(envGtk,'src/gui', appendCPP='LUMIERA_PLUGIN') - guimodule = envGtk.LumieraPlugin('gtk_gui', objgui, 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'], exports='env artifacts core') - SConscript(dirs=['tests'], exports='env artifacts core') - - - -def definePostBuildTargets(env, artifacts): - """ define further actions after the core build (e.g. Documentaion). - define alias targets to trigger the installing. - """ - build = env.Alias('build', ( artifacts['lumiera'] - + artifacts['plugins'] - + artifacts['tools'] - + artifacts['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') - env.Alias ('doc', doxydoc) - env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) - - env.Alias ('all', build+artifacts['testsuite']+doxydoc) - env.Default('build') - # SCons default target - - -def defineInstallTargets(env, artifacts): - """ 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']) - - env.Alias('install', artifacts['gui']) - env.Alias('install', '$DESTDIR') ##################################################################### +env = Setup.defineBuildEnvironment() # dirs & compiler flags +env = Platform.configure(env) # library dependencies + + + +### === MAIN BUILD === ############################################## + +# call subdir SConscript(s) to define the actual build targets... +SConscript(dirs=['data','src','src/tool','research','tests','doc'], exports='env') -### === MAIN === #################################################### +# 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' ]) -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. -# 'lumiera' : the App -# 'gui' : the GTK UI (plugin) -# 'plugins' : plugin shared lib -# 'tools' : small tool applications (e.g mpegtoc) -defineSetupTargets(env, artifacts) -defineBuildTargets(env, artifacts) -definePostBuildTargets(env, artifacts) -defineInstallTargets(env, artifacts) +### === 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.Default('build') +# SCons default target + + +env.Alias ('all', build + testsuite + doxydoc) +env.Alias ('doc', doxydoc) +env.Alias ('none') + +env.Alias('install', gui) +env.Alias('install', '$DESTDIR') + +##################################################################### diff --git a/admin/render_icon.py b/admin/render_icon.py deleted file mode 100755 index 63bacbf37..000000000 --- a/admin/render_icon.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/python -# -# render_icons.py - Icon rendering utility script -# -# 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. - -import sys -import getopt -from xml.dom import minidom -import os -import shutil - -#svgDir = "svg" -#prerenderedDir = "prerendered" -inkscapePath = "/usr/bin/inkscape" -rsvgPath = "./rsvg-convert" -artworkLayerPrefix = "artwork:" - -# -# 2/2011 some notes by Ichthyo -# The purpose of this python script is -# - 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', -# which is built during the Lumiera build process. -# -# Judging from the code and the actual SVGs, this seems to work as follows: -# The SVG contains a design to be rendered into raster images of various sizes. -# These sizes are determined by special rectangles, which act as bounding box and -# are placed on a special 'plate' layer, which is a child layer of the main -# 'artwork:' layer. The grid of the SVG is setup such as to result in pixel sizes -# suitable for icon generation. The actual size of the generated icons are then -# parsed from the height and width attributes of the mentioned bounding box -# rectangles. -# -# The parser seems to be rather simplistic; the sizes and positions need to be -# integral numbers. In one instance we had a float number in the y coordinate, -# which resulted in an invalid, zero sized output icon -# -# - - -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 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 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 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]) - height = int(rectangle[3]) - - 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], rectangle[2], rectangle[3]), - "--output=" + os.path.join(out_dir, "%gx%g/%s.png" % (rectangle[2], rectangle[3], 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 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 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" - 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) - - # Copy in prerendered icons - #copyPrerenderedIcons() - -if __name__=="__main__": - main(sys.argv[1:]) - diff --git a/admin/rfc.sh b/admin/rfc.sh index 06f050613..bbec05e0c 100755 --- a/admin/rfc.sh +++ b/admin/rfc.sh @@ -2,66 +2,90 @@ 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() { - less <<"EOF" -Script to maintain Lumiera RFC's + 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 +// 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 } @@ -183,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 @@ -208,29 +232,43 @@ function find_chapter() -function process() +function process_file() { local file="$1" local path="${1%/*}" + local basename="${1##*/}" + local linkdest="$path" local state=$(grep '^\*State\* *' "$file") case "$state" in *Final*) - if [[ "$path" != "./doc/devel/rfc" ]]; then - git mv "$file" "./doc/devel/rfc" - fi + linkdest="./doc/devel/rfc_final" ;; *Idea*|*Draft*) - if [[ "$path" != "./doc/devel/rfc_pending" ]]; then - git mv "$file" "./doc/devel/rfc_pending" - fi + linkdest="./doc/devel/rfc_pending" ;; - *Parked*|*Dropped*) - if [[ "$path" != "./doc/devel/rfc_dropped" ]]; then - git mv "$file" "./doc/devel/rfc_dropped" - fi + *Parked*) + linkdest="./doc/devel/rfc_parked" ;; + *Dropped*) + linkdest="./doc/devel/rfc_dropped" + ;; + *) + echo "Unknown State: '$state'" >&2 + exit 1 esac + + local oldpath + for oldpath in ./doc/devel/rfc_*/$basename; do :; done + + if [[ -h "$oldpath" ]]; 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 } @@ -273,7 +311,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 @@ -316,10 +354,10 @@ function change_state() local state="$2" local nl=$'\n' - local comment=".State -> $state$nl//add reason$nl $(date +%c) $(git config --get user.name) <$(git config --get user.email)>$nl" + local comment=".State -> $state$nl//add reason$nl$nl$(git config --get user.name):: '$(date +%c)' ~<$(git config --get user.email)>~$nl" edit_state "$name" "$state" "$comment" edit "$name" -4 "endof_comments" - process "$name" + process_file "$name" } @@ -329,13 +367,16 @@ 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" + process_file "$file" 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" @@ -353,22 +394,20 @@ 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 - git add "./doc/devel/rfc_pending/${name}.txt" - process "./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) name=$(unique_name "$1") if [[ "$name" ]]; then edit "${name}" 2 "$2" - git add "$name" + process_file "$name" fi ;; asciidoc) @@ -381,21 +420,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) @@ -405,32 +441,38 @@ 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) 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) find_rfc "$1" | while read file; do smart_wrap "$file" 80 + process_file "$file" done ;; smart_wrap) diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index e5d111885..673d050ba 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 @@ -49,20 +46,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)) @@ -74,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) @@ -87,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 @@ -104,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 @@ -118,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): @@ -133,38 +121,39 @@ 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):] + name = str(d) + if str(d).startswith(basePrefix): + name = name[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. """ return [env.LumieraPlugin( getDirname(tree) - , srcSubtree(env, tree, appendCPP='LUMIERA_PLUGIN') + , 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 new file mode 100755 index 000000000..9de758100 --- /dev/null +++ b/admin/scons/IconSvgRenderer.py @@ -0,0 +1,186 @@ +#!/usr/bin/python +# +# IconSvgRenderer.py - Icon rendering utility script +# +# Copyright (C) Lumiera.org +# 2008, Joel Holdsworth <joel@airwebreathe.org.uk> +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# 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 +import shutil +from xml.dom import minidom + + +rsvgPath = "./rsvg-convert" +artworkLayerPrefix = "artwork:" + +# +# 2/2011 some notes by Ichthyo +# The purpose of this python script is +# - to parse a SVG +# - to invoke Inkscape to render this SVG into a raster image (icon) +# +# 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: +# The SVG contains a design to be rendered into raster images of various sizes. +# These sizes are determined by special rectangles, which act as bounding box and +# are placed on a special 'plate' layer, which is a child layer of the main +# 'artwork:' layer. The grid of the SVG is setup such as to result in pixel sizes +# suitable for icon generation. The actual size of the generated icons are then +# parsed from the height and width attributes of the mentioned bounding box +# rectangles. +# +# The parser seems to be rather simplistic; the sizes and positions need to be +# integral numbers. In one instance we had a float number in the y coordinate, +# which resulted in an invalid, zero sized output icon +# +# + + +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 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 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]) + + 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 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" + +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) + + + + +if __name__=="__main__": + main(sys.argv[1:]) + diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index c0ce77375..8d2d8db43 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -22,11 +22,10 @@ ##################################################################### -import os from os import path -import SCons import SCons.SConf +from SCons.Action import Action from SCons.Environment import Environment from Buildhelper import * @@ -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) @@ -123,7 +129,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): @@ -151,43 +157,44 @@ 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) + 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 + """ + 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) + 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") @@ -205,8 +212,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 +298,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 +338,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 +369,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/admin/scons/Options.py b/admin/scons/Options.py new file mode 100644 index 000000000..bbccb20bd --- /dev/null +++ b/admin/scons/Options.py @@ -0,0 +1,81 @@ +# -*- python -*- +## +## Options.py - SCons build: command line options and help +## + +# 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. +##################################################################### + + +from SCons.Script import PathVariable, EnumVariable, BoolVariable, Help + + + + + +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. + """ + 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++') + ,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) + ) + + + +def prepareOptionsHelp(buildVars,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 + buildVars.GenerateHelpText(env)) + + + diff --git a/admin/scons/Platform.py b/admin/scons/Platform.py new file mode 100644 index 000000000..3aee76abd --- /dev/null +++ b/admin/scons/Platform.py @@ -0,0 +1,149 @@ +# -*- python -*- +## +## Platform.py - SCons build: platform configuration and library detection +## + +# 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. +##################################################################### + + +from SCons.Script import Exit +from Buildhelper import isCleanupOperation, isHelpRequest + + + + +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 build environment defined thus far + + # 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('alsa', '1.0.23'): + problems.append('Support for ALSA sound output is required') + + 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() + diff --git a/admin/scons/Setup.py b/admin/scons/Setup.py new file mode 100644 index 000000000..5a064381d --- /dev/null +++ b/admin/scons/Setup.py @@ -0,0 +1,137 @@ +# -*- python -*- +## +## Setup.py - SCons build: setup, definitions and compiler flags +## + +# 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. +##################################################################### + +from SCons.Script import EnsurePythonVersion, EnsureSConsVersion, Variables, Decider + +from LumieraEnvironment import * +from Buildhelper import * +import Options + + + +#-------------------------------------------------------Configuration +TARGDIR = 'target' +VERSION = '0.pre.01' +TOOLDIR = './admin/scons' # SCons plugins +OPTCACHE = 'optcache' +CUSTOPTFILE = 'custom-options' + +# these are accessible via env.path.xxxx +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 +buildSetup = Record(locals()) + + + + + +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 + """ + EnsureSConsVersion(1,0) + EnsurePythonVersion(2,4) + Decider('MD5-timestamp') # detect changed files by timestamp, then do a MD5 + + 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') + 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/:.\\"') + + 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) ) + 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 ]" + + + 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} 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/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/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 <joel@airwebreathe.org.uk> +# +# 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/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') 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/design/architecture/ArchitectureSummary.txt b/doc/design/architecture/ArchitectureSummary.txt new file mode 100644 index 000000000..4ddd3b4ba --- /dev/null +++ b/doc/design/architecture/ArchitectureSummary.txt @@ -0,0 +1,36 @@ +Lumiera Architecture: Executive Summary +======================================= +:Author: Hermann Voßeler +: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 +``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/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: <Ichthyostega@web.de> +: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: <Ichthyostega@web.de> +: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/design/architecture/index.txt b/doc/design/architecture/index.txt new file mode 100644 index 000000000..b061aa74c --- /dev/null +++ b/doc/design/architecture/index.txt @@ -0,0 +1,20 @@ +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. + +.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..2d76f716e --- /dev/null +++ b/doc/design/architecture/playRender.txt @@ -0,0 +1,191 @@ +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 +------------------------- + +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] + 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 + + +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_''. +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 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 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 in sync*. The parameters of delivery 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 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 +- 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. + + + +Conclusions for the Design +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Based on these observations, the following design looks pretty much obvious: + +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, 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*: 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: + +- 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 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 +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 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 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 +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. + +Thus there are three fundamental guidelines when it comes to rounding + +. 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 +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 technicalities, + ``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 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. + Most of all, it violates the subsidiarity principle. + +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 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/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/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/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 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/devel/rfc.txt b/doc/devel/design_process.txt similarity index 80% rename from doc/devel/rfc.txt rename to doc/devel/design_process.txt index 06873b7db..df982d575 100644 --- a/doc/devel/rfc.txt +++ b/doc/devel/design_process.txt @@ -2,16 +2,20 @@ 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' +//Menu: put child 'rfc_dropped' after 'rfc_final' +//Menu: attach child 'rfc' + 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, +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. @@ -37,6 +41,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]. 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/images/lumiera_big_graph.png b/doc/devel/images/lumiera_big_graph.png new file mode 100644 index 000000000..ec08d77fd Binary files /dev/null and b/doc/devel/images/lumiera_big_graph.png differ diff --git a/doc/devel/images/uml.time-entities.png b/doc/devel/images/uml.time-entities.png new file mode 100644 index 000000000..91c795876 Binary files /dev/null and b/doc/devel/images/uml.time-entities.png differ 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-11-12.txt b/doc/devel/meeting_summary/2008-11-12.txt new file mode 100644 index 000000000..c33fc3c74 --- /dev/null +++ b/doc/devel/meeting_summary/2008-11-12.txt @@ -0,0 +1,785 @@ +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 +---------------------------------- +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 + + + +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. + +_joelholdsworth_ points out that configurability can't replace real GUI and workflow design. +He quotes from a recent Blender GUI discussion + +[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 + + +.-- Discussion about foreseeable problems with GUI Plugins -- +[caption="☉Transcript☉ "] +---------------------------- +[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 + +[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: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:07:51] * cehteh thinks about some canvas widget which the plugin can use to draw stuff there +[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: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:27] <ichthyo> but it is manageable +[2008-11-12 22:09:38] <ichthyo> LADSPA is a prominent example +[2008-11-12 22:09:55] <ichthyo> then, the problem in the GUI is how to arrange those controls +[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:11:00] <joelholdsworth> that hit the UI how to lay out out +[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: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" + 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: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: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: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:59] <joelholdsworth> UI design? well that's what presentation hints help with +[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:18:11] <ichthyo> for example: you use a sound panner plugin + 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: + 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:22:20] <ichthyo> ...which creates sort of a cascading scheme +[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 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" +[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: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, 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: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: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: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: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> 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: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:04] <joelholdsworth> yes again that would work via an auxialiry controller setup config + 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: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 + 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:28:45] <joelholdsworth> I see the problem +[2008-11-12 23:28:58] <ichthyo> ok thanks + +[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 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: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, 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 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: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: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. 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. 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" + 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 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:58] <ichthyo> but it is just a ABC +[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: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: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, 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: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:55] <ichthyo> put them in a clearly separated namespace +[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: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. + 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'd rather use the name include +[2008-11-13 00:17:59] <ichthyo> so src/lumiera/** -> /usr/include/lumiera/** +[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: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:21:04] <ichthyo> so namespace lumiera:: -> /src/include +[2008-11-13 00:21:35] <ichthyo> namespace lumiera::edit -> /src/include/edit (just as an example) + +[2008-11-13 00:22:00] <ichthyo> while namespace proc::builder -> src/proc/builder + +[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:45] <ichthyo> and when an interface dir as 60 entries it starts to get messy +[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 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. 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:59] <ichthyo> probably those just count as "reasonable exceptions from the rule" +[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: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: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 + 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 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: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" 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? + 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:11] <cehteh> and a tree there +[2008-11-13 00:48:22] <cehteh> plugins/video/effects +[2008-11-13 00:49:56] <cehteh> plugins/video/effects/foo/ ... with foobar.c foobaz.c links to foo.lum +[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: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 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: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: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: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: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: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:20] <ichthyo> the rest is fully automatic + +[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: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:01] <cehteh> -DLUMIERA_PLUGIN for compiling plugins +[2008-11-13 01:00:11] <ichthyo> plus libdl +[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: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 + 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:52] <cehteh> huh i feel comfortable with autotools +[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" + 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" +[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: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:14:00] <ichthyo> so the build process doesn't need to build anything in admin +[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:15] <joelholdsworth> it's the standard rsvg utility with some options added +[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:59] <joelholdsworth> but yes that makes perfect snese +[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 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: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 temporaries +[2008-11-13 01:20:01] <ichthyo> ok +[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: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: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:27:24] <ichthyo> btw: do we need "test plugins"? +[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: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:54] <ichthyo> just it will be only used for test runs +[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: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 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: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 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 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 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: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:15] <cehteh> so now my solution: +[2008-11-13 01:46:01] <cehteh> *thinking* +[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: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: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? +[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: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: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 + 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/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/2011-04-13.txt b/doc/devel/meeting_summary/2011-04-13.txt new file mode 100644 index 000000000..77b794746 --- /dev/null +++ b/doc/devel/meeting_summary/2011-04-13.txt @@ -0,0 +1,642 @@ +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 +----------------------- +_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. + +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. + +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: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: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 +--------------------- + + +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? +[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 +--------------------- + + +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 +[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 +--------------------- + + + + +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. +[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 +--------------------- + +Conclusion +~~~~~~~~~~ + + * 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 + + + + + +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: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: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:44] <ichthyo> either really discuss something and then decide, or just leave it in draft +[2011-04-14 00:54:05] <ichthyo> lets just postpone the application install and leave it in draft! +--------------------- + + + +Delectus Shot Evaluator +~~~~~~~~~~~~~~~~~~~~~~~ +Agreement to _park_ it until someone else comes up to advance this topic further. + +Clip Cataloging System +~~~~~~~~~~~~~~~~~~~~~~ +similarily to _park_ until someone cares.... + + +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 +[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 +--------------------- + + + + +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? +[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 +--------------------- + + +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 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 +--------------------- + + + + +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 +[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 +--------------------- + + +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 +[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 +--------------------- + +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. + + +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. + +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 +[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 +--------------------- + + +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 +[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 +--------------------- + + +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 +[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 +--------------------- + + +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. + +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. + +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 + + 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..8b3f1cc69 --- /dev/null +++ b/doc/devel/meeting_summary/2011-05-11.txt @@ -0,0 +1,677 @@ +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 + * daylife + +_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 ... + +- 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 +~~~~~~~~~~ +_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 + + +'''' + + +.-- 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: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: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 +[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 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: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 +---------------------------- + +.-- 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: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: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: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 +---------------------------- + +.-- 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: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: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: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: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' +[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 -- 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: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: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. 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 +[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 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: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 +[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: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. 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: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: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: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: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 +[2011-05-11 23:21:23] <ichthyo> cehteh: AFAIK, the jack buffers come from the jack server +[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: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: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 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 +[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: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 +[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 + 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 +[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 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: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, + 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)? +[2011-05-11 23:45:34] <cehteh> waiting on other jobs +[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:49] <cehteh> if it turns out we need a yield i could implement it .. but not prematurely +[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 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: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:38] <cehteh> the next question is .. do we release this waiting jobs by some signal or by a job termination? +[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:51] <cehteh> for simplicity and a first implementation yes i think that suffices +[2011-05-11 23:55:59] * ichthyo thinks the same +---------------------------- + +.-- 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: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: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: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. 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: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: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: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 +[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: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 -- 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. 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:56] <ichthyo> in GTK, you invoke so called "signals" + 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: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: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 -- 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: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: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: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: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 + 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: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: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 +---------------------------- + +.-- 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) +[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: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: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, 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: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: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: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: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 +[2011-05-12 01:34:40] <ichthyo> at least +[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: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: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 +[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: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: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: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 +---------------------------- + + 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..e128bbc0d --- /dev/null +++ b/doc/devel/meeting_summary/2012-01-11.txt @@ -0,0 +1,37 @@ +2012-01-11 Lumiera Developers Meeting +===================================== +:Author: cehteh +:Date: 2012-01-11 + +Jan 11, 2011 on #lumiera 20:00 - 22:30 + +__Participants__ + + * cehteh + * ichthyo + * benn + * raffa + +Conclusions +----------- + +. _ichthyo_ removed most of the TiddlyWikis, and worked the content into the website +. _cehteh_ reports that Lumiera got another donation (75 €), 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 anonymous and will set up + a wiki page listing donations and expenses. +. _ichthyo_ reworked 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. +. _benny_ and _ichthyo_ follow up on the + link:http://libregraphicsmag.com/[Libre Graphics Magazine]'s offer to promote + graphics and media projects and try to get into discussion with them and see + what can be done within our limited time. +. 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 -- + it mostly boils down to support ffmpeg -- besides we'll investigate simple + alternatives for basic output. diff --git a/doc/devel/meeting_summary/index.txt b/doc/devel/meeting_summary/index.txt new file mode 100644 index 000000000..06b15df36 --- /dev/null +++ b/doc/devel/meeting_summary/index.txt @@ -0,0 +1,373 @@ +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. + + +************************ +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 +----------- + +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. + +- 9 Nov 2011 +- 12 Oct 2011 +- 14 Sep 2011 +- 10 Aug 2011 +- 13 Jul 2011 +- 8 Jun 2011 +************************ + +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 -- scrolling -- colours + + * Website licensing and legal questions + * Trac spam + * Go through Ideas and Drafts in design process ++ +accepted: ForwardIterator, HighLevelModel, Placement concept, Session structure + +Summary +^^^^^^^ + * link:2011-04-13.html[Summary (written by Hermann Voßeler)] + + + + +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 +^^^^^^^ + * link:2008-11-12.html[Transcript (supplemented by Ichthyo in 2011)] + + + +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)] + 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 ++++++++++++++++++++++++++++++++++++++ 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 <prg@ichthyostega.de> +------------------------------------- + +[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 <prg@ichthyostega.de> + + +//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/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/devel/rfc_pending/ClipCatalogingSystem.txt b/doc/devel/rfc/ClipCatalogingSystem.txt similarity index 96% rename from doc/devel/rfc_pending/ClipCatalogingSystem.txt rename to doc/devel/rfc/ClipCatalogingSystem.txt index 8d9ca5fa5..8149c23ac 100644 --- a/doc/devel/rfc_pending/ClipCatalogingSystem.txt +++ b/doc/devel/rfc/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/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/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_pending/DelectusShotEvaluator.txt b/doc/devel/rfc/DelectusShotEvaluator.txt similarity index 99% rename from doc/devel/rfc_pending/DelectusShotEvaluator.txt rename to doc/devel/rfc/DelectusShotEvaluator.txt index fa8d2df12..0d5b55a88 100644 --- a/doc/devel/rfc_pending/DelectusShotEvaluator.txt +++ b/doc/devel/rfc/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/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_pending/DesignRenderNodesInterface.txt b/doc/devel/rfc/DesignRenderNodesInterface.txt similarity index 95% rename from doc/devel/rfc_pending/DesignRenderNodesInterface.txt rename to doc/devel/rfc/DesignRenderNodesInterface.txt index 19bae98ff..201c34a72 100644 --- a/doc/devel/rfc_pending/DesignRenderNodesInterface.txt +++ b/doc/devel/rfc/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/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 <ct@pipapo.org> +------------------------------------- + +[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 <prg@ichthyostega.de> + + +//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 <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. + +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 <prg@ichthyostega.de> + + +//endof_comments: + +'''' +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/FeatureBundle_PluggableModules.txt similarity index 98% rename from doc/devel/rfc_pending/FeatureBundle_PluggableModules.txt rename to doc/devel/rfc/FeatureBundle_PluggableModules.txt index 7124b01b1..06b9fe814 100644 --- a/doc/devel/rfc_pending/FeatureBundle_PluggableModules.txt +++ b/doc/devel/rfc/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/GitCommitMessageFormat.txt b/doc/devel/rfc/GitCommitMessageFormat.txt new file mode 100644 index 000000000..3446ff87d --- /dev/null +++ b/doc/devel/rfc/GitCommitMessageFormat.txt @@ -0,0 +1,208 @@ +Git Commit Message Format +========================= + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Final_ +*Date* _Fr 31 Aug 2012 03:54:14 CEST_ +*Proposed by* Christian Thaeter <ct@pipapo.org> +------------------------------------- + +******************************************************************************** +.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. + +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 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 + 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. 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 + (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. + +'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. + +'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. + +'ALPHA:':: + Notifies the CI system that this commit defines an ALPHA release, the CI + may take this and build an package accordingly + +'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 + + +Note: This list will be updated as need arises + + +.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 + +.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' ~<ct@pipapo.org>~ + + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_dropped/GitSubmoduleTransistion.txt b/doc/devel/rfc/GitSubmoduleTransistion.txt similarity index 100% rename from doc/devel/rfc_dropped/GitSubmoduleTransistion.txt rename to doc/devel/rfc/GitSubmoduleTransistion.txt diff --git a/doc/devel/rfc_pending/LumieraForwardIterator.txt b/doc/devel/rfc/LumieraForwardIterator.txt similarity index 80% rename from doc/devel/rfc_pending/LumieraForwardIterator.txt rename to doc/devel/rfc/LumieraForwardIterator.txt index 5994865bd..5bb298127 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[] ------------------------------------- @@ -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,36 @@ 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' + +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: + + +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/MakeSconsTheOfficialBuildSystem.txt b/doc/devel/rfc/MakeSconsTheOfficialBuildSystem.txt new file mode 100644 index 000000000..61be3df3c --- /dev/null +++ b/doc/devel/rfc/MakeSconsTheOfficialBuildSystem.txt @@ -0,0 +1,88 @@ +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 <ct@pipapo.org> +------------------------------------- + +******************************************************************************** +.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 on the release 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: + +result of discussions and evaluation during the last years + + +Comments +-------- +//comments: append below + +.State -> Final +//add reason +Decided on the December 2011 Developer meeting. + +Christian Thaeter:: 'Wed 11 Jan 2012 22:28:36 CET' ~<ct@pipapo.org>~ + +//endof_comments: + +'''' +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/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_pending/NormalizedDeviceCoordinates.txt b/doc/devel/rfc/NormalizedDeviceCoordinates.txt similarity index 95% rename from doc/devel/rfc_pending/NormalizedDeviceCoordinates.txt rename to doc/devel/rfc/NormalizedDeviceCoordinates.txt index e5bbd7cdb..b594cc3b2 100644 --- a/doc/devel/rfc_pending/NormalizedDeviceCoordinates.txt +++ b/doc/devel/rfc/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/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_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/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 <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] diff --git a/doc/devel/rfc_pending/RenderOptimizer.txt b/doc/devel/rfc/RenderOptimizer.txt similarity index 92% rename from doc/devel/rfc_pending/RenderOptimizer.txt rename to doc/devel/rfc/RenderOptimizer.txt index 956aa89a4..74604e77e 100644 --- a/doc/devel/rfc_pending/RenderOptimizer.txt +++ b/doc/devel/rfc/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/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 <ct@pipapo.org> +------------------------------------- + +[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 <ct@pipapo.org> +------------------------------------- + +[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/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/SemanticTags.txt b/doc/devel/rfc/SemanticTags.txt new file mode 100644 index 000000000..1816d7bed --- /dev/null +++ b/doc/devel/rfc/SemanticTags.txt @@ -0,0 +1,199 @@ +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 <ct@pipapo.org> +------------------------------------- + +******************************************************************************** +.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 + +//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' ~<prg@ichthyostega.de>~ + + + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_dropped/SkillsCollection.txt b/doc/devel/rfc/SkillsCollection.txt similarity index 100% rename from doc/devel/rfc_dropped/SkillsCollection.txt rename to doc/devel/rfc/SkillsCollection.txt 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/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 <prg@ichthyostega.de> +------------------------------------- + +******************************************************************************** +.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/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_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 79% rename from doc/devel/rfc_pending/TimelineSequenceOutput.txt rename to doc/devel/rfc/TimelineSequenceOutput.txt index d94071cb8..b1f043a1d 100644 --- a/doc/devel/rfc_pending/TimelineSequenceOutput.txt +++ b/doc/devel/rfc/TimelineSequenceOutput.txt @@ -1,56 +1,63 @@ [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Final_ *Date* _2008-11-03_ *Proposed by* link:Ichthyostega[] ------------------------------------- +.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 @@ -266,5 +273,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_dropped/TodoLists.txt b/doc/devel/rfc/TodoLists.txt similarity index 100% rename from doc/devel/rfc_dropped/TodoLists.txt rename to doc/devel/rfc/TodoLists.txt 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_pending/UseCases.txt b/doc/devel/rfc/UseCases.txt similarity index 81% rename from doc/devel/rfc_pending/UseCases.txt rename to doc/devel/rfc/UseCases.txt index 423263e87..7f1b96d5c 100644 --- a/doc/devel/rfc_pending/UseCases.txt +++ b/doc/devel/rfc/UseCases.txt @@ -1,6 +1,6 @@ [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Parked_ *Date* _2008-10-31_ *Proposed by* link:Ichthyostega[] ------------------------------------- @@ -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,14 +282,29 @@ 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 +~~~~~~ +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/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..450ff27b6 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,11 @@ Comments -------- //comments: append below +.State -> Final +considered common practice + + Do 14 Apr 2011 03:46:07 CEST Christian Thaeter <ct@pipapo.org> + //endof_comments: diff --git a/doc/devel/rfc/WebsiteNavigation.txt b/doc/devel/rfc/WebsiteNavigation.txt new file mode 100644 index 000000000..144c41a79 --- /dev/null +++ b/doc/devel/rfc/WebsiteNavigation.txt @@ -0,0 +1,153 @@ +WebsiteNavigation +================= + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Draft_ +*Date* _Mi 08 Dez 2010 11:32:32 CET_ +*Proposed by* Ichthyostega <prg@ichthyostega.de> +------------------------------------- + +[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 +~~~~~ + * 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. + + + +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: + + + + +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' ~<prg@ichthyostega.de>~ + + +//endof_comments: diff --git a/doc/devel/rfc/WebsiteSupportMarkup.txt b/doc/devel/rfc/WebsiteSupportMarkup.txt new file mode 100644 index 000000000..2b4c6ad28 --- /dev/null +++ b/doc/devel/rfc/WebsiteSupportMarkup.txt @@ -0,0 +1,245 @@ +WebsiteSupportMarkup +==================== + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Idea_ +*Date* _Sa 06 Okt 2012 16:47:44 CEST_ +*Proposed by* Ichthyostega <prg@ichthyostega.de> +------------------------------------- + +******************************************************************************** +.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 + +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' ~<prg@ichthyostega.de>~ + + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc/index.txt b/doc/devel/rfc/index.txt index c9e8a4515..7c0ca2c92 100644 --- a/doc/devel/rfc/index.txt +++ b/doc/devel/rfc/index.txt @@ -1,11 +1,19 @@ -Accepted Design Proposals -========================= +Design Proposal Pool +==================== -//Menu: label accepted +//Menu: label (all) //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 pool of all RfC Design Proposals. + +Please use the navigation menu to access individual entries. +++++ +<br/><br/><br/> +++++ +'''' +[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)# + 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_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_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/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/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/GitCommitMessageFormat.txt b/doc/devel/rfc_final/GitCommitMessageFormat.txt new file mode 120000 index 000000000..1de41b3f6 --- /dev/null +++ b/doc/devel/rfc_final/GitCommitMessageFormat.txt @@ -0,0 +1 @@ +../rfc/GitCommitMessageFormat.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_final/index.txt b/doc/devel/rfc_final/index.txt new file mode 100644 index 000000000..a29cff26d --- /dev/null +++ b/doc/devel/rfc_final/index.txt @@ -0,0 +1,13 @@ +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, modified and finally agreed upon +during some developers meeting in the past. + +So they represent design decisions taken and can be considered binding. + 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/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 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 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 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 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 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 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 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 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 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_parked/index.txt b/doc/devel/rfc_parked/index.txt new file mode 100644 index 000000000..e975a1af2 --- /dev/null +++ b/doc/devel/rfc_parked/index.txt @@ -0,0 +1,19 @@ +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. + +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. + + + + 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 <prg@ichthyostega.de> -------------------------------------- - -[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 <prg@ichthyostega.de> - - -//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 <ct@pipapo.org> -------------------------------------- - -[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 7e89c4a9c..000000000 --- a/doc/devel/rfc_pending/EngineInterfaceOverview.txt +++ /dev/null @@ -1,245 +0,0 @@ -[grid="all"] -`------------`----------------------- -*State* _Idea_ -*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 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) when 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 - * 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 - - -Discussion -~~~~~~~~~~ - -Pros/Cons/Alternatives -^^^^^^^^^^^^^^^^^^^^^^ -Possible variants to consider.... - - -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 - - - - - -Comments --------- - -//////////////////// - -Conclusion -~~~~~~~~~~ -* *accepted* / *dropped* by MMMMMM.YYYY developer meeting. - -//////////////////// - - -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 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 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 <ct@pipapo.org> -------------------------------------- - -[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 <ct@pipapo.org> -------------------------------------- - -[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/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 diff --git a/doc/devel/rfc_pending/StreamTypeSystem.txt b/doc/devel/rfc_pending/StreamTypeSystem.txt deleted file mode 100644 index 2b6adbc06..000000000 --- a/doc/devel/rfc_pending/StreamTypeSystem.txt +++ /dev/null @@ -1,234 +0,0 @@ -[grid="all"] -`------------`----------------------- -*State* _Idea_ -*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. + -This is the foundation to be able to define what can be connected and to -separate out generic parts and isolate specific parts. - - - -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 notriously - 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 a elementary stream, which can't be further separated _in - the given context_ - * 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. - * 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 -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 - 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 - 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 Facade; 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 -"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 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 -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 Facade consists of coming up with a generic - scheme to decide this question for media streams implemented by this - library. - - -Tasks -^^^^^ - * draft the interfaces ([green]#✔ done#) - * define a fallback 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# - * evaluate if it's a good idea to handle (still) images as a separate distinct - kind of media - - - - -Alternatives -^^^^^^^^^^^^ -Instead of representing types my 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 -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 -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. - -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 - - * 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. - - - - - -Comments --------- - -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 - -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/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 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 <prg@ichthyostega.de> -------------------------------------- - -[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 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 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] diff --git a/doc/devel/template/YYYY-MM-DD-developerMeetingSummary.txt b/doc/devel/template/YYYY-MM-DD-developerMeetingSummary.txt index 64d72138f..0b4b4a8ee 100644 --- a/doc/devel/template/YYYY-MM-DD-developerMeetingSummary.txt +++ b/doc/devel/template/YYYY-MM-DD-developerMeetingSummary.txt @@ -11,13 +11,13 @@ __Participants__ * ichthyo * ... -_Protocol written by XXXXX_ +_Summary 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 diff --git a/doc/devel/template/new_rfc.sh b/doc/devel/template/new_rfc.sh index a08ac3d06..a78af057b 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 @@ -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 diff --git a/doc/devel/uml/fig130309.png b/doc/devel/uml/fig130309.png index d63c8432b..6afb6d279 100644 Binary files a/doc/devel/uml/fig130309.png and b/doc/devel/uml/fig130309.png differ diff --git a/doc/devel/uml/fig130437.png b/doc/devel/uml/fig130437.png index e12e4b48b..925723480 100644 Binary files a/doc/devel/uml/fig130437.png and b/doc/devel/uml/fig130437.png differ diff --git a/doc/devel/uml/fig131205.png b/doc/devel/uml/fig131205.png index 17826d19b..5d46c3249 100644 Binary files a/doc/devel/uml/fig131205.png and b/doc/devel/uml/fig131205.png differ diff --git a/doc/devel/uml/fig142725.png b/doc/devel/uml/fig142725.png index 6e8c570d8..91c795876 100644 Binary files a/doc/devel/uml/fig142725.png and b/doc/devel/uml/fig142725.png differ diff --git a/doc/devel/uml/fig143877.png b/doc/devel/uml/fig143877.png new file mode 100644 index 000000000..1e628bc41 Binary files /dev/null and b/doc/devel/uml/fig143877.png differ diff --git a/doc/devel/uml/fig144005.png b/doc/devel/uml/fig144005.png new file mode 100644 index 000000000..6ca73fc49 Binary files /dev/null and b/doc/devel/uml/fig144005.png differ diff --git a/doc/devel/uml/fig145157.png b/doc/devel/uml/fig145157.png new file mode 100644 index 000000000..dac8e23cb Binary files /dev/null and b/doc/devel/uml/fig145157.png differ diff --git a/doc/devel/uml/fig151685.png b/doc/devel/uml/fig151685.png new file mode 100644 index 000000000..77b41e446 Binary files /dev/null and b/doc/devel/uml/fig151685.png differ diff --git a/doc/index.txt b/doc/index.txt index ad0c5d3ee..268479de4 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -1,23 +1,41 @@ -== Documentation += Documentation = This documentation section contains documentation for both users and developers. -=== User +== 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 : +== Developer == +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] 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. +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 == + +This section holds documents and materials used to present and promote Lumiera, +articles, summaries and whitepapers + -> link:/media/index.html[Index] -=== Presentation documents -Documents that you can use as you wish to present or promote Lumiera : - * link:/media/index.html[Whitepapers and Presentations] 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/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 +} +-------------- + + 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/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/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+ <TARGET> 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 a9b3261da..41a7dd76d 100644 --- a/doc/technical/build/index.txt +++ b/doc/technical/build/index.txt @@ -3,13 +3,17 @@ 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 -* SCons -* Autotools -* Dependencies +* link:SCons.html[Buildsystem] +* link:Dependencies.html[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/code/codingGuidelines.txt b/doc/technical/code/codingGuidelines.txt new file mode 100644 index 000000000..1a0aeb89e --- /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 highlight the presence of a nesting. + +Naming conventions +~~~~~~~~~~~~~~~~~~ +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. + +- 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 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!) +- 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/code/darkCorners.txt b/doc/technical/code/darkCorners.txt new file mode 100644 index 000000000..03bd9311c --- /dev/null +++ b/doc/technical/code/darkCorners.txt @@ -0,0 +1,54 @@ +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. + + +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. + 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 + + 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 + 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/UsingBoost.txt b/doc/technical/howto/UsingBoost.txt new file mode 100644 index 000000000..5b3bab017 --- /dev/null +++ b/doc/technical/howto/UsingBoost.txt @@ -0,0 +1,167 @@ +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 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 +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) + 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] + diff --git a/doc/technical/index.txt b/doc/technical/index.txt index 147751f86..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 @@ -29,7 +30,9 @@ 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 +* 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/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 new file mode 100644 index 000000000..8573e3803 --- /dev/null +++ b/doc/technical/infra/debianDepot.txt @@ -0,0 +1,128 @@ +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 + + +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 + +everyday usage +~~~~~~~~~~~~~~ + +import a package:: + +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 'squeeze' repository, into the 'experimental' section + +dump out an entire repository:: + +reprepro -V export squeeze+ ++ +this will __re__generate all of the indices, signatures and metadata of the 'squeeze' repository + + +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/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. 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. +- 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 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/technical/infra/index.txt b/doc/technical/infra/index.txt index f1c903af9..a47abbf95 100644 --- a/doc/technical/infra/index.txt +++ b/doc/technical/infra/index.txt @@ -8,6 +8,64 @@ 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 +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 + diff --git a/doc/technical/infra/rfcsh.txt b/doc/technical/infra/rfcsh.txt new file mode 100644 index 000000000..a75fb3640 --- /dev/null +++ b/doc/technical/infra/rfcsh.txt @@ -0,0 +1,69 @@ +Lumiera RFC maintenance script +============================== + +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. + 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). + + + + 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 5688d9dad..919d365c8 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. @@ -92,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 @@ -157,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 @@ -182,9 +252,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 @@ -199,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. @@ -291,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_ @@ -308,17 +427,40 @@ Lua Scripting _tbw_ + 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 @@ -328,15 +470,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 ~~~~~~ @@ -425,7 +632,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 ~~~~~~~~~~~~~~~~~~ @@ -437,27 +670,162 @@ _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 +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +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 +in 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 -- 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 +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 +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 ~~~~~~~~~~~~~~~~~~ @@ -477,16 +845,42 @@ 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 ~~~~~~~~~~ -_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 @@ -525,6 +919,11 @@ Functor Utils ^^^^^^^^^^^^^ _tbw_ +Duck Typing +^^^^^^^^^^^ +_tbw_ + + Preprocessor Metaprogramming ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -546,7 +945,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...) 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/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] diff --git a/doc/user/intro/Glossary.txt b/doc/user/intro/Glossary.txt index 9f5f7c2ea..cd2ba0636 100644 --- a/doc/user/intro/Glossary.txt +++ b/doc/user/intro/Glossary.txt @@ -2,15 +2,142 @@ 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:: + Windows displaying and managing various things that can be edited, + 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 + 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 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 + 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 an extra keyboard, midi mixer, Jog, .. + 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 + 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 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 + slots targetting these. + + anchor:OutputMapping[] link:#OutputMapping[->]OutputMapping:: + 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 + of data to be output. + + anchor:Pipe[] link:#Pipe[->]Pipe:: + 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. 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) + + 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 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:: + 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:: + 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 + 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). 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 @@ -18,6 +145,51 @@ explanations, the long explanation is the topic of the document above..' 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,185 +210,16 @@ 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 + 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 - 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) - - 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 + 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. diff --git a/doc/user/intro/intro.txt b/doc/user/intro/intro.txt old mode 100644 new mode 100755 index 1bc008b7c..6e77f2523 --- a/doc/user/intro/intro.txt +++ b/doc/user/intro/intro.txt @@ -1,36 +1,34 @@ Lumiera (as seen) from Outer Space ================================== -:Author: Christian Thäter ct@pipapo.org +: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 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 start working with Lumiera quickly. ****************************************************************************** - -About this Document -------------------- - // all things starting with '//' are asciidoc comments and drafts/notes while // working on 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. +.About this Document +// 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, then develop details towards the end. Vision @@ -38,59 +36,72 @@ 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 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, 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 +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. - - 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. + 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 this crisp quality without - compromise throughout you 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 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:: + 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. + in different dimensions and use available resources as best it + 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 + 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. -< + 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 -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. @@ -102,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]] @@ -119,7 +134,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 @@ -135,24 +150,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 @@ -162,53 +177,51 @@ 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, -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. Viewer ~~~~~~ -// 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 ~~~~~~~~~~~~~~~~~~ -// 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 +233,26 @@ Transport Controls Timeline View ~~~~~~~~~~~~~ -// hierarchical tracks, not just a stack +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)? @@ -241,19 +268,34 @@ 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 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. -// 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 +Plugins are also managed in the Asset View. + + + +Manages all assets available in one project. + * source media/footage/soundfiles + + * 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,19 +314,27 @@ 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... +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 hud, i.e., +programers, etc. -Now lets take a look under the hood. Lumiera +Most of the material in this section is to be found in the proc layer and in the +backend. Session storage ~~~~~~~~~~~~~~~ +[red]#to be written# + + +//databank with logging, no data loss. // not generateable data // its the timeline mostly @@ -314,15 +364,42 @@ 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# +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. -I/O Subsystem -~~~~~~~~~~~~~ // file handling // vault, work, cache // repositories @@ -331,20 +408,74 @@ I/O Subsystem Configuration ~~~~~~~~~~~~~ +[red]#to be written# + // configuration system // serves defaults, actual data are stored in the session Plugins/Interfaces ~~~~~~~~~~~~~~~~~~ -// 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. + +How are Plugins Implemented? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Rendering Video --------------- +[red]#to be written# // describe the flow of data to render a frame @@ -354,6 +485,7 @@ Rendering Video Pulling a Frame ~~~~~~~~~~~~~~~ +[red]#to be written# // special cases, // case studies, @@ -372,45 +504,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> diff --git a/doc/user/tutorials/DebianBuilding.txt b/doc/user/tutorials/DebianBuilding.txt new file mode 100644 index 000000000..8c249b667 --- /dev/null +++ b/doc/user/tutorials/DebianBuilding.txt @@ -0,0 +1,198 @@ +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 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. + + +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/ squeeze 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, 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 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. + + + 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] 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 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] 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 <joel@airwebreathe.org.uk> -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the 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/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..cde733bf9 --- /dev/null +++ b/research/SConscript @@ -0,0 +1,22 @@ +# -*- python -*- +## +## SConscript - SCons buildscript for experiments and investigations. +## Things defined here usuall won't be installed +## + +Import('env core support_lib') + + + +envR = env.Clone() +# envR.Append(CCFLAGS=' -O3 ') + +# build additional test and administrative tools.... +experiments = [ envR.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/research/try.cpp b/research/try.cpp new file mode 100644 index 000000000..d03b9d2da --- /dev/null +++ b/research/try.cpp @@ -0,0 +1,165 @@ +/* try.cpp - for trying out some language features.... + * scons will create the binary bin/try + * + */ + +// 8/07 - how to control NOBUG?? +// execute with NOBUG_LOG='ttt:TRACE' bin/try +// 1/08 - working out a static initialisation problem for Visitor (Tag creation) +// 1/08 - check 64bit longs +// 4/08 - comparison operators on shared_ptr<Asset> +// 4/08 - conversions on the value_type used for boost::any +// 5/08 - how to guard a downcasting access, so it is compiled in only if the involved types are convertible +// 7/08 - combining partial specialisation and subclasses +// 10/8 - abusing the STL containers to hold noncopyable values +// 6/09 - investigating how to build a mixin template providing an operator bool() +// 12/9 - tracking down a strange "warning: type qualifiers ignored on function return type" +// 1/10 - can we determine at compile time the presence of a certain function (for duck-typing)? +// 4/10 - pretty printing STL containers with python enabled GDB? +// 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? +// 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? + + +/** @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> + +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; + + +namespace lib { +namespace meta{ +namespace func{ + + +template<typename SIG, uint num> +struct _PupS + { + 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; + + 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: + 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; +// } + }; + + + + + +int +main (int, char**) + { + Something some(23); + Something::FunP fup = some.getBinding(); + + fup ('a'); + + cout << "\n.gulp.\n"; + + return 0; + } 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/SConscript b/src/SConscript new file mode 100644 index 000000000..de4d03a91 --- /dev/null +++ b/src/SConscript @@ -0,0 +1,48 @@ +# -*- 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 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) +lBack = env.SharedLibrary('lumierabackend', srcSubtree('backend'),install=True) +lProc = env.SharedLibrary('lumieraproc', srcSubtree('proc'), install=True) + +core = lLib+lApp+lBack+lProc +core_lib = core +support_lib = lLib + +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 + + +# 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) +gui = ( guimodule + + icons + + [env.GuiResource(f) for f in env.Glob('gui/*.rc')] + ) + + +Export('lumiera core core_lib support_lib plugins gui') 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/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/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/proc/engine/buffhandle.cpp b/src/backend/engine/scheduler-frontend.cpp similarity index 75% rename from src/proc/engine/buffhandle.cpp rename to src/backend/engine/scheduler-frontend.cpp index 24573990e..3cf1698ab 100644 --- a/src/proc/engine/buffhandle.cpp +++ b/src/backend/engine/scheduler-frontend.cpp @@ -1,8 +1,8 @@ /* - BuffHandle - Buffer handling support for the render engine + 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,12 +21,14 @@ * *****************************************************/ -#include "proc/engine/buffhandle.hpp" +#include "backend/engine/scheduler-frontend.hpp" +namespace backend{ namespace engine { - - - /** */ - - -} // namespace engine + + + /** */ + + + +}} // namespace backend::engine diff --git a/src/backend/engine/scheduler-frontend.hpp b/src/backend/engine/scheduler-frontend.hpp new file mode 100644 index 000000000..887435191 --- /dev/null +++ b/src/backend/engine/scheduler-frontend.hpp @@ -0,0 +1,55 @@ +/* + SCHEDULER-FRONTEND.hpp - access point to the scheduler within the renderengine + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef BACKEND_ENGINE_SCHEDULER_FRONTEND_H +#define BACKEND_ENGINE_SCHEDULER_FRONTEND_H + + + +//using std::list; + + +namespace backend{ +namespace engine { + + + /** + * @todo this is planned to become the frontend + * to the render node network, which can be considered + * at the lower end of the middle layer; the actual + * render operations are mostly implemented by the backend + * ////////TODO WIP as of 12/2010 + */ + class SchedulerFrontend + { + public: + ///// TODO: find out about the public operations + // note: the play controller lives in the proc-layer, + // but is a subsystem separate of the sesison. + + private: + + }; + +}} // namespace backend::engine +#endif diff --git a/src/backend/mediaaccessfacade.cpp b/src/backend/media-access-facade.cpp similarity index 79% rename from src/backend/mediaaccessfacade.cpp rename to src/backend/media-access-facade.cpp index e9feab29f..a04cd76f7 100644 --- a/src/backend/mediaaccessfacade.cpp +++ b/src/backend/media-access-facade.cpp @@ -21,37 +21,33 @@ * *****************************************************/ -#include "backend/mediaaccessfacade.hpp" +#include "backend/media-access-facade.hpp" #include "lib/util.hpp" 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/media-access-facade.hpp similarity index 70% rename from src/backend/mediaaccessfacade.hpp rename to src/backend/media-access-facade.hpp index 261e84a61..9604360af 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,17 +21,24 @@ */ -#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/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 information record + * required for further handling this kind of 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/backend/real-clock.cpp b/src/backend/real-clock.cpp new file mode 100644 index 000000000..9e758057a --- /dev/null +++ b/src/backend/real-clock.cpp @@ -0,0 +1,53 @@ +/* + Timings - timing specifications for a frame quantised data stream + + 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 "backend/real-clock.hpp" + +#include <ctime> + + +namespace backend { + +#define MICRO_TICS_PER_NANOSECOND (1000*1000*1000 / GAVL_TIME_SCALE) + + + + TimeValue + RealClock::_readSystemTime() + { + timespec now; + 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; + + 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..4d1ad49eb --- /dev/null +++ b/src/backend/real-clock.hpp @@ -0,0 +1,98 @@ +/* + REAL-CLOCK.hpp - convenience front-end to access the system clock + + 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 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. + ** @todo not clear if this becomes some kind of central service (singleton) + ** or just a bunch of library routines + ** + ** @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 <boost/noncopyable.hpp> +//#include <string> +//#include <vector> +//#include <tr1/memory> +//#include <boost/scoped_ptr.hpp> + +//namespace lib { +//namespace time{ +// class Quantiser; +// typedef std::tr1::shared_ptr<const Quantiser> 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 + { + + + public: + + static Time + now() + { + return Time(_readSystemTime()); + } + + + private: + static TimeValue _readSystemTime(); + }; + + + +} // namespace backend +#endif diff --git a/src/backend/thread-wrapper.hpp b/src/backend/thread-wrapper.hpp index 60413b037..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. */ @@ -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/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..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,16 +111,16 @@ 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."); } - 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/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 * */ 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/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/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.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/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/common/configrules.hpp b/src/common/configrules.hpp index 841858883..28b535d02 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; @@ -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 lumiera::typelist::Types < mobject::session::Track - , asset::Pipe - , const asset::ProcPatt - , asset::Timeline - , 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/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/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/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/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 diff --git a/src/common/interfaceproxy.cpp b/src/common/interfaceproxy.cpp index e403e40a5..814f991b4 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 is closed; service currently not accessible"); + + + /** + * Implementation Base + * for building Facade Proxy implementations. + * Typically the purpose of such a proxy is to route + * any calls through the C-Bindings of the Lumiera Interface system. + * The actual storage for the concrete proxy object is embedded, + * inline within the #open() function. For access by the clients, + * a frontend-object of type \c Accessor<FA> may be placed into + * the facade interface; this accessor-frontend is basically + * a concealed static pointer to the proxy, and will be set, + * when the interface is opened. This opening and closing + * of the interface itself is controlled by the + * InstanceHandle, which in turn is typically + * created and managed within the context + * of the service implementation. + */ + template<class IHA> + class Holder; + + template<class FA, class I> + class Holder<InstanceHandle<I,FA> > + : Accessor<FA> + , protected FA + { + protected: + typedef InstanceHandle<I,FA> IHandle; + typedef Holder<IHandle> THolder; + typedef Proxy<IHandle> TProxy; + typedef Accessor<FA> Access; + + I& _i_; + + Holder (IHandle const& iha) + : _i_(iha.get()) + { } + + public: + static TProxy& open(IHandle const& iha) + { + static char buff[sizeof(TProxy)]; + TProxy* p = new(buff) TProxy(iha); + Access::implProxy_ = p; + return *p; + } + + static void close() + { + if (!Access::implProxy_) return; + TProxy* p = static_cast<TProxy*> (Access::implProxy_); + Access::implProxy_ = 0; + p->~TProxy(); + } + }; + + + template<class FA> + FA* Accessor<FA>::implProxy_; + + + template<class IHA> + void + openProxy (IHA const& iha) + { + Proxy<IHA>::open(iha); + } + + template<class IHA> + void + closeProxy () + { + Proxy<IHA>::close(); + } + +}} // namespace lumiera::facade 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/src/common/query/fake-configrules.hpp b/src/common/query/fake-configrules.hpp index bbc9f91ca..37b7d8d07 100644 --- a/src/common/query/fake-configrules.hpp +++ b/src/common/query/fake-configrules.hpp @@ -54,10 +54,13 @@ 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 lib::meta::InstantiateChained; using util::isnil; @@ -140,7 +143,7 @@ namespace lumiera { virtual bool resolve (Ret& solution, Query<TY> 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<Ret const&> (entry)); @@ -163,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 @@ -246,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/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/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/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/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/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/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.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..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< boost::shared_ptr<Clip> >& + lumiera::observable_list<shared_ptr<Clip> >& getClipList(void); private: - lumiera::observable_list< boost::shared_ptr<Clip> > clips; + lumiera::observable_list<shared_ptr<Clip> > clips; }; diff --git a/src/gui/model/clip.cpp b/src/gui/model/clip.cpp index 1dc0f2e00..f78e2c2af 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(3)) { } - - gavl_time_t - Clip::getBegin() const + + + void + Clip::setBegin (Time newStartTime) { - 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); + timeCoord_.accept (Mutation::changeTime (newStartTime)); + TODO("Emit A Signal"); } void - Clip::setBegin(gavl_time_t begin) + Clip::setDuration (Duration newLength) { - this->begin = begin; - // TODO: emit signal + timeCoord_.accept (Mutation::changeDuration(newLength)); + TODO("Emit A Signal"); } void - Clip::setEnd(gavl_time_t end) + Clip::setName (string const& name) { - this->end = end; - // TODO: emit signal - } - - void - Clip::setName(const std::string &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 ab3e7d2a3..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" +#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/model/parent-track.cpp b/src/gui/model/parent-track.cpp index 874c7e9d5..45c8d13ae 100644 --- a/src/gui/model/parent-track.cpp +++ b/src/gui/model/parent-track.cpp @@ -23,7 +23,7 @@ #include "parent-track.hpp" #include <boost/foreach.hpp> -using namespace boost; +using std::tr1::shared_ptr; namespace gui { namespace model { @@ -32,13 +32,13 @@ ParentTrack::ParentTrack() { } -const std::list< boost::shared_ptr<Track> >& +const std::list<shared_ptr<Track> >& ParentTrack::get_child_tracks() const { return tracks.get_list(); } -lumiera::observable_list< boost::shared_ptr<Track> >& +lumiera::observable_list<shared_ptr<Track> >& ParentTrack::get_child_track_list() { return tracks; @@ -52,11 +52,11 @@ ParentTrack::can_host_children() const bool ParentTrack::remove_descendant_track( - const boost::shared_ptr<Track> track) + const shared_ptr<Track> track) { REQUIRE(track); - boost::shared_ptr<ParentTrack> parent = + shared_ptr<ParentTrack> parent = find_descendant_track_parent(track); if(parent) { @@ -67,23 +67,24 @@ ParentTrack::remove_descendant_track( return false; } -boost::shared_ptr<ParentTrack> -ParentTrack::find_descendant_track_parent( - boost::shared_ptr<Track> child) +shared_ptr<ParentTrack> +ParentTrack::find_descendant_track_parent(shared_ptr<Track> child) { + using namespace boost; + 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>(); } } // namespace model diff --git a/src/gui/model/parent-track.hpp b/src/gui/model/parent-track.hpp index 78451972e..a1c709713 100644 --- a/src/gui/model/parent-track.hpp +++ b/src/gui/model/parent-track.hpp @@ -28,13 +28,9 @@ #ifndef PARENT_TRACK_HPP #define PARENT_TRACK_HPP -#include "track.hpp" +#include "gui/model/track.hpp" #include "lib/observable-list.hpp" -#include <boost/shared_ptr.hpp> -#include <boost/enable_shared_from_this.hpp> - - namespace gui { namespace model { @@ -44,7 +40,7 @@ namespace model { */ class ParentTrack : public Track, - public boost::enable_shared_from_this<ParentTrack> + public std::tr1::enable_shared_from_this<ParentTrack> { protected: /** @@ -57,13 +53,13 @@ public: /** * Gets a read-only reference to the the list of child tracks. */ - const std::list< boost::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< boost::shared_ptr<Track> >& + lumiera::observable_list<shared_ptr<Track> >& get_child_track_list(); /** @@ -79,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 boost::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 @@ -88,14 +84,14 @@ 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); + shared_ptr<ParentTrack> + find_descendant_track_parent (shared_ptr<Track> child); protected: /** * The internal list of child tracks of this parent. */ - lumiera::observable_list< boost::shared_ptr<Track> > tracks; + lumiera::observable_list<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..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< boost::shared_ptr<Sequence> >& + lumiera::observable_list<shared_ptr<Sequence> >& get_sequences(); void add_new_sequence(uString name); private: - lumiera::observable_list< boost::shared_ptr<Sequence> > sequences; + lumiera::observable_list<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/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 b8588ed49..c81cc9739 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 { @@ -35,11 +35,12 @@ const list< shared_ptr<Track> > Track::NoChildren; Track::Track() : enabled(true), locked(false) -{ +{ } -} +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 +97,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..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< boost::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 boost::shared_ptr<ParentTrack> - find_descendant_track_parent (boost::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< boost::shared_ptr<Track> > NoChildren; + static const std::list< shared_ptr<Track> > NoChildren; /** * The internal implementation of print_branch. 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/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..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 { @@ -59,9 +60,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..827a2bee9 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -20,47 +20,52 @@ * *****************************************************/ -#include <boost/foreach.hpp> #include "gui/gtk-lumiera.hpp" -#include "timeline-panel.hpp" +#include "gui/panels/timeline-panel.hpp" +#include "gui/widgets/timeline/timeline-zoom-scale.hpp" #include "gui/workspace/workspace-window.hpp" #include "gui/model/project.hpp" #include "gui/controller/controller.hpp" -#include "lib/time.h" +#include "lib/util.hpp" +#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::widgets::timeline; using namespace gui::model; +using std::tr1::shared_ptr; +using std::tr1::weak_ptr; +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) + , zoomScale() + , updatingToolbar(false) + , currentTool(timeline::Arrow) { // Hook up notifications get_project().get_sequences().signal_changed().connect(mem_fun(this, @@ -100,14 +105,15 @@ TimelinePanel::TimelinePanel(workspace::PanelManager &panel_manager, toolbar.append(separator2); - toolbar.append(zoomIn, mem_fun(this, &TimelinePanel::on_zoom_in)); - toolbar.append(zoomOut, mem_fun(this, &TimelinePanel::on_zoom_out)); - + toolbar.append(zoomScale); + zoomScale.signal_zoom(). + connect(mem_fun(this,&TimelinePanel::on_zoom)); + toolbar.show_all(); panelBar.pack_start(toolbar, PACK_SHRINK); // Setup tooltips - sequenceChooser .set_tooltip_text(_("Change sequence")); + sequenceChooser .set_tooltip_text(_("Change sequence")); previousButton .set_tooltip_text(_("To beginning")); rewindButton .set_tooltip_text(_("Rewind")); @@ -121,23 +127,23 @@ 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 - = *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); - + + // since TimelineWidget is now initialised, + // wire the zoom slider to react on timeline state changes + zoomScale.wireTimelineState (timelineWidget->get_state(), + timelineWidget->state_changed_signal()); + // Set the initial UI state update_sequence_chooser(); update_tool_buttons(); update_zoom_buttons(); - show_time(0); -} - -TimelinePanel::~TimelinePanel() -{ - + show_time (Time::ZERO); } const char* @@ -186,6 +192,13 @@ TimelinePanel::on_ibeam_tool() set_tool(timeline::IBeam); } +void +TimelinePanel::on_zoom(double time_scale_ratio) +{ + REQUIRE(timelineWidget); + timelineWidget->zoom_view(time_scale_ratio); +} + void TimelinePanel::on_zoom_in() { @@ -203,9 +216,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 +229,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(); @@ -239,18 +252,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(); } @@ -319,19 +333,9 @@ TimelinePanel::update_tool_buttons() void 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); - } +/* This function is no longer needed + * TODO: Let the ZoomScaleWidget perform + * the update on its own */ } void @@ -365,36 +369,27 @@ 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; } 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) { @@ -407,5 +402,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..febcb857c 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -29,17 +29,22 @@ #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 "gui/widgets/timeline/timeline-zoom-scale.hpp" -#include <boost/scoped_ptr.hpp> -#include <boost/weak_ptr.hpp> +#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; @@ -47,6 +52,10 @@ class Sequence; namespace panels { +using lib::time::Time; + + + /** * The definition of the timeline panel class, which holds timeline * widgets. @@ -55,17 +64,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. @@ -88,12 +92,13 @@ private: void on_arrow_tool(); void on_ibeam_tool(); + void on_zoom(double time_scale_ratio); void on_zoom_in(); void on_zoom_out(); 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 +126,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); + shared_ptr<widgets::timeline::TimelineState> + load_state (weak_ptr<model::Sequence> sequence); private: @@ -146,7 +151,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< weak_ptr<model::Sequence> > sequenceColumn; /** @@ -170,9 +175,10 @@ private: // Body Widgets boost::scoped_ptr<TimelineWidget> timelineWidget; - - std::map< boost::weak_ptr<model::Sequence>, - boost::shared_ptr<widgets::timeline::TimelineState> > + + std::map< weak_ptr<model::Sequence> + , shared_ptr<widgets::timeline::TimelineState> + > timelineStates; // Toolbar Widgets @@ -192,6 +198,7 @@ private: MiniButton zoomIn; MiniButton zoomOut; + gui::widgets::timeline::TimelineZoomScale zoomScale; Gtk::SeparatorToolItem separator2; 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 { diff --git a/src/gui/widgets/timecode-widget.cpp b/src/gui/widgets/timecode-widget.cpp index fbf9c1eee..4fdd9180f 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; @@ -187,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| @@ -208,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)); @@ -423,8 +423,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 +434,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 +465,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 +1045,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 +1110,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 +1136,7 @@ TimeCode::current_time(Time pos) const Time TimeCode::current_duration(Time pos) const { - Time ret = Time(0); + TimeVar ret; switch (_mode) { @@ -1202,7 +1206,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 +1222,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 3a280a589..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> @@ -33,13 +36,16 @@ #include <gtkmm/label.h> #include <gtkmm/frame.h> -#include "lib/lumitime.hpp" -using lumiera::Time; namespace gui { namespace widgets { +using lib::time::Time; +using lib::time::TimeVar; +using lib::time::TimeValue; + + class TimeCode : public Gtk::HBox { public: @@ -63,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; @@ -131,7 +137,7 @@ private: Gtk::EventBox clock_base; Gtk::Frame clock_frame; - Time last_when; + TimeVar last_when; bool last_pdelta; bool last_sdelta; @@ -166,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..fac09a6f5 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -27,10 +27,13 @@ using namespace Gtk; using namespace std; -using namespace boost; +using namespace std::tr1; + using namespace util; using namespace gui::widgets::timeline; -using namespace lumiera; + +using lib::time::Time; +using lib::time::TimeValue; namespace gui { namespace widgets { @@ -38,21 +41,19 @@ namespace widgets { 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; -TimelineWidget::TimelineWidget( - boost::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); @@ -60,7 +61,7 @@ TimelineWidget::TimelineWidget( 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( @@ -69,7 +70,7 @@ TimelineWidget::TimelineWidget( 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); @@ -91,7 +92,7 @@ TimelineWidget::~TimelineWidget() /* ===== Data Access ===== */ -boost::shared_ptr<timeline::TimelineState> +shared_ptr<timeline::TimelineState> TimelineWidget::get_state() { return state; @@ -100,6 +101,7 @@ TimelineWidget::get_state() void TimelineWidget::set_state(shared_ptr<timeline::TimelineState> new_state) { + state = new_state; // Clear the track tree @@ -123,17 +125,17 @@ TimelineWidget::set_state(shared_ptr<timeline::TimelineState> new_state) update_tracks(); // Send the state changed signal - stateChangedSignal.emit(); + stateChangedSignal.emit (state); } void -TimelineWidget::zoom_view(int zoom_size) +TimelineWidget::zoom_view(double timescale_ratio) { if(state) - { - const int view_width = body->get_allocation().get_width(); - state->get_view_window().zoom_view(view_width / 2, zoom_size); - } + { + const int view_width = body->get_allocation().get_width(); + state->get_view_window().zoom_view(view_width / 2, timescale_ratio); + } } ToolType @@ -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; @@ -176,7 +178,7 @@ TimelineWidget::hovering_track_changed_signal() const return hoveringTrackChangedSignal; } -sigc::signal<void> +TimelineWidget::TimelineStateChangeSignal TimelineWidget::state_changed_signal() const { return stateChangedSignal; @@ -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 @@ -208,11 +212,13 @@ 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(window.get_time_offset()); + + horizontalAdjustment.set_value(_raw(window.get_time_offset())); } } @@ -327,8 +333,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 @@ -349,9 +355,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); @@ -407,12 +413,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 @@ -473,7 +480,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 f27479d60..ffff15dda 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 @@ -56,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( - boost::shared_ptr<timeline::TimelineState> source_state); + TimelineWidget (shared_ptr<timeline::TimelineState> source_state); - /** - * Destructor - */ - ~TimelineWidget(); + virtual ~TimelineWidget(); + /* ===== Data Access ===== */ public: @@ -76,13 +80,13 @@ public: * @return The state object that the timeline widget is currently * working with. */ - boost::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(boost::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 @@ -90,7 +94,7 @@ public: * @param zoom_size The number of steps to zoom by. The scale factor * is 1.25^(-zoom_size). */ - void zoom_view(int zoom_size); + void zoom_view(double timescale_ratio); /** * Gets the type of the tool currently active. @@ -102,19 +106,21 @@ public: */ void set_tool(timeline::ToolType tool_type); - boost::shared_ptr<timeline::Track> + shared_ptr<timeline::Track> get_hovering_track() const; public: /* ===== Signals ===== */ - sigc::signal<void, lumiera::Time> mouse_hover_signal() const; + 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; 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: @@ -159,8 +165,8 @@ private: * already exist in trackMap. * @param list The parent track of the branch. */ - void create_timeline_tracks_from_branch( - boost::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. @@ -168,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). */ - boost::shared_ptr<timeline::Track> - create_timeline_track_from_modelTrack( - boost::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 @@ -178,10 +183,10 @@ 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); + 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 @@ -191,8 +196,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; + shared_ptr<timeline::Track> + lookup_timeline_track (shared_ptr<model::Track> modelTrack) const; // ----- Layout Functions ----- // @@ -222,12 +227,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; + shared_ptr<model::Sequence> sequence() const; // ----- Other Functions ----- // - void set_hovering_track( - boost::shared_ptr<timeline::Track> hovering_track); + void + set_hovering_track (shared_ptr<timeline::Track> hovering_track); protected: @@ -235,7 +240,7 @@ protected: * The state that will be used as the data source for this timeline * widget. */ - boost::shared_ptr<timeline::TimelineState> state; + shared_ptr<timeline::TimelineState> state; // Model Data @@ -246,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<boost::shared_ptr<model::Track>, - boost::shared_ptr<timeline::Track> > + std::map<shared_ptr<model::Track> + ,shared_ptr<timeline::Track> > trackMap; - boost::shared_ptr<timeline::Track> hoveringTrack; + shared_ptr<timeline::Track> hoveringTrack; // Helper Classes timeline::TimelineLayoutHelper layoutHelper; @@ -265,28 +270,18 @@ 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; - sigc::signal<void> stateChangedSignal; + HoveringTrackChangedSignal hoveringTrackChangedSignal; + TimelineStateChangeSignal stateChangedSignal; 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; - 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/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/basic-draw-strategy.cpp b/src/gui/widgets/timeline/draw-strategy.cpp similarity index 80% rename from src/gui/widgets/timeline/basic-draw-strategy.cpp rename to src/gui/widgets/timeline/draw-strategy.cpp index 52967ced9..5fc505260 100644 --- a/src/gui/widgets/timeline/basic-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..8b2e0ad37 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) @@ -51,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(); + 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(); - boost::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 == boost::shared_ptr<Clip>()) + if (!clip) return; clip->setSelected(true); @@ -73,8 +77,7 @@ namespace timeline { REQUIRE (event != NULL); Tool::on_button_release_event(event); - boost::shared_ptr<timeline::Track> track = - getHoveringTrack(); + shared_ptr<timeline::Track> track = getHoveringTrack(); } void @@ -88,10 +91,10 @@ namespace timeline { return; } - boost::shared_ptr<timeline::Track> + shared_ptr<timeline::Track> ArrowTool::getHoveringTrack () { - boost::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 c9b7decab..b390a25d5 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 { @@ -79,7 +81,7 @@ namespace timeline { private: - boost::shared_ptr<timeline::Track> + 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 431c7d44b..1b580c262 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -34,34 +34,37 @@ using namespace Gtk; using namespace std; -using namespace boost; using namespace lumiera; using gui::util::CairoUtil; +using std::tr1::shared_ptr; 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( sigc::mem_fun(this, &TimelineBody::on_state_changed) ); + // Set a default Tool + this->set_tool(Arrow); + // Install style properties register_styles(); - + // Reset the state - on_state_changed(); + propagateStateChange(); } TimelineBody::~TimelineBody() @@ -69,6 +72,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 { @@ -83,10 +93,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) @@ -147,7 +157,7 @@ TimelineBody::on_expose_event(GdkEventExpose* event) // Makes sure the widget styles have been loaded read_styles(); - if(timelineWidget.get_state()) + if (timelineState) { // Prepare to render via cairo const Allocation allocation = get_allocation(); @@ -169,9 +179,9 @@ TimelineBody::on_scroll_event (GdkEventScroll* event) { REQUIRE(event != NULL); - if(timelineWidget.get_state()) + if (timelineState) { - TimelineViewWindow &window = view_window(); + TimelineViewWindow &window = viewWindow(); const Allocation allocation = get_allocation(); if(event->state & GDK_CONTROL_MASK) @@ -255,19 +265,20 @@ 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 + // 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: { - 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(); - 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); @@ -293,15 +304,26 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event) } void -TimelineBody::on_state_changed() +TimelineBody::on_state_changed (shared_ptr<TimelineState> newState) { - if(timelineWidget.get_state()) + REQUIRE (newState); + timelineState = newState; + propagateStateChange(); +} + +void +TimelineBody::propagateStateChange() +{ + if (timelineState) { // Connect up some events - view_window().changed_signal().connect( + viewWindow().changed_signal().connect( sigc::mem_fun(this, &TimelineBody::on_update_view) ); } - + + // Need to reload the current tool... + set_tool (get_tool(), true); + // Redraw queue_draw(); } @@ -334,7 +356,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? @@ -372,7 +394,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(); } @@ -385,12 +407,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); - 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 = 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()) @@ -426,18 +447,13 @@ TimelineBody::draw_playback_point(Cairo::RefPtr<Cairo::Context> cr) { REQUIRE(cr); - // Prepare - - shared_ptr<TimelineState> state = timelineWidget.get_state(); - if(state) + if (timelineState) { - const Allocation allocation = get_allocation(); - - const gavl_time_t point = state->get_playback_point(); - if(point == (gavl_time_t)GAVL_TIME_UNDEFINED) - return; + if (!timelineState->isPlaying()) return; - const int x = view_window().time_to_x(point); + const Allocation allocation = get_allocation(); + Time point = timelineState->getPlaybackPoint(); + const int x = viewWindow().time_to_x(point); // Set source cr->set_source(playbackPointColour); @@ -456,10 +472,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(); } } @@ -476,14 +492,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 72dc374b2..c248ca061 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,14 @@ class TimelineWidget; namespace timeline { +using lib::time::TimeVar; +using lib::time::TimeSpan; + + class Track; class TimelineViewWindow; + /** * Implementation of the timeline body subwidget. This widget is * displayed in the centre of the timeline widget, and displays the @@ -62,10 +69,7 @@ public: */ TimelineBody(gui::widgets::TimelineWidget &timeline_widget); - /** - * Destructor - */ - ~TimelineBody(); + virtual ~TimelineBody(); TimelineWidget& getTimelineWidget () const; @@ -81,7 +85,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: @@ -119,13 +123,18 @@ protected: bool on_motion_notify_event(GdkEventMotion *event); /** - * The event handler for when the TimelineWidget's state object is - * replaced. + * The event handler for when the TimelineWidget's state is switched. */ - void on_state_changed(); + void on_state_changed (shared_ptr<TimelineState> newState); /* ===== Internals ===== */ private: + /** + * Access the current timeline view window + * @warning must not be called unless the TimlineWidget + * has a valid state. + */ + TimelineViewWindow& viewWindow() const; /** * Draws the timeline tracks. @@ -134,8 +143,8 @@ private: void draw_tracks(Cairo::RefPtr<Cairo::Context> cr); void draw_track(Cairo::RefPtr<Cairo::Context> cr, - boost::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. @@ -155,13 +164,9 @@ private: void set_vertical_offset(int offset); - /** - * A helper function to get the view window - * @remarks This function must not be called unless the TimlineWidget - * has a valid state. - */ - TimelineViewWindow& view_window() const; - + /** adjust to the new timeline state */ + void propagateStateChange(); + /** * Registers all the styles that this class will respond to. */ @@ -186,7 +191,7 @@ private: // Scroll State DragType dragType; - gavl_time_t beginShiftTimeOffset; + TimeVar beginShiftTimeOffset; int beginShiftVerticalOffset; // Style properties @@ -196,6 +201,8 @@ private: Cairo::RefPtr<Cairo::SolidPattern> playbackPointColour; gui::widgets::TimelineWidget &timelineWidget; + shared_ptr<TimelineState> timelineState; + friend class Tool; friend class ArrowTool; diff --git a/src/gui/widgets/timeline/timeline-clip-track.cpp b/src/gui/widgets/timeline/timeline-clip-track.cpp index b07b3ee1c..f659aa1f8 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.cpp +++ b/src/gui/widgets/timeline/timeline-clip-track.cpp @@ -22,13 +22,13 @@ #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; -using boost::dynamic_pointer_cast; -using boost::shared_ptr; + +using std::tr1::shared_ptr; using util::contains; namespace gui { @@ -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); @@ -76,19 +76,19 @@ namespace timeline { } } - boost::shared_ptr<timeline::Clip> - ClipTrack::getClipAt(lumiera::Time position) const + shared_ptr<timeline::Clip> + 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; } // Nothing found - return boost::shared_ptr<timeline::Clip>(); + return 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 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 461d71876..32b93b321 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: @@ -48,7 +52,7 @@ namespace timeline { * Constructor. */ ClipTrack(TimelineWidget &timelineWidget, - boost::shared_ptr<model::ClipTrack> track); + shared_ptr<model::ClipTrack> track); /** * Draw the track in the timeline. @@ -62,8 +66,8 @@ namespace timeline { * pointer. * @param the given time */ - boost::shared_ptr<timeline::Clip> - getClipAt(lumiera::Time position) const; + shared_ptr<timeline::Clip> + getClipAt(Time position) const; private: @@ -76,7 +80,7 @@ namespace timeline { /** * Gets the modelTrack as a ClipTrack. */ - boost::shared_ptr<model::ClipTrack> + shared_ptr<model::ClipTrack> getModelTrack (); /** @@ -103,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<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 001964ae1..1b4e04d72 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 std::tr1::shared_ptr; + - Clip::Clip(boost::shared_ptr<model::Clip> clip, - boost::shared_ptr<timeline::DrawStrategy> drawStrategy) - : Entity(drawStrategy), - modelClip(clip), - selected(false) + Clip::Clip (shared_ptr<model::Clip> clip, + 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..c7e6bd64b 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 (shared_ptr<model::Clip> clip, + 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: + + shared_ptr<model::Clip> modelClip; + + /** + * 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..3f7582375 100644 --- a/src/gui/widgets/timeline/timeline-entity.cpp +++ b/src/gui/widgets/timeline/timeline-entity.cpp @@ -20,18 +20,16 @@ * *****************************************************/ -#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 { namespace timeline { - Entity::Entity(boost::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 fc1e8e345..f30a804a1 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,22 @@ #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 <tr1/memory> #include <cairomm/cairomm.h> + namespace gui { namespace widgets { namespace timeline { - + + using lib::time::Time; + using std::tr1::shared_ptr; + + class DrawStrategy; class TimelineViewWindow; @@ -51,14 +53,12 @@ namespace timeline { class Entity { protected: - Entity(boost::shared_ptr<timeline::DrawStrategy> drawStrategy); + Entity (shared_ptr<timeline::DrawStrategy> drawStrategy); virtual ~Entity(); public: - virtual gavl_time_t - getBegin () const = 0; virtual void draw(Cairo::RefPtr<Cairo::Context> cairo, @@ -67,7 +67,10 @@ namespace timeline { bool getEnabled () const; - virtual gavl_time_t + virtual Time + getBegin () const = 0; + + virtual Time getEnd () const = 0; virtual std::string @@ -80,7 +83,7 @@ namespace timeline { bool enabled; - boost::shared_ptr<timeline::DrawStrategy> drawStrategy; + 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..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, - boost::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 3ecfba2d0..1d1af9537 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 { @@ -342,12 +342,9 @@ TimelineHeaderContainer::on_scroll() } void -TimelineHeaderContainer::on_hovering_track_changed( - boost::shared_ptr<timeline::Track> hovering_track) +TimelineHeaderContainer::on_hovering_track_changed( shared_ptr<timeline::Track>) { - (void)hovering_track; - - + /* do nothing */ } bool @@ -392,7 +389,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..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( - boost::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( - boost::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. */ - boost::shared_ptr<timeline::Track> lookup_timeline_track( - boost::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 -----// - boost::shared_ptr<timeline::Track> hoveringTrack; + 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 99e263ed7..67da78ecd 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp @@ -20,12 +20,14 @@ * *****************************************************/ -#include "timeline-ibeam-tool.hpp" +#include "gui/widgets/timeline/timeline-ibeam-tool.hpp" #include "gui/widgets/timeline-widget.hpp" +#include "lib/time/mutation.hpp" -using namespace boost; using namespace gui::widgets; -using namespace lumiera; + +using lib::time::Mutation; +using std::tr1::shared_ptr; namespace gui { namespace widgets { @@ -41,15 +43,18 @@ const int IBeamTool::ScrollSlideEventInterval = 40; IBeamTool::IBeamTool(TimelineBody &timeline_body) : Tool(timeline_body), + selectionControl(), dragType(None), - pinnedDragTime(0), + pinnedDragTime(), scrollSlideRate(0) { - + // Connect the timlinebody selection to the selectionControl + get_state()->set_selection_control (selectionControl); } IBeamTool::~IBeamTool() { + selectionControl.disconnect(); end_scroll_slide(); } @@ -103,20 +108,20 @@ 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); + selectionControl (TimeSpan(time, Duration::NIL)); } } } @@ -175,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; @@ -187,12 +192,17 @@ 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); + // 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 + + selectionControl (TimeSpan (newStartPoint, Duration(selectionLength))); } 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..ecf5b517d 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.hpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.hpp @@ -19,21 +19,29 @@ 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 "lib/time/timequant.hpp" +#include "lib/time/control.hpp" -#include <gtkmm.h> -#include "timeline-tool.hpp" 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 */ @@ -130,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. @@ -154,6 +162,8 @@ private: }; /* ==== Internals ===== */ + Control<TimeSpan> selectionControl; + /** * Specifies the type of drag currently taking place. */ @@ -164,7 +174,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-layout-helper.cpp b/src/gui/widgets/timeline/timeline-layout-helper.cpp index 0e311cd7d..df00dd131 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,8 +112,8 @@ TimelineLayoutHelper::header_from_point(Gdk::Point point) return shared_ptr<timeline::Track>(); } -boost::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( - boost::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(); } -boost::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 bc0f1c39d..2c2cca44a 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,8 +114,7 @@ public: * tracks. * @see update_layout() */ - boost::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() */ - boost::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. */ - boost::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( - boost::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, - boost::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. */ - boost::shared_ptr<timeline::Track> lookup_timeline_track( - boost::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. */ - boost::shared_ptr<model::Sequence> get_sequence() const; + 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 3d2d73cf3..532e461f7 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 std::tr1::shared_ptr; 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( @@ -74,6 +75,14 @@ TimelineRuler::TimelineRuler( register_styles(); } +TimelineViewWindow& +TimelineRuler::viewWindow() const +{ + REQUIRE(timelineState); + return timelineState->get_view_window(); +} + + void TimelineRuler::set_mouse_chevron_offset(int offset) { @@ -113,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(); @@ -159,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; } } @@ -218,31 +227,42 @@ TimelineRuler::on_size_allocate(Gtk::Allocation& allocation) } void -TimelineRuler::on_state_changed() +TimelineRuler::on_state_changed (shared_ptr<TimelineState> newState) { - if(timelineWidget.get_state()) - { - // Connect up some events - view_window().changed_signal().connect( - sigc::mem_fun(this, &TimelineRuler::on_update_view) ); - } - + REQUIRE (newState); + timelineState = newState; + + propagateStateChange(); +} + +void +TimelineRuler::propagateStateChange() +{ + // Connect up some events + viewWindow().changed_signal().connect( + sigc::mem_fun(this, &TimelineRuler::on_update_view) ); + // Redraw on_update_view(); } + + void TimelineRuler::set_leading_x(const int x) { - shared_ptr<TimelineState> state = timelineWidget.get_state(); - - if(state) + if (timelineState) { - 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 (viewWindow().x_to_time(x)); + Offset selectionLength (pinnedDragTime, newStartPoint); + + if (newStartPoint > pinnedDragTime) + newStartPoint=pinnedDragTime; // use the smaller one as selection start + + 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 } } @@ -254,8 +274,9 @@ 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 gavl_time_t left_offset = window.get_time_offset(); + /////////////////////////////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(); // Preparation steps @@ -301,7 +322,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(); @@ -355,16 +376,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 = window.time_to_x(state->get_selection_start()) + 1; + 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()); @@ -374,7 +394,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(timelineState->getSelectionEnd()); if(b >= 0 && b < ruler_rect.get_width()) { cr->move_to(b, ruler_rect.get_height()); @@ -391,30 +411,25 @@ 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 = window.time_to_x( - state->get_playback_period_start()) + 1 + 0.5f; + const float a = 1.5f + window.time_to_x(timelineState->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(timelineState->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) @@ -463,15 +478,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); - const TimelineViewWindow &window = state->get_view_window(); + if (!timelineState->isPlaying()) return; - const gavl_time_t point = state->get_playback_point(); - if(point == (gavl_time_t)GAVL_TIME_UNDEFINED) - return; - 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); @@ -493,7 +505,7 @@ TimelineRuler::calculate_major_spacing() const { unsigned int i; - const int64_t time_scale = view_window().get_time_scale(); + /////////////////////////////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, @@ -511,14 +523,16 @@ 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 }; + 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; @@ -530,14 +544,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 fe50669b9..3d903aaf5 100644 --- a/src/gui/widgets/timeline/timeline-ruler.hpp +++ b/src/gui/widgets/timeline/timeline-ruler.hpp @@ -28,6 +28,8 @@ #define TIMELINE_RULER_HPP #include "gui/gtk-lumiera.hpp" +#include "lib/time/timevalue.hpp" +#include "gui/widgets/timeline/timeline-state.hpp" namespace gui { namespace widgets { @@ -107,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 (shared_ptr<TimelineState> newState); private: /* ===== Internal Methods ===== */ @@ -160,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. @@ -169,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. @@ -198,7 +205,7 @@ private: * the mouse, the other is pinned. pinnedDragTime specifies the time * of that point. */ - lumiera::Time pinnedDragTime; + lib::time::TimeVar pinnedDragTime; // Indicated values /** @@ -232,6 +239,11 @@ private: */ gui::widgets::TimelineWidget &timelineWidget; + /** + * the currently active timeline state object + */ + shared_ptr<TimelineState> timelineState; + /** * The caches image of the ruler, over which the chevrons etc. will * be drawn. diff --git a/src/gui/widgets/timeline/timeline-state.cpp b/src/gui/widgets/timeline/timeline-state.cpp index f8e712169..a38911ff5 100644 --- a/src/gui/widgets/timeline/timeline-state.cpp +++ b/src/gui/widgets/timeline/timeline-state.cpp @@ -20,34 +20,54 @@ * *****************************************************/ -#include "timeline-state.hpp" -using namespace boost; +#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; -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 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) { REQUIRE(sequence); + + // Initialize the selection listener + selectionListener (TimeSpan (Time::ZERO, Duration::NIL)); + 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(GAVL_TIME_SCALE / 200); - set_selection(Time(2000000), Time(4000000)); + viewWindow.set_time_scale(DEFAULT_TIMELINE_SCALE); + + setSelection (Mutation::changeTime (Time(FSecs(2)))); + setSelection (Mutation::changeDuration (Duration(FSecs(2)))); + //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all } -boost::shared_ptr<model::Sequence> +shared_ptr<model::Sequence> TimelineState::get_sequence() const { return sequence; @@ -59,85 +79,37 @@ 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 +void +TimelineState::set_selection_control (SelectionControl &control) { - return playbackPoint; + control.disconnect(); + selection_.accept (control); + control.connectChangeNotification (selectionListener); } sigc::signal<void> @@ -152,6 +124,12 @@ TimelineState::playback_changed_signal() const return playbackChangedSignal; } +void +TimelineState::on_selection_changed (TimeSpan selection) +{ + 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 22fb12cfd..7b5221aa1 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 "lib/time/control.hpp" + namespace gui { @@ -37,6 +40,43 @@ 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. + * SelectionListener wraps a sigc::signal that emits every time + * 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> +class SelectionListener + : boost::noncopyable + { + sigc::signal<void, TI> valueChangedSignal; + + public: + SelectionListener() { } + + void + operator() (TI const& changeValue) const + { + valueChangedSignal.emit (changeValue); + } + + + void connect (const sigc::slot<void, TI> &connection) + { + valueChangedSignal.connect (connection); + } + + }; + /** * TimelineState is a container for the state data for TimelineWidget. * @remarks TimelineState s can be swapped out so that TimelineWidget @@ -47,11 +87,10 @@ class TimelineState public: /** - * Constructor * @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 (shared_ptr<model::Sequence> source_sequence); public: @@ -59,7 +98,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; + shared_ptr<model::Sequence> get_sequence() const; /** * Gets a reference to the timeline view window object. @@ -67,57 +106,42 @@ public: */ timeline::TimelineViewWindow& get_view_window(); - /** - * Gets the time at which the selection begins. - */ - lumiera::Time get_selection_start() const; + 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();} + Time getPlaybackPeriodEnd() const { return selection_.end(); } - /** - * Gets the time at which the selection begins. - */ - lumiera::Time get_selection_end() const; + Time getPlaybackPoint() const { return playbackPoint_; } + + + /** is there currently any ongoing playback process? + * Otherwise the #getPlaybackPoint is meaningless */ + bool isPlaying() const { return isPlayback_; } + + void set_selection_control (SelectionControl &control); /** * 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. */ @@ -128,51 +152,53 @@ public: * changed. */ sigc::signal<void> playback_changed_signal() const; + +private: + + /* ========= Event Handlers ========== */ + + /** + * Event handler for when the selection is changed + */ + void on_selection_changed (TimeSpan selection); 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. */ - boost::shared_ptr<model::Sequence> sequence; + shared_ptr<model::Sequence> sequence; - // View State - /** - * The ViewWindow for the TimelineWidget display with. - */ + + /* == View State == */ + + /** ViewWindow for the TimelineWidget display */ timeline::TimelineViewWindow viewWindow; - // Selection State - /** - * The start time of the selection period. + /* == Selection State == */ + + /** currently selected time period. */ + TimeSpan selection_; + + /** listens for a selection change */ + SelectionListener<TimeSpan> selectionListener; + + /** current playback period. */ + TimeSpan playbackPeriod_; + + /** current playback position. + * @todo very likely to be handled differently + * when actually integrated with the Player */ - lumiera::Time selectionStart; + TimeVar playbackPoint_; + + bool isPlayback_; - /** - * The end time of the selection period. - */ - lumiera::Time selectionEnd; - /** - * The start time of the playback period. - */ - lumiera::Time playbackPeriodStart; - - /** - * The end time of the playback period. - */ - lumiera::Time playbackPeriodEnd; - - /** - * The time of the playback point. - */ - lumiera::Time playbackPoint; - - // Signals + /* == Signals == */ /** * A signal to notify when the selected period has changed. @@ -186,8 +212,7 @@ private: sigc::signal<void> playbackChangedSignal; }; -} // namespace timeline -} // namespace widgets -} // namespace gui + +}}} // namespace gui::widgets::timeline #endif // TIMELINE_STATE_HPP 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..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 */ - boost::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 2ac324f1f..ac1c6e4d1 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 std::tr1::shared_ptr; + namespace gui { namespace widgets { namespace timeline { @@ -120,11 +121,11 @@ 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>(); + return shared_ptr<timeline::Clip>(); } void @@ -271,7 +272,7 @@ void Track::on_remove_track() { REQUIRE(modelTrack); - boost::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 a45bf5ab4..dc7b18129 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; @@ -69,20 +72,14 @@ public: Collapse }; - /** - * Constructor - */ Track(TimelineWidget &timeline_widget, - boost::shared_ptr<model::Track> track); - - /** - * Destructor - */ - ~Track(); + shared_ptr<model::Track> track); + + virtual ~Track(); Gtk::Widget& get_header_widget(); - boost::shared_ptr<model::Track> + shared_ptr<model::Track> getModelTrack() const; /** @@ -152,8 +149,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 shared_ptr<timeline::Clip> + getClipAt (Time position) const; private: @@ -206,7 +203,7 @@ private: protected: TimelineWidget &timelineWidget; - boost::shared_ptr<model::Track> modelTrack; + shared_ptr<model::Track> modelTrack; private: /** diff --git a/src/gui/widgets/timeline/timeline-view-window.cpp b/src/gui/widgets/timeline/timeline-view-window.cpp index 2a62fcc90..357e4db97 100644 --- a/src/gui/widgets/timeline/timeline-view-window.cpp +++ b/src/gui/widgets/timeline/timeline-view-window.cpp @@ -31,28 +31,36 @@ namespace gui { namespace widgets { namespace timeline { -TimelineViewWindow::TimelineViewWindow( - Time offset, int64_t scale) : - timeOffset(offset), - timeScale(scale) +/* == 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) { + } -Time -TimelineViewWindow::get_time_offset() const +Offset +TimelineViewWindow::get_time_offset() const /////////////////////TICKET #795: 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) /////////TICKET #795: this function shouldn't be accessible from outside { timeOffset = offset; changedSignal.emit(); } int64_t -TimelineViewWindow::get_time_scale() const +TimelineViewWindow::get_time_scale() const /////////////////////TICKET #795: this function shouldn't be accessible from outside { return timeScale; } @@ -65,24 +73,51 @@ TimelineViewWindow::set_time_scale(int64_t scale) } void -TimelineViewWindow::zoom_view(int point, int zoom_size) -{ - int64_t new_time_scale = (double)timeScale * pow(1.25, -zoom_size); - - // Limit zooming in too close - if(new_time_scale < 1) new_time_scale = 1; - - // Nudge zoom problems caused by integer rounding - if(new_time_scale == timeScale && zoom_size < 0) - new_time_scale++; - - // Limit zooming out too far - if(new_time_scale > TimelineWidget::MaxScale) - new_time_scale = TimelineWidget::MaxScale; - +TimelineViewWindow::set_time_scale(double ratio) +{ + int64_t max = 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)); +} + +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, ZoomSmoothing) + * double(MaxScale)); + + /* Prevent Zooming in To Close and Far */ + if(new_time_scale < 1) + new_time_scale = 1; + + if(new_time_scale > MaxScale) + new_time_scale = 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 = timeOffset + TimeValue(point * (timeScale - new_time_scale)); + set_time_offset (newStartPoint); // Apply the new scale set_time_scale(new_time_scale); @@ -91,20 +126,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> @@ -113,7 +148,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-view-window.hpp b/src/gui/widgets/timeline/timeline-view-window.hpp index ebd476b66..1b4b19888 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 @@ -44,30 +54,46 @@ namespace timeline { class TimelineViewWindow { public: - - /** - * Constructor - * @param offset The initial view offset. - * @param scale The initial scale. - */ - TimelineViewWindow(lumiera::Time offset, int64_t scale); /** - * Gets the time offset. This is the time value displaid at the - * left-hand edge of the timeline body area. + * 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. + * @param scale The initial view scale. */ - lumiera::Time get_time_offset() const; + TimelineViewWindow(Offset offset, int64_t scale); + + /** + * 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 information... + */ + 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; @@ -78,14 +104,18 @@ public: * zero */ 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. - * @param zoom_size The number of steps to zoom by. The scale factor - * is 1.25^(-zoom_size). + * @param new_time_scale The number of steps to zoom by. The scale factor + * is 1.25^(-new_time_scale). */ - void zoom_view(int point, int zoom_size); + void zoom_view(int point, double timescale_ratio); /** * Scrolls the view horizontally as a proportion of the view area. @@ -100,15 +130,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 +151,14 @@ public: sigc::signal<void> changed_signal() const; private: - lumiera::Time timeOffset; + 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 new file mode 100644 index 000000000..99299608a --- /dev/null +++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp @@ -0,0 +1,146 @@ +/* + 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-widget.hpp" +#include "gui/widgets/timeline/timeline-zoom-scale.hpp" + +using namespace Gtk; + +namespace gui { +namespace widgets { + +class TimelineWidget; + +namespace timeline { + +/** + * @todo The initial adjustment value needs to + * match what the TimelineViewWindow's actual timeScale + * Value is. TimelineViewWindow::get_time_scale() is + * currently a public method, but will soon be private. + * Maybe TimelineViewWindow can have a zoom_adjustment + * that gets passed to this widget's Constructor? + * + * @todo actually there is a more involved problem. + * The TimelineWidget maintains a TimelineState, which in turn + * owns the TimelineViewWindow. Now, the problem is: when we + * switch to another Sequence (View), then this TimelineState + * gets switched too, causing also a entirely different TimelineViewWindow + * to become effective. Thus + * - how can we managed to be notified from that switch? + * - problem is: TimelineZoomScale widget is owned by the TimelinePannel. + * Likewise, TimelineWidget is owned by the TimelinePannel. But the + * state handling/switching logic is embedded within TimelineWidget + * - and finally: how can we translate the actual scale (in time units), + * as maintained within TimelineViewWindow, back into the adjustment + * used here (which uses a relative scale 0...1.0 ) + * + */ + +TimelineZoomScale::TimelineZoomScale() + : HBox() + , adjustment(0.5, 0.0, 1.0, 0.000001) + , slider() + , zoomIn(Stock::ZOOM_IN) + , zoomOut(Stock::ZOOM_OUT) + , button_step_size(0.03) +{ + /* Setup the Slider Control */ + slider.set_adjustment (adjustment); + slider.set_size_request (123,10); + slider.set_digits (6); + + /* 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)); + zoomOut.signal_clicked(). + connect (sigc::mem_fun(this, &TimelineZoomScale::on_zoom_out_clicked)); + adjustment.signal_value_changed(). + connect (sigc::mem_fun(this, &TimelineZoomScale::on_zoom)); + + /* Add Our Widgets and show them */ + pack_start (zoomOut,PACK_SHRINK); + pack_start (slider,PACK_SHRINK); + pack_start (zoomIn,PACK_SHRINK); + + show_all(); +} + +void +TimelineZoomScale::wireTimelineState (shared_ptr<TimelineState> currentState, + TimelineWidget::TimelineStateChangeSignal stateChangeSignal) +{ + on_timeline_state_changed (currentState); + stateChangeSignal.connect ( + sigc::mem_fun(this, &TimelineZoomScale::on_timeline_state_changed)); +} + +void +TimelineZoomScale::on_timeline_state_changed (shared_ptr<TimelineState> newState) +{ + REQUIRE (newState); + timelineState = newState; + + adjustment.set_value (getViewWindow().get_smoothed_time_scale()); +} + +void +TimelineZoomScale::on_zoom_in_clicked() +{ + double newValue = adjustment.get_value() - button_step_size; + adjustment.set_value(newValue); +} + +void +TimelineZoomScale::on_zoom_out_clicked() +{ + double newValue = adjustment.get_value() + button_step_size; + adjustment.set_value(newValue); +} + +void +TimelineZoomScale::on_zoom() +{ + zoomSignal.emit (adjustment.get_value()) ; +} + +sigc::signal<void, double> +TimelineZoomScale::signal_zoom() +{ + return zoomSignal; +} + +TimelineViewWindow& +TimelineZoomScale::getViewWindow() +{ + REQUIRE (timelineState, "lifecycle error"); + return timelineState->get_view_window(); +} + +} // namespace gui +} // namespace widgets +} // namespace timeline 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..694252319 --- /dev/null +++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp @@ -0,0 +1,108 @@ +/* + timeline-zoom-scale.hpp - Declaration of the zoom scale widget + + Copyright (C) Lumiera.org + 2011, Michael R. Fisher <mfisher31@gmail.com> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +/** @file timeline-zoom-scale.hpp + ** This file contains the definition of the zoom scale widget + */ + +#ifndef TIMELINE_ZOOM_SCALE_HPP +#define TIMELINE_ZOOM_SCALE_HPP + +#include "gui/gtk-lumiera.hpp" +#include "gui/widgets/mini-button.hpp" +#include "gui/widgets/timeline-widget.hpp" + +using namespace Gtk; +using namespace gui::widgets; + +namespace gui { +namespace widgets { +namespace timeline { + +class TimelineZoomScale : public Gtk::HBox +{ +public: + /** + * Constructor + */ + TimelineZoomScale(); + + /** + * Accessor method to the zoomSignal + * @return the zoomSignal + */ + sigc::signal<void, double> signal_zoom(); + + void set_value(double val) { adjustment.set_value(val); } + + void wireTimelineState (shared_ptr<TimelineState> currentState, + TimelineWidget::TimelineStateChangeSignal); + +private: + /* Event Handlers */ + + /** + * Update the slider position when the timeline state + * is changed. + */ + void on_timeline_state_changed (shared_ptr<TimelineState> newState); + + /** + * Event handler for when the zoomIn Button + * is clicked + */ + void on_zoom_in_clicked(); + + /** + * Event handler for when the zoomIn Button + * is clicked + */ + void on_zoom_out_clicked(); + + /** + * Event handler for when the adjustment + * value is changed + */ + void on_zoom(); + + /** access current timeline state */ + TimelineViewWindow& getViewWindow(); + + /* Widgets */ + Gtk::Adjustment adjustment; + Gtk::HScale slider; + MiniButton zoomIn; + MiniButton zoomOut; + +private: + /* Signals */ + sigc::signal<void, double> zoomSignal; + + const double button_step_size; + + shared_ptr<TimelineState> timelineState; +}; + +} // namespace gui +} // namespace widgets +} // namespace timeline + +#endif /* TIMELINE_ZOOM_SCALE_HPP */ 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..e06b0f4ee 100644 --- a/src/gui/window-manager.hpp +++ b/src/gui/window-manager.hpp @@ -39,14 +39,14 @@ #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 { using std::string; +using std::tr1::shared_ptr; namespace model { class Project; } @@ -195,7 +195,7 @@ private: private: - std::list< boost::shared_ptr<workspace::WorkspaceWindow> > windowList; + std::list<shared_ptr<workspace::WorkspaceWindow> > windowList; public: 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/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/include/interfaceproxy.hpp b/src/include/interfaceproxy.hpp index ea5677042..0469da8f9 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. @@ -48,17 +48,18 @@ ** Any sort of dependency management is outside the scope of the InstanceHandle (for the core ** services, it is handled by the dependency of subsystems, while the plugin loader cares ** for dependency issues regarding loadable modules, thereby building on the deployment - ** descriptors. + ** descriptors.) ** ** For the Layer separation interfaces, the process of loading and opening is abstracted as ** an InstanceHandle object. When creating such an InstanceHandle using the appropriate ** template and ctor parameters, in addition to the registration with the Interface/Plugin - ** system, the corresponding facade::Proxy factory is addressed and "opened" by creating - ** the right proxy object instance. Similarly, when the InstanceHandle object goes out - ** of scope, prior to detaching from the Interface/Proxy system, the corresponding - ** lumiera::facade::Accessor factory is "closed", which additionally means destroying - ** the proxy object instance and switching any further access to throwing and exception. - ** + ** system, the corresponding facade::Proxy factory is addressed and the interface instance + ** is "opened" by creating the appropriate proxy object instance. Similarly, when the + ** InstanceHandle object goes out of scope, prior to detaching from the Interface/Proxy + ** system, the corresponding lumiera::facade::Accessor frontend is "closed", which + ** additionally means destroying the proxy object instance and switching any + ** further access to throwing and exception. + ** ** While client code just includes the interface header (including interfaceproxy.hpp ** in turn), there needs to be an actual implementation of each proxy object located in ** some translation unit. The usual place is interfaceproxy.cpp, which gets linked into @@ -82,45 +83,66 @@ namespace lumiera { - namespace facade { - - /** error-ID for accessing a (currently) closed facade */ - LUMIERA_ERROR_DECLARE(FACADE_LIFECYCLE); +namespace facade { + /** error-ID for accessing a (currently) closed facade */ + LUMIERA_ERROR_DECLARE(FACADE_LIFECYCLE); + + + /********************************************************************* + * access-frontend to the implementation of a service. + * Usually, an instance of Accessor is placed as static member + * right into the facade interface used to access the service. + * This allows clients to get the current actual implementation + * of that service, just by invoking the function operator on + * that member, e.g. \c lumiera::Play::facade() + * + * The reason for this rather indirect access technique is Lifecycle: + * Service implementations may come up and go down; moreover, a service + * might be implemented through a plugin component and thus the actual + * invocation needs to be passed through a binding layer. In this case, + * clients rather access a proxy object, which then passes on any call + * through that binding layer to the actual implementation located + * "somewhere". + * + * @note the pointer to the actual implementation is a static variable. + * This has two consequences. For one, we're dealing with kind of + * singleton service here. And, secondly, the implementation or + * proxy accessor can inherit from Accessor<FA> where FA is the + * facade interface. Being a subclass, allows the implementation + * to set that pointer when the service comes up, and to clear + * it when the service goes down and the access needs to + * be closed. + */ + template<class FA> + class Accessor + { + protected: + static FA* implProxy_; + + + public: + FA& + operator() () + { + if (implProxy_) + return *implProxy_; + else + throw error::State("Facade interface currently closed." + , LUMIERA_ERROR_FACADE_LIFECYCLE); + } + }; - /********************************************************************* - * - */ - template<class FA> - class Accessor - { - protected: - static FA* implProxy_; - - - public: - FA& - operator() () - { - if (implProxy_) - return *implProxy_; - else - throw error::State("Facade interface currently closed."); - } - }; - - template<class IHA> - void openProxy (IHA const&); - - template<class IHA> - void closeProxy (); - - template<class IHA> - class Proxy; - - - } // namespace facade + template<class IHA> + void openProxy (IHA const&); -} // namespace lumiera + template<class IHA> + void closeProxy (); + + template<class IHA> + class Proxy; + + +}} // namespace lumiera::facade #endif 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/include/play-facade.h b/src/include/play-facade.h new file mode 100644 index 000000000..480a1d6fd --- /dev/null +++ b/src/include/play-facade.h @@ -0,0 +1,163 @@ +/* + PLAYER-FACADE.h - access point to the Lumiera player subsystem + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef PROC_INTERFACE_PLAY_H +#define PROC_INTERFACE_PLAY_H + + + + +#ifdef __cplusplus /* ============== C++ Interface ================= */ + +//#include "include/interfaceproxy.hpp" +#include "lib/handle.hpp" +#include "lib/iter-source.hpp" +#include "lib/time/control.hpp" +#include "lib/time/timevalue.hpp" +#include "include/interfaceproxy.hpp" +#include "proc/mobject/model-port.hpp" +#include "proc/mobject/output-designation.hpp" +#include "proc/mobject/session/clip.hpp" +#include "proc/mobject/session/track.hpp" +#include "proc/play/output-manager.hpp" +#include "proc/asset/timeline.hpp" +#include "proc/asset/viewer.hpp" + + + +namespace proc { +namespace play { + + class PlayProcess; +}} + + +namespace lumiera { + + namespace time = lib::time; + + using std::tr1::weak_ptr; + + + /****************************************************************** + * Interface to the Player subsystem of Lumiera (Proc-Layer). + * Global access point for starting playback and render processes, + * calculating media data by running the render engine. + * + * @todo WIP-WIP-WIP 6/2011 + * @note Lumiera is not yet able actually to deliver rendered data. + * @todo there should be an accompanying CL Interface defined for + * the Lumiera interface system, so the player can be + * accessed from external clients. This was left out + * for now, as we don't have either plugins, nor + * any script running capabilities yet. (5/2011) + */ + class Play + { + public: + + /** get an implementation instance of this service */ + static lumiera::facade::Accessor<Play> facade; + + + /** + * Continuous playback process, which has been hooked up + * and started with a fixed set of output slots. started with a specific + * output size, format and framerate. It is a handle to a calculation process, + * which is about to produce a stream of frames and push them to the outputs. + * + * The Lifecycle of the referred playback process is managed automatically + * through this handle (by ref count). Client code is supposed to use the + * API on this handle to navigate and control the playback mode. + * + * @see handle.hpp + * @see player-service.cpp implementation + */ + class Controller + : public lib::Handle<proc::play::PlayProcess> + { + public: + void play(bool); ///< play/pause toggle + void scrub(bool); ///< scrubbing playback + void adjustSpeed(double); ///< playback speed control + void go(time::Time); ///< skip to the given point in time + + void controlPlayhead (time::Control<time::Time> & ctrl); + void controlDuration (time::Control<time::Duration> & ctrl); + void controlLooping (time::Control<time::TimeSpan> & ctrl); + + void useProxyMedia (bool); + void setQuality (uint); + + bool is_playing() const; + bool is_scrubbing() const; + double getSpeed() const; + uint getQuality() const; + bool usesProxy() const; + + operator weak_ptr<proc::play::PlayProcess>() const; + }; + + + typedef lib::IterSource<proc::mobject::ModelPort>::iterator ModelPorts; + typedef lib::IterSource<proc::mobject::OutputDesignation>::iterator Pipes; + typedef proc::play::POutputManager Output; + 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 */ + virtual Controller connect(ModelPorts, Output) =0; + + + /* ==== convenience shortcuts for common use cases ==== */ + Controller perform(Pipes, Output); + Controller perform(Timeline); + Controller perform(Viewer); + Controller perform(Track); + Controller perform(Clip); + + protected: + virtual ~Play(); + }; + + +} // namespace lumiera + + + + +extern "C" { +#endif /* =========================== CL Interface ===================== */ + + +// #include "common/interface.h" +////////////////////////////////////TODO define a C binding for the Interface system here + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index fe64f516a..003773a51 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -54,13 +54,16 @@ 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 \ $(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 \ @@ -81,21 +84,17 @@ 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 \ $(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,31 @@ 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)/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 \ $(liblumiera_la_srcdir)/visitor-dispatcher.hpp \ $(liblumiera_la_srcdir)/visitor-policies.hpp \ $(liblumiera_la_srcdir)/visitor.hpp - 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/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/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<class TY> + size_t count() const; + + private: /** initiate an allocation for the given type */ template<class TY> @@ -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<MemoryManager> HMemManager; - typedef Allocator_TransferNoncopyable<HMemManager> Allo; + typedef Allocator_TransferNoncopyable<HMemManager> Allo; typedef std::vector<HMemManager,Allo> ManagerTable; - ManagerTable typeHandlers_; ///< table of active MemoryManager instances + ManagerTable typeHandlers_; ///< table of active MemoryManager instances - HMemManager& + + HMemManager& handler (size_t slot) { ASSERT (0<slot && slot<=typeHandlers_.size()); return typeHandlers_[slot-1]; } + HMemManager const& + handler (size_t slot) const + { + ASSERT (0<slot && slot<=typeHandlers_.size()); + return typeHandlers_[slot-1]; + } + /** implementation of the actual memory allocation * is pushed down to the MemoryManager impl. */ void* initiateAlloc (size_t& slot); @@ -188,6 +210,9 @@ namespace lib { /** enrol the allocation after successful ctor call */ void finishAlloc (size_t& slot, void*); + /** @internal helper for diagnostics, + * delegating to actual memory manager */ + size_t countActiveInstances (size_t& slot) const; }; @@ -275,6 +300,19 @@ namespace lib { } + /** helper for diagnostics + * @return number of currently allocated + * object instances of the given type + */ + template<class TY> + size_t + AllocationCluster::count() const + { + return countActiveInstances (TypeSlot<TY>::get()); + } + + + } // namespace lib diff --git a/src/lib/diagnostic-context.hpp b/src/lib/diagnostic-context.hpp index 28f97c5c1..5d3167fee 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, @@ -57,26 +57,23 @@ 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. - * @note as of 2/10 used for housing the NoBug resource tracker handle - * in conjunction with object monitor based locking - * @see sync.hpp + * @warning relies on thread-local access; never use this within global data structures. */ + 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 @@ -90,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); @@ -104,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 */ @@ -116,39 +113,37 @@ 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. + * @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() { - 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/element-tracker.hpp b/src/lib/element-tracker.hpp index d7d6c1794..160c6b5a2 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; /** @@ -115,7 +114,7 @@ namespace lib { { REQUIRE (asset, "Attempt to track a NIL element"); remove (*asset); - push_back (asset); + this->push_back (asset); } void @@ -125,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; } } @@ -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/error.hpp b/src/lib/error.hpp index e96942ee6..364fd1fe6 100644 --- a/src/lib/error.hpp +++ b/src/lib/error.hpp @@ -122,8 +122,11 @@ 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 (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 LUMIERA_ERROR_DECLARE (UNIMPLEMENTED);///< unimplemented feature @@ -214,6 +217,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/lib/exception.cpp b/src/lib/exception.cpp index 82e228687..53d068899 100644 --- a/src/lib/exception.cpp +++ b/src/lib/exception.cpp @@ -79,8 +79,11 @@ 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 (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"); LUMIERA_ERROR_DEFINE (UNIMPLEMENTED, "using a feature not yet implemented...."); diff --git a/src/lib/format-string.cpp b/src/lib/format-string.cpp new file mode 100644 index 000000000..85c3abc19 --- /dev/null +++ b/src/lib/format-string.cpp @@ -0,0 +1,259 @@ +/* + 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 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 dumps 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-util.hpp" +#include "lib/format-string.hpp" + +#include <boost/static_assert.hpp> +#include <boost/format.hpp> +#include <iostream> + + + + + +namespace util { + + using boost::format; + + + namespace { // implementation details... + + inline boost::format& + accessImpl (char* buffer) + { + return reinterpret_cast<boost::format&> (*buffer); + } + + + inline void + destroyImpl (char* buffer) + { + accessImpl(buffer).~format(); + } + + + /** in case the formatting of a (primitive) value fails, + * we try to supply an error indicator instead */ + 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 + + + + + + /** 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) + 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 () + { + destroyImpl (formatter_); + } + + + /** @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'' + * 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::format (const VAL val, Implementation& formatter) + 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) + { + _clear_errorflag(); + WARN (progress, "Format: Parameter '%s' causes problems: %s" + , cStr(str(val)) + , failure.what()); + pushFailsafeReplacement (formatter, failure.what()); + } + catch (...) + { + _clear_errorflag(); + WARN (progress, "Format: Unexpected problems accepting format parameter '%s'", cStr(str(val))); + pushFailsafeReplacement (formatter); + } + + + + + /* ===== explicitly supported =================== */ + + 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&); + + + + + /** @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 + try { + return accessImpl(formatter_).str(); + } + + 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>"; + } + + + + /** 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 string-stream to generate + * the formatted representation, relying on the C++ output framework + */ + std::ostream& + operator<< (std::ostream& os, _Fmt const& fmt) + try { + return os << accessImpl(fmt.formatter_); + } + + 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 new file mode 100644 index 000000000..716aa0e3d --- /dev/null +++ b/src/lib/format-string.hpp @@ -0,0 +1,421 @@ +/* + 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 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). + ** + ** \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-util.hpp + ** + */ + + +#ifndef UTIL_FORMAT_STRING_H +#define UTIL_FORMAT_STRING_H + +#include "lib/error.hpp" +#include "lib/meta/util.hpp" +#include "lib/meta/size-trait.hpp" + +#include <string> +#include <typeinfo> +#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 std::string; + using boost::enable_if; + + typedef unsigned char uchar; + + LUMIERA_ERROR_DECLARE (FORMAT_SYNTAX); ///< "Syntax error in format string for boost::format" + + + + /** + * 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 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_; + + + template<typename VAL> + static void format (const VAL, Implementation&); + + /** helper to prepare parameters for inclusion */ + template<typename VAL, class SEL =void> + struct Converter; + + + + public: + ~_Fmt (); + _Fmt (string formatString); + + operator string() const; ///< get the formatted result + + template<typename VAL> + _Fmt& + 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&, 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); } + }; + + + + + + /* ===== forwarding into the implementation ====== */ + + /** 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 a custom + * 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. + */ + template<typename VAL> + inline _Fmt& + _Fmt::operator% (VAL const& val) + { + Converter<VAL>::dump (val, formatter_); + return *this; + } + + + + namespace { // helpers to pick a suitable specialisation.... + + /** + * 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 _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<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_convert_toString + { + enum{ value = ! _shall_format_directly<X>::value + && lib::meta::can_convertToString<X>::value + }; + }; + + + 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()+">"; + } + catch(...) { /* secondary errors ignored */ } + return "(formatting failure)"; + } + + 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); + 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 === */ + + /** default/fallback: just indicate the (static) type */ + template<typename VAL, class SEL> + struct _Fmt::Converter + { + static void + dump (VAL const&, Implementation& impl) + { + 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); + } + }; + + /** some custom types explicitly provide a string representation */ + template<typename VAL> + struct _Fmt::Converter<VAL, typename enable_if< _shall_convert_toString<VAL> >::type> + { + static void + dump (VAL const& val, Implementation& impl) + try { + format (string(val), impl); + } + catch(std::exception const& ex) + { + format (_log_and_stringify(ex), impl); + } + catch(...) + { + format (_log_unknown_exception(), impl); + } + }; + + /** 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_format_directly<VAL> >::type> + { + static void + dump (const VAL val, Implementation& impl) + { + format (val, impl); + } + }; + + + + /* === comparison of formatter objects === */ + + 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 diff --git a/src/lib/format.hpp b/src/lib/format-util.hpp similarity index 55% rename from src/lib/format.hpp rename to src/lib/format-util.hpp index 4aa0a1743..c060299f7 100644 --- a/src/lib/format.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> @@ -21,20 +21,25 @@ */ -/** @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. + ** - 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,85 +51,55 @@ 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 ""; } - } - - template<typename X> - inline string - invoke_indirect2string ( typename disable_if< can_lexical2string<X>, - X >::type const&) - { - return ""; - } - } - - - - /** try to get an object converted to string. - * An 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. - */ - template<typename TY> - inline string - str ( TY const& val - , Symbol prefix="" ///< prefix to prepend in case conversion is possible - , Symbol fallback =0 /// < replacement text to show if string conversion fails - ) - { - if (can_ToString<TY>::value) - return string(prefix) + invoke_2string<TY>(val); - - else + struct use_LexicalConversion { - if (can_lexical2string<TY>::value) - { - string res (invoke_indirect2string<TY> (val)); - if ("" != res) - return string(prefix) + res; - } - - return fallback? string(fallback) - : tyStr(val); - } - } + 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> + 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 /** @return a string denoting the type. */ @@ -140,7 +115,28 @@ namespace util { 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 + * converted by a lexical_cast (based on operator<< ). + * Otherwise, either the fallback string is used, or just + * a string based on the (mangled) type. + */ + template<typename TY> + inline string + str ( TY const& val + , Symbol prefix="" ///< prefix to prepend in case conversion is possible + , Symbol fallback =0 /// < replacement text to show if string conversion fails + ) + { + string res = _InvokeFailsafe<TY>::toString(val); + if (!isnil (res)) + return string(prefix) + res; + else + return fallback? string(fallback) + : tyStr(val); + } } // namespace util - -#endif /*UTIL_FORMAT_H*/ +#endif 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/functor-util.hpp b/src/lib/functor-util.hpp index d47b86a9c..f10dc7b2b 100644 --- a/src/lib/functor-util.hpp +++ b/src/lib/functor-util.hpp @@ -38,11 +38,15 @@ #define FUNCTOR_UTIL_H_ #include <tr1/functional> -#include <cstring> +#include <boost/functional/hash.hpp> -namespace util { ////////////TODO: refactor it. But probably not directly into namespace lib. Needs some more consideration though +namespace lib { + typedef size_t HashVal; +} + +namespace util { ////////////TODO: refactor namespace. But probably not directly into namespace lib. Needs some more consideration though using std::tr1::function; using std::tr1::bind; @@ -86,7 +90,7 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n /** convenience shortcut to call two functors in sequence. * @return a Dispatch functor object which incorporates the - * functors as copy and on invocation calls the first + * functors as copy and on invocation calls the first * function and then returns the result of the second */ template<typename SIG> Dispatch<SIG> @@ -99,9 +103,12 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n namespace { // hiding some nasty details... + + using lib::HashVal; + using boost::hash_combine; /** - * This Class is used to surpass the access protection + * This Class is used to bypass the access protection * and break into the tr1::function implementation. * Thus we can implement a raw comparison function, * as a replacement for the missing functor comparison @@ -136,6 +143,16 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n && (f1._M_functor._M_unused._M_const_object == f2._M_functor._M_unused._M_const_object ); } // note: we don't cover any member pointer offset + + friend HashVal + hash_value (HijackedFunction const& fun) + { + HashVal hash(0); + hash_combine (hash, fun.invoker_); + hash_combine (hash, fun._M_manager); + hash_combine (hash, fun._M_functor._M_unused._M_const_object); + return hash; // note: member pointer offset part uncovered + } }; } @@ -184,6 +201,41 @@ namespace util { ////////////TODO: refactor it. But probably not directly into n } + /** workaround to calculate a hash value for a given function object. + * @note use with caution. This implementation relies on internal details + * of boost/function; it can be expected to be rather conservative, + * i.e. yielding different hash values for objects, which actually + * are semantically equivalent. + * @warning especially function objects bound to member functions aren't + * fully supported. It \em may happen that we miss differences on the + * offset part and only hash the "this" pointer on some platform. + */ + template<typename SIG> + inline HashVal + rawHashValue (function<SIG> const& fun) + { + typedef HijackedFunction const& Hij; + + return hash_value (reinterpret_cast<Hij> (fun)); + } + } // namespace util +namespace std { +namespace tr1 { + + /** inject into std::tr1 to be picked up by ADL: + * @return hash value of given functor + * @note use with caution. Hash is calculated + * relying on undocumented boost internals. + */ + template<typename SIG> + inline lib::HashVal + hash_value (function<SIG> const& fun) + { + return util::rawHashValue (fun); + } + +}} + #endif /*UTIL_HPP_*/ 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/lib/hash-fnv.c b/src/lib/hash-fnv.c new file mode 100644 index 000000000..1842b3819 --- /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 "lib/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..95d5c5aeb --- /dev/null +++ b/src/lib/hash-fnv.h @@ -0,0 +1,171 @@ +/* + HASH-FNV.h - 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: +*/ diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index a8306a774..c2ac2eafe 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -22,35 +22,50 @@ /** @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 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 ** 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 within 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,13 +73,11 @@ ** 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. ** - ** @todo naming of the iteration control function: TICKET #410 - ** ** @see iter-adapter-test.cpp ** @see itertools.hpp ** @see IterSource (completely opaque iterator) @@ -122,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 @@ -146,7 +159,7 @@ namespace lib { : source_(src) , pos_(startpos) { - checkPos(); + check(); } IterAdapter () @@ -179,19 +192,10 @@ namespace lib { return *this; } - IterAdapter - operator++(int) - { - _maybe_throw(); - IterAdapter oldPos(*this); - iterate(); - return oldPos; - } - bool isValid () const { - return checkPos(); + return check(); } bool @@ -209,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 { @@ -253,6 +254,129 @@ 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 + * a set of free function to be resolved by ADL. + * + * \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 <b>iteration control API</b> 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 + */ + template<typename T, class ST =T> + class IterStateWrapper + : public lib::BoolCheckable<IterStateWrapper<T,ST> > + { + ST core_; + + public: + typedef T* pointer; + typedef T& reference; + typedef T value_type; + + IterStateWrapper (ST const& initialState) + : core_(initialState) + { + checkPoint (core_); + } + + IterStateWrapper () + : core_() + { } + + + /* === lumiera forward iterator concept === */ + + reference + operator*() const + { + __throw_if_empty(); + return yield (core_); // extension point: yield + } + + pointer + operator->() const + { + __throw_if_empty(); + return & yield(core_); // extension point: yield + } + + IterStateWrapper& + operator++() + { + __throw_if_empty(); + iterNext (core_); // extension point: iterNext + return *this; + } + + bool + isValid () const + { + return checkPoint(core_); // extension point: checkPoint + } + + bool + empty () const + { + return !isValid(); + } + + protected: + + /** allow derived classes to + * access state representation */ + ST & + stateCore() + { + return core_; + } + + void + __throw_if_empty() const + { + if (!isValid()) + _throwIterExhausted(); + } + + + /// comparison is allowed to access state implementation core + template<class T1, class T2, class STX> + friend bool operator== (IterStateWrapper<T1,STX> const&, IterStateWrapper<T2,STX> const&); + }; + + + + /// Supporting equality comparisons of equivalent iterators (same state type)... + template<class T1, class T2, class ST> + bool operator== (IterStateWrapper<T1,ST> const& il, IterStateWrapper<T2,ST> const& ir) + { + return (il.empty() && ir.empty()) + || (il.isValid() && ir.isValid() && il.core_ == ir.core_); + } + + template<class T1, class T2, class ST> + bool operator!= (IterStateWrapper<T1,ST> const& il, IterStateWrapper<T2,ST> const& ir) + { + return ! (il == ir); + } + + + + + @@ -322,13 +446,6 @@ namespace lib { return *this; } - RangeIter - operator++(int) - { - _maybe_throw(); - return RangeIter (p_++,e_); - } - bool isValid () const { @@ -408,7 +525,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 +562,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 +584,7 @@ namespace lib { /** PtrDerefIter is always created * by wrapping an existing iterator. */ + explicit PtrDerefIter (IT srcIter) : i_(srcIter) { } @@ -485,9 +608,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 === */ @@ -511,12 +656,6 @@ namespace lib { return *this; } - PtrDerefIter - operator++(int) - { - return PtrDerefIter (i_++); - } - bool isValid () const { diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp new file mode 100644 index 000000000..7c9868f4f --- /dev/null +++ b/src/lib/iter-explorer.hpp @@ -0,0 +1,986 @@ +/* + ITER-EXPLORER.hpp - building blocks for iterator evaluation strategies + + 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 iter-explorer.hpp + ** Helper template(s) for establishing various evaluation strategies for hierarchical data structures. + ** Based on the <b>Lumiera Forward Iterator</b> 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 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 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 + ** 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) + ** + ** \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. + ** - 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" 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. + ** 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 + ** @see itertools.hpp + ** @see IterSource (completely opaque iterator) + ** + */ + + +#ifndef LIB_ITER_EXPLORER_H +#define LIB_ITER_EXPLORER_H + + +#include "lib/error.hpp" +#include "lib/meta/function.hpp" +#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 <boost/utility/enable_if.hpp> +#include <stack> + + +namespace lib { + + namespace iter_explorer { + + template<class SRC, class FUN> + class DefaultCombinator; + } + + + + /** + * 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. 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 + * 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" 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. + * @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 SRC + ,template<class,class> class _COM_ = iter_explorer::DefaultCombinator + > + class IterExplorer + : public IterStateWrapper<typename SRC::value_type, SRC> + { + + + public: + typedef typename SRC::value_type value_type; + typedef typename SRC::reference reference; + typedef typename SRC::pointer pointer; + + /** 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<class FUN> + struct FlatMapped + { + typedef IterExplorer<_COM_<IterExplorer,FUN>, _COM_> Type; + }; + + + + /** 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 + * by the core, and it provides the (monad) bind operator. + */ + explicit + IterExplorer (SRC const& iterStateCore) + : IterStateWrapper<value_type, SRC> (iterStateCore) + { } + + + /** 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<class FUN> + typename FlatMapped<FUN>::Type + operator >>= (FUN explorer) + { + typedef _COM_<IterExplorer,FUN> Combinator; // instantiation of the combinator strategy + typedef typename FlatMapped<FUN>::Type ResultsIter; // result IterExplorer using that instance as state core + + return ResultsIter ( + Combinator (explorer // build a new iteration state core + ,accessRemainingElements())); // based on a copy of this iterator / sequence + } + + + + private: + IterExplorer const& + accessRemainingElements() + { + return *this; + } + }; + + + + + + + + + + + namespace iter_explorer { ///< predefined policies and configurations + + using util::unConst; + using boost::enable_if; + using boost::disable_if; + using std::tr1::function; + using meta::_Fun; + + + /** + * 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 SIG> + struct ExploreByFunction + : function<SIG> + { + template<typename FUN> + ExploreByFunction(FUN explorationFunctionDefinition) + : function<SIG>(explorationFunctionDefinition) + { } + + 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<class SIG> + struct UnalteredPassThrough; + + template<class IT> + struct UnalteredPassThrough<IT(IT)> + { + IT operator() (IT elm) const { return elm; } + bool operator! () const { return false; } ///< identity function is always valid + }; + + + + /** + * 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 SRC, class FUN + ,template<class> class _EXP_ = ExploreByFunction ///< Strategy: how to store and evaluate the function to apply on each element + > + class CombinedIteratorEvaluation + { + typedef typename _Fun<FUN>::Ret ResultIter; + typedef typename SRC::value_type SrcElement; + typedef _EXP_<ResultIter(SrcElement)> Explorer; + + + SRC srcSeq_; + ResultIter results_; + Explorer explorer_; + + public: + typedef typename ResultIter::value_type value_type; + typedef typename ResultIter::reference reference; + typedef typename ResultIter::pointer pointer; + + + CombinedIteratorEvaluation() { } + + CombinedIteratorEvaluation(FUN explorerFunction) + : srcSeq_() + , results_() + , explorer_(explorerFunction) + { } + + // using standard copy operations + + + void + setSourceSequence (SRC const& followUpSourceElements) + { + 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 (CombinedIteratorEvaluation const& seq) + { + return unConst(seq).findNextResultElement(); + } + + friend reference + yield (CombinedIteratorEvaluation const& seq) + { + return *(seq.results_); + } + + friend void + iterNext (CombinedIteratorEvaluation & seq) + { + ++(seq.results_); + } + }; + + /** + * a generic "Combinator strategy" for IterExplorer. + * 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) + * 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 SRC, class FUN> + class DefaultCombinator + : public CombinedIteratorEvaluation<SRC,FUN> + { + typedef typename _Fun<FUN>::Ret ResultIter; + + public: + DefaultCombinator() { } + + DefaultCombinator(FUN explorerFunction, SRC const& sourceElements) + : CombinedIteratorEvaluation<SRC,FUN>(explorerFunction) + { + this->setSourceSequence (sourceElements); + } + }; + + + /** Metafunction to detect an iterator yielding an iterator sequence */ + template<class IT> + struct _is_iterator_of_iterators + { + typedef typename IT::value_type IteratorElementType; + + enum{ value = meta::can_IterForEach<IteratorElementType>::value }; + }; + + + template<class ITI, class SEQ> + class ChainedIteratorImpl + : public CombinedIteratorEvaluation<ITI, SEQ(SEQ) + , UnalteredPassThrough + > + { }; + + + /** + * 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 ITI, class SEL = void> + class ChainedIters; + + template<class ITI> + class ChainedIters<ITI, typename enable_if< _is_iterator_of_iterators<ITI> >::type + > + : public IterStateWrapper<typename ITI::value_type::value_type + ,ChainedIteratorImpl<ITI, typename ITI::value_type> + > + { + 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 SEQ> + class ChainedIters<SEQ, typename disable_if< _is_iterator_of_iterators<SEQ> >::type + > + : public IterStateWrapper<typename SEQ::value_type + ,ChainedIteratorImpl<IterStack<SEQ>, SEQ> + > + { + public: + typedef IterStack<SEQ> IteratorIterator; + + ChainedIters(IteratorIterator const& iteratorOfIterators) + { + this->stateCore().setSourceSequence (iteratorOfIterators); + } + + /** empty result sequence by default */ + 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. (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 + * 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. + * - \c Strategy::getFeed() accesses the point from where + * to pull the next element to be expanded. This function <i>must not</i> + * yield an empty sequence, \em unless the overall exploration is exhausted + * - \c Strategy::feedBack() re-integrates the results of an expansion step + */ + template<class SRC, class FUN + ,template<class> class _BUF_ + > + class RecursiveExhaustingEvaluation + { + typedef typename _Fun<FUN>::Ret ResultIter; + typedef typename SRC::value_type Val; + typedef function<ResultIter(Val)> Explorer; + typedef _BUF_<ResultIter> Buffer; + + Buffer resultBuf_; + Explorer explore_; + + + public: + typedef typename ResultIter::value_type value_type; + typedef typename ResultIter::reference reference; + typedef typename ResultIter::pointer pointer; + + RecursiveExhaustingEvaluation (Explorer fun, SRC const& src) + : resultBuf_() + , explore_(fun) + { + resultBuf_.feedBack( + initEvaluation (src)); + } + + RecursiveExhaustingEvaluation() { }; + + // standard copy operations + + + private: + /** 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) + { + 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); + } + + + /* === Iteration control API for IterStateWrapper== */ + + friend bool + checkPoint (RecursiveExhaustingEvaluation const& seq) + { + return bool(seq.feed()); + } + + friend reference + yield (RecursiveExhaustingEvaluation const& seq) + { + return *(seq.feed()); + } + + friend void + iterNext (RecursiveExhaustingEvaluation & seq) + { + seq.iterate(); + } + }; + + + /** + * 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 IT + , template<class> class _QUEUE_ ///< the actual container to use for storage of intermediary results + > + class EvaluationBufferStrategy + { + _QUEUE_<IT> 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<IT>::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_.insert (newEvaluationResults); + } + }; + + + + /** + * concrete strategy for recursive \em depth-first evaluation. + * Using heap allocated storage in a STL Deque (used stack-like) + */ + template<class IT> + struct DepthFirstEvaluationBuffer + : EvaluationBufferStrategy<IT, IterStack> + { }; + + /** + * concrete strategy for recursive \em breadth-first evaluation. + * Using heap allocated storage in a STL Deque (used queue-like) + */ + template<class IT> + struct BreadthFirstEvaluationBuffer + : EvaluationBufferStrategy<IT, IterQueue> + { }; + + + + /** + * preconfigured IterExplorer "state core" resulting in + * depth-first exhaustive evaluation + */ + template<class SRC, class FUN> + struct DepthFirstEvaluationConbinator + : RecursiveExhaustingEvaluation<SRC, FUN, DepthFirstEvaluationBuffer> + { + DepthFirstEvaluationConbinator() { } + + DepthFirstEvaluationConbinator(FUN explorerFunction, SRC const& sourceElements) + : RecursiveExhaustingEvaluation<SRC, FUN, DepthFirstEvaluationBuffer> (explorerFunction,sourceElements) + { } + }; + + /** + * preconfigured IterExplorer "state core" resulting in + * breadth-first exhaustive evaluation + */ + template<class SRC, class FUN> + struct BreadthFirstEvaluationConbinator + : RecursiveExhaustingEvaluation<SRC, FUN, BreadthFirstEvaluationBuffer> + { + BreadthFirstEvaluationConbinator() { } + + BreadthFirstEvaluationConbinator(FUN explorerFunction, SRC const& sourceElements) + : RecursiveExhaustingEvaluation<SRC, FUN, BreadthFirstEvaluationBuffer> (explorerFunction,sourceElements) + { } + }; + + + + + /** + * 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 <i>special result sequence</i> 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 SRC, class FUN> + class RecursiveSelfIntegration + { + typedef typename _Fun<FUN>::Ret ResultIter; + typedef typename SRC::value_type Val; + typedef function<ResultIter(Val)> 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 nextSteps = explore_(*outSeq_); + ++ outSeq_; + build(outSeq_).usingSequence(nextSteps); // 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 + * 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 IT> + class WrappedSequence + : public IT + { + public: + WrappedSequence() + : IT() + { } + + WrappedSequence(IT const& srcIter) + : IT(srcIter) + { } + + + /* === Iteration control API for IterStateWrapper == */ + + friend bool + checkPoint (WrappedSequence const& sequence) + { + return bool(sequence); + } + + friend typename IT::reference + yield (WrappedSequence const& sequence) + { + return *sequence; + } + + friend void + iterNext (WrappedSequence & sequence) + { + ++sequence; + } + }; + + + template<class SRC> + struct DepthFirst + : IterExplorer<WrappedSequence<SRC>, DepthFirstEvaluationConbinator> + { + DepthFirst() { }; + DepthFirst(SRC const& srcSeq) + : IterExplorer<WrappedSequence<SRC>, DepthFirstEvaluationConbinator> (srcSeq) + { } + }; + + template<class SRC> + struct BreadthFirst + : IterExplorer<WrappedSequence<SRC>, BreadthFirstEvaluationConbinator> + { + BreadthFirst() { }; + BreadthFirst(SRC const& srcSeq) + : IterExplorer<WrappedSequence<SRC>, BreadthFirstEvaluationConbinator> (srcSeq) + { } + }; + + + }//(End) namespace iter_explorer : predefined policies and configurations + + + + + /* ==== convenient builder free functions ==== */ + + template<class IT> + inline IterExplorer<iter_explorer::WrappedSequence<IT> > + exploreIter (IT const& srcSeq) + { + return IterExplorer<iter_explorer::WrappedSequence<IT> > (srcSeq); + } + + + template<class IT> + inline iter_explorer::DepthFirst<IT> + depthFirst (IT const& srcSeq) + { + return iter_explorer::DepthFirst<IT> (srcSeq); + } + + + template<class IT> + inline iter_explorer::BreadthFirst<IT> + breadthFirst (IT const& srcSeq) + { + return iter_explorer::BreadthFirst<IT> (srcSeq); + } + + + + template<class IT> + inline iter_explorer::ChainedIters<IT> + iterChain(IT const& seq) + { + typename iter_explorer::ChainedIters<IT>::IteratorIterator sequenceOfIterators; + + sequenceOfIterators.push (seq); + return iter_explorer::ChainedIters<IT>(sequenceOfIterators); + } + + template<class IT> + inline iter_explorer::ChainedIters<IT> + iterChain(IT const& seq1, IT const& seq2) + { + typename iter_explorer::ChainedIters<IT>::IteratorIterator sequenceOfIterators; + + sequenceOfIterators.push (seq2); + sequenceOfIterators.push (seq1); + return iter_explorer::ChainedIters<IT>(sequenceOfIterators); + } + + template<class IT> + inline iter_explorer::ChainedIters<IT> + iterChain(IT const& seq1, IT const& seq2, IT const& seq3) + { + typename iter_explorer::ChainedIters<IT>::IteratorIterator sequenceOfIterators; + + sequenceOfIterators.push (seq3); + sequenceOfIterators.push (seq2); + sequenceOfIterators.push (seq1); + return iter_explorer::ChainedIters<IT>(sequenceOfIterators); + } + + template<class IT> + inline iter_explorer::ChainedIters<IT> + iterChain(IT const& seq1, IT const& seq2, IT const& seq3, IT const& seq4) + { + typename iter_explorer::ChainedIters<IT>::IteratorIterator sequenceOfIterators; + + sequenceOfIterators.push (seq4); + sequenceOfIterators.push (seq3); + sequenceOfIterators.push (seq2); + sequenceOfIterators.push (seq1); + return iter_explorer::ChainedIters<IT>(sequenceOfIterators); + } + + template<class IT> + inline iter_explorer::ChainedIters<IT> + iterChain(IT const& seq1, IT const& seq2, IT const& seq3, IT const& seq4, IT const& seq5) + { + typename iter_explorer::ChainedIters<IT>::IteratorIterator sequenceOfIterators; + + sequenceOfIterators.push (seq5); + sequenceOfIterators.push (seq4); + sequenceOfIterators.push (seq3); + sequenceOfIterators.push (seq2); + sequenceOfIterators.push (seq1); + return iter_explorer::ChainedIters<IT>(sequenceOfIterators); + } + + + + +} // namespace lib +#endif diff --git a/src/lib/iter-source.hpp b/src/lib/iter-source.hpp index 7910ecd5c..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); } @@ -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 an IterSource. + * This convenience shortcut can be used to build a + * processing chain; the resulting IterSource will + * hide any detail types involved. + * @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/lib/iter-stack.hpp b/src/lib/iter-stack.hpp new file mode 100644 index 000000000..b67adce35 --- /dev/null +++ b/src/lib/iter-stack.hpp @@ -0,0 +1,285 @@ +/* + ITER-STACK.hpp - a stack which can be popped by iterating + + 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 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. + ** + ** \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 + ** 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 +#define LIB_ITER_STACK_H + + +#include "lib/error.hpp" +#include "lib/iter-adapter.hpp" +#include "lib/nocopy.hpp" +#include "lib/util.hpp" + +#include <deque> + + +namespace lib { + + namespace { + using util::unConst; + + /** + * Wrapper to mark a std::deque instance for use + * as "state core" within lib::IterStateWrapper. + * Iteration will pop elements from the back side. + */ + template<class TY> + struct IterDequeStorage + : std::deque<TY> + { + /* === Iteration control API for IterStateWrapper == */ + + friend bool + checkPoint (IterDequeStorage const& elements) + { + return !elements.empty(); + } + + friend TY & + yield (IterDequeStorage const& elements) + { + REQUIRE (!elements.empty()); + return unConst(elements).back(); + } + + friend void + iterNext (IterDequeStorage & elements) + { + REQUIRE (!elements.empty()); + elements.pop_back(); + } + }; + }//(End) Wrapper/Helper + + + + /** + * A Stack which can be popped by iterating. + * 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. + * + * 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<class TY> + struct IterStack + : IterStateWrapper<TY, IterDequeStorage<TY> > + { + + // using default create and copy operations + + IterStack& + push (TY const& elm) + { + this->stateCore().push_back (elm); + return *this; + } + + IterStack& + insert (TY const& elm) + { + return push(elm); + } + + + TY + pop() + { + this->__throw_if_empty(); + TY topElement (this->stateCore().back()); + this->stateCore().pop_back(); + 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<class TY> + struct IterQueue + : IterStateWrapper<TY, IterDequeStorage<TY> > + { + + // using default create and copy operations + + IterQueue& + feed (TY const& elm) + { + this->stateCore().push_front (elm); + return *this; + } + + IterQueue& + insert (TY const& elm) + { + return feed(elm); + } + + 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 + : util::no_copy_by_client + { + Builder(IterQueue& initialElements) + : queue_(initialElements) + { } + // standard copy operations allowed + + template<typename IT> + IterQueue & + usingSequence (IT src) + { + for ( ; src; ++src ) + queue_.feed (*src); + return queue_; + } + + IterQueue & + wrapping (TY const& elm) + { + queue_.feed (elm); + 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& initial) + { + return Builder(initial); + } + }; + + + /** convenience free function to build an iterable sequence */ + template<typename T> + inline IterQueue<T> + elements (T const& elm) + { + return IterQueue<T>().feed(elm); + } + + template<typename T> + inline IterQueue<T> + elements (T const& e0, T const& e1) + { + return IterQueue<T>().feed(e0).feed(e1); + } + + template<typename T> + inline IterQueue<T> + elements (T const& e0, T const& e1, T const& e2) + { + return IterQueue<T>().feed(e0).feed(e1).feed(e2); + } + + template<typename T> + inline IterQueue<T> + elements (T const& e0, T const& e1, T const& e2, T const& e3) + { + return IterQueue<T>().feed(e0).feed(e1).feed(e2).feed(e3); + } + + template<typename T> + inline IterQueue<T> + elements (T const& e0, T const& e1, T const& e2, T const& e3, T const& e4) + { + return IterQueue<T>().feed(e0).feed(e1).feed(e2).feed(e3).feed(e4); + } + + + +} // namespace lib +#endif diff --git a/src/lib/itertools.hpp b/src/lib/itertools.hpp index a4efc4829..8a4e78e96 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 @@ -331,13 +331,6 @@ namespace lib { FilterIter (IT const& src, PRED filterPredicate) : _Impl(_Filter(src,filterPredicate)) { } - - FilterIter& - operator++ () - { - _Impl::operator++(); - return *this; - } }; @@ -478,20 +471,13 @@ namespace lib { TransformIter (IT const& src, FUN trafoFunc) : _IteratorImpl(_Trafo(src,trafoFunc)) { } - - TransformIter& - operator++ () - { - _IteratorImpl::operator++(); - return *this; - } }; namespace { // Helper to pick up the produced value type automatically - using lumiera::typelist::FunctionSignature; + using lib::meta::FunctionSignature; template<typename SIG> struct _ProducedOutput @@ -540,10 +526,35 @@ namespace lib { } + template<class IT> + inline typename IT::value_type + pull_last (IT iter) + { + typedef typename IT::value_type Val; + typedef wrapper::ItemWrapper<Val> 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<class IT> - FilterIter<IT> + inline FilterIter<IT> filterRepetitions (IT const& source) { typedef typename IT::value_type Val; 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; diff --git a/src/lib/linked-elements.hpp b/src/lib/linked-elements.hpp new file mode 100644 index 000000000..0e654c65e --- /dev/null +++ b/src/lib/linked-elements.hpp @@ -0,0 +1,597 @@ +/* + LINKED-ELEMENTS.hpp - configurable intrusive single linked list template + + 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 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 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 + ** - convenient access through Lumiera Forward Iterators + ** - the need to integrate tightly with a custom allocator + ** + ** @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 + ** @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 "lib/util.hpp" + +#include <boost/noncopyable.hpp> +#include <boost/static_assert.hpp> + + + +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 + + /** + * 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<class X> + void + destroy (X* elm) + { + delete elm; + } + + + /** this policy creates new elements + * simply by heap allocation */ + template<class TY> + TY& + create () + { + return *new TY(); + } + + + template<class TY, typename A1> + 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<class TY> + TY& //__________________________________________ + create () ///< place object of type TY, by default ctor + { + return cluster_.create<TY>(); + } + + + template<class TY, typename A1> + TY& //___________________________________________ + create (A1 a1) ///< place object of type TY, using 1-arg ctor + { + return cluster_.create<TY> (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<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 + { + return cluster_.create<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 + { + return cluster_.create<TY> (a1,a2,a3,a4); + } + }; + +#endif //(End)if lib/allocation-cluster.hpp was included + + }//(END)namespace linked_elements + + + + + + + + + + + /** + * 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 ALO = linked_elements::OwningHeapAllocated + > + class LinkedElements + : boost::noncopyable + , ALO + { + N* head_; + + + public: + + ~LinkedElements () + { + clear(); + } + + LinkedElements () + : head_(0) + { } + + /** @param allo custom allocator or memory manager + * to be used by the policy for creating and + * discarding of node elements + */ + explicit + LinkedElements (typename ALO::CustomAllocator allo) + : ALO(allo) + , head_(0) + { } + + /** 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<class IT> + LinkedElements (IT elements) + : head_(0) + { + try { + pushAll(elements); + } + catch(...) + { + WARN (progress, "Failure while populating LinkedElements list. " + "All elements will be discarded"); + clear(); + throw; + } } + + + + /** @note EX_FREE */ + void + clear() + { + while (head_) + { + N* elm = head_; + head_ = head_->next; + try { + ALO::destroy(elm); + } + ERROR_LOG_AND_IGNORE (progress, "Clean-up of element in LinkedElements list") + } + } + + + /** convenience shortcut to add all the elements + * yielded by the given Lumiera Forward Iterator + * @note EX_SANE + */ + template<class IT> + void + pushAll (IT elements) + { + for ( ; elements; ++elements ) + push (*elements); + } + + + + + /** 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<typename TY> + TY& + push (TY& elm) + { + elm.next = head_; + head_ = &elm; + return elm; + } + + + template< class TY > + TY& //_________________________________________ + pushNew () ///< add object of type TY, using 0-arg ctor + { + return push (ALO::template create<TY>()); + } + + + template< class TY + , typename A1 + > + TY& //_________________________________________ + pushNew (A1 a1) ///< add object of type TY, using 1-arg ctor + { + return push (ALO::template create<TY>(a1)); + } + + + template< class TY + , typename A1 + , typename A2 + > + TY& //_________________________________________ + pushNew (A1 a1, A2 a2) ///< add object of type TY, using 2-arg ctor + { + return push (ALO::template create<TY>(a1,a2)); + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + > + TY& //_________________________________________ + pushNew (A1 a1, A2 a2, A3 a3) ///< add object of type TY, using 3-arg ctor + { + return push (ALO::template create<TY>(a1,a2,a3)); + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + , typename A4 + > + TY& //_________________________________________ + pushNew (A1 a1, A2 a2, A3 a3, A4 a4) ///< add object of type TY, using 4-arg ctor + { + return push (ALO::template create<TY>(a1,a2,a3,a4)); + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + , typename A4 + , typename A5 + > + TY& //_________________________________________ + pushNew (A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) ///< add object of type TY, using 5-arg ctor + { + return push (ALO::template create<TY>(a1,a2,a3,a4,a5)); + } + + + + + + /* === Element access and iteration === */ + + N& + operator[] (size_t index) const + { + N* p = head_; + while (p && index) + { + p = p->next; + --index; + } + + 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_; + } + + 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; + + IterationState(N* p=0) + : node(p) + { } + + friend bool + operator== (IterationState const& il, IterationState const& ir) + { + return il.node == ir.node; + } + }; + + public: + typedef IterStateWrapper< N, IterationState> iterator; + typedef IterStateWrapper<const N, IterationState> const_iterator; + + + iterator begin() { return iterator (head_); } + const_iterator begin() const { return const_iterator (head_); } + iterator end () { return iterator(); } + const_iterator end () const { return const_iterator(); } + + + + private: /* ==== internal callback API for the iterator ==== */ + + /** Iteration-logic: switch to next position + * @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 (IterationState & pos) + { + pos.node = pos.node->next; + } + + friend void + iterNext (IterationState const& pos) + { + pos.node = pos.node->next; + } + + /** Iteration-logic: detect iteration end. */ + friend bool + checkPoint (IterationState const& pos) + { + return bool(pos.node); + } + + friend N& + yield (IterationState const& pos) + { + REQUIRE (pos.node); + return * unConst(pos).node; + } + }; + + + + +} // 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/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/maybe.hpp b/src/lib/maybe.hpp new file mode 100644 index 000000000..f8e7204ff --- /dev/null +++ b/src/lib/maybe.hpp @@ -0,0 +1,121 @@ +/* + 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 "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/src/lib/meta/configflags.hpp b/src/lib/meta/configflags.hpp index 16c81a7bb..b80dae229 100644 --- a/src/lib/meta/configflags.hpp +++ b/src/lib/meta/configflags.hpp @@ -45,28 +45,28 @@ */ -#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; - 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 typelist{ }; - 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,31 +95,31 @@ namespace typelist{ - 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> > { - 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; 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 typelist{ 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 typelist{ 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 @@ -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 0091a894f..f2b36c069 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 @@ -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 @@ -76,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 - - @@ -182,22 +188,37 @@ namespace meta{ -/** Detector for a prefix increment operator. Works like function detection */ -#define META_DETECT_OPERATOR_INC() \ - template<typename TY> \ - class HasOperator_inc \ - { \ - template<typename X, X& (X::*)(void)> \ - struct Probe \ - { }; \ - \ - template<class X> \ - static Yes_t check(Probe<X, &X::operator++> * ); \ - template<class> \ - static No_t check(...); \ - \ - public: \ - static const bool value = (sizeof(Yes_t)==sizeof(check<TY>(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<typename TY> \ + class HasOperator_inc \ + { \ + template<typename X, X& (X::*)(void)> \ + struct Probe_1 \ + { }; \ + template<typename X, int i = sizeof(&X::operator++)> \ + struct Probe_2 \ + { }; \ + \ + template<class X> \ + static Yes_t check1(Probe_1<X, &X::operator++> * ); \ + template<class> \ + static No_t check1(...); \ + template<class X> \ + static Yes_t check2(Probe_2<X> * ); \ + template<class> \ + static No_t check2(...); \ + \ + public: \ + static const bool value = (sizeof(Yes_t)==sizeof(check1<TY>(0)) \ + ||sizeof(Yes_t)==sizeof(check2<TY>(0))); \ }; diff --git a/src/lib/meta/function-closure.hpp b/src/lib/meta/function-closure.hpp index 374c75883..081e9160f 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; @@ -67,32 +67,6 @@ namespace func { using tuple::element; - template<typename SIG> - struct _Fun - { - typedef typename FunctionSignature<function<SIG> >::Ret Ret; - typedef typename FunctionSignature<function<SIG> >::Args Args; - }; - template<typename SIG> - struct _Fun<SIG*> - { - typedef typename FunctionSignature<function<SIG> >::Ret Ret; - typedef typename FunctionSignature<function<SIG> >::Args Args; - }; - template<typename SIG> - struct _Fun<function<SIG> > - { - typedef typename FunctionSignature<function<SIG> >::Ret Ret; - typedef typename FunctionSignature<function<SIG> >::Args Args; - }; - - template<typename FUN> - struct is_Functor { static const bool value = false; }; - template<typename SIG> - struct is_Functor<function<SIG> > { 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 @@ -467,8 +441,8 @@ namespace func { template<typename SIG> class FunctionClosure { - typedef typename func::_Fun<SIG>::Args Args; - typedef typename func::_Fun<SIG>::Ret Ret; + typedef typename _Fun<SIG>::Args Args; + typedef typename _Fun<SIG>::Ret Ret; function<Ret(void)> closure_; @@ -673,8 +647,8 @@ namespace func { template<typename F1, typename RET> class FunctionComposition { - typedef typename func::_Fun<F1>::Args Args; - typedef typename func::_Fun<F1>::Ret Ret1; + typedef typename _Fun<F1>::Args Args; + typedef typename _Fun<F1>::Ret Ret1; typedef Types<Ret1> ArgsF2; typedef typename FunctionTypedef<RET, ArgsF2>::Sig SigF2; @@ -796,10 +770,11 @@ 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> + inline typename _Sig<void, ARG>::Applicator tupleApplicator (Tuple<ARG>& args) { @@ -810,6 +785,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 +800,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 +813,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 +826,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 +841,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 +855,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) { @@ -884,5 +865,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 6e04effc2..f2ed457e4 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"). @@ -51,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" @@ -63,8 +64,8 @@ #include <tr1/functional> -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta{ using std::tr1::function; using util::unConst; @@ -239,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..87f7379df 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; @@ -209,6 +209,164 @@ namespace typelist{ + + + /** + * 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 when using a function reference */ + 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> > + { + typedef typename FunctionSignature<function<SIG> >::Ret Ret; + 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> + struct is_Functor<function<SIG> > { static const bool value = true; }; + + + + + + + + /** * Build function types from given Argument types. * As embedded typedefs, you'll find a tr1 functor #Func @@ -357,5 +515,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 new file mode 100644 index 000000000..bbb85ed0e --- /dev/null +++ b/src/lib/meta/generator-combinations.hpp @@ -0,0 +1,101 @@ +/* + GENERATOR-COMBINATIONS.hpp - generate combinations and variations + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file generator-combinations.hpp + ** Metaprogramming facilities to generate combination cases. + ** Similar to the plain typelist-based generators, a custom supplied + ** template will be instantiated with combinations of the parameter types + ** and then mixed into the resulting type + ** + ** @see generator-combinations-test.cpp + ** @see generator.hpp + ** + */ + + +#ifndef LIB_META_GENERATOR_COMBINATIONS_H +#define LIB_META_GENERATOR_COMBINATIONS_H + +#include "lib/meta/typelist.hpp" +#include "lib/meta/typelist-manip.hpp" +#include "lib/meta/generator.hpp" + + + +namespace lib { +namespace meta{ + + + template<class TYPES_1, class TYPES_2> + struct CartesianProduct + : Distribute< typename TYPES_1::List + , typename TYPES_2::List + > + { }; + + + + template< template<class,class,class> class _X_> + struct PickParametersFromSublist + { + template<class SUBLIST, class BASE> + struct SingleCaseInstantiation + : _X_< typename Pick<SUBLIST,0>::Type + , typename Pick<SUBLIST,1>::Type + , BASE + > + { }; + }; + + + /** + * Build a Case matrix. + * The given parameter template _X_ + * will be instantiated for each possible combination + * of the elements from both parameter type-lists. + * All these instantiations will be chained up + * into a linear inheritance chain rooted + * at the BASE type. + * @note the custom-supplied template _X_ needs to take a 3rd parameter, + * and inherit from this parameter, in order to form that chain. + * Typically you'll define some (static) functions within that + * template, which then forward the call to the given BASE + * (and of course, that BASE then needs to define this + * function as well). + */ + template + < class TYPES_1, class TYPES_2 ///< the two type collections to pick combinations from + , template<class,class,class> class _X_ ///< template with two arg types and a base type + , class BASE = NullType + > + struct InstantiateChainedCombinations + : InstantiateChained< typename CartesianProduct<TYPES_1,TYPES_2>::List + , PickParametersFromSublist<_X_>::template SingleCaseInstantiation + , BASE + > + { }; + + + +}} // namespace 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/size-trait.hpp b/src/lib/meta/size-trait.hpp new file mode 100644 index 000000000..1abbb046b --- /dev/null +++ b/src/lib/meta/size-trait.hpp @@ -0,0 +1,149 @@ +/* + 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; + typedef std::vector<bool> BVector; + + 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; + + virtual ~BasicStringbuf() { } + }; + + struct BasicAltstringbuf + : BasicStringbuf + { + char * putend_; + bool is_allocated_; + IOS_Openmode mode_; + CompatAllocator alloc_; + }; + + struct BoostFormat + { + 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_; + Optional<Locale> loc_; + }; + //-------------------------------------mimicked-definitions-- + + + public:/* ===== Interface: size constants ===== */ + + enum { ALIGNMENT = sizeof(size_t) + + , STRING = sizeof(std::string) + , VECTOR = sizeof(Vector) + , BVECTOR = sizeof(BVector) + + , BOOST_FORMAT = sizeof(BoostFormat) + }; + }; + + +}} // namespace lib::meta +#endif diff --git a/src/lib/meta/trait.hpp b/src/lib/meta/trait.hpp index ee77db289..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 lumiera{ - template<class X, class B> class P; +namespace lib{ + template<class X, class B> class P; } namespace mobject{ - template<class X, class B> class Placement; + template<class X, class B> class Placement; } @@ -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/meta/tuple.hpp b/src/lib/meta/tuple.hpp index 38d5def29..73e633f1b 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 { @@ -283,6 +283,7 @@ namespace typelist{ namespace tuple { // some convenience access functions template<uint n, class TUP> + inline typename Shifted<TUP,n>::Head& element (TUP& tup) { @@ -743,5 +744,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 new file mode 100644 index 000000000..536630e13 --- /dev/null +++ b/src/lib/meta/typelist-manip.hpp @@ -0,0 +1,331 @@ +/* + TYPELIST-MANIP.hpp - Utils for working with lists-of-types + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file typelist-manip.hpp + ** Metaprogramming: Helpers for manipulating lists-of-types. + ** Sometimes, we use metaprogramming to generate a variation of concrete + ** implementations by combining some basic building blocks. Typically, there + ** is a number of similar, but not suitably related types involved. We want to + ** process those types using a common scheme, without being forced to squeeze + ** all those types into a artificial inheritance relationship. Now, generating + ** some kind of common factory or adapter, while mixing in pieces of code tailored + ** specifically to the individual types, allows still to build a common processing + ** in such situations. + ** + ** The facilities in this header provide the basics of simple functional list + ** processing (mostly with tail recursion). Usually, there is one template parameter + ** TYPES, which accepts a \em Type-list. The result of the processing step is then + ** accessible as an embedded typedef named \c List . Here, all of the 'processing' + ** to calculate this result is performed by the compiler, as a side-effect of + ** figuring out the resulting concrete type. At run time, in the generated + ** code, typically the resulting classes are empty, maybe just + ** exposing a specifically built-up function. + ** + ** @see generator.hpp + ** @see typelist-manip-test.cpp + ** @see TimeControl_test usage example + ** @see typelist.hpp + ** + */ + + +#ifndef LIB_META_TYPELIST_MANIP_H +#define LIB_META_TYPELIST_MANIP_H + + + +#include "lib/meta/typelist.hpp" + +#include <sys/types.h> + +namespace lib { +namespace meta { + + + /** 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 lib::meta +#endif diff --git a/src/lib/meta/typelist-util.hpp b/src/lib/meta/typelist-util.hpp index cf66a791a..812aa2487 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> @@ -21,19 +21,41 @@ */ -#ifndef LUMIERA_META_TYPELIST_UTIL_H -#define LUMIERA_META_TYPELIST_UTIL_H +/** @file typelist-util.hpp + ** Metaprogramming: simple helpers for working with lists-of-types. + ** This header provides some very basic "meta functions" for extracting + ** 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. + ** + ** The "meta functions" defined here are templates; to access the "result" of + ** such a meta function, we instantiate the template and then access one of the + ** embedded constant definitions (usually the enum constant named \c value) + ** + ** @see generator.hpp + ** @see TypelistUtil_test + ** @see lib::SimpleAllocator usage example (for isInList) + ** @see TypelistManip_test + ** @see typelist.hpp + ** + */ + + +#ifndef LIB_META_TYPELIST_UTIL_H +#define LIB_META_TYPELIST_UTIL_H + - #include "lib/meta/typelist.hpp" -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { /** * Metafunction counting the number of Types in the collection + * @return an embedded constant \c value holding the result */ template<class TYPES> struct count; @@ -48,6 +70,7 @@ namespace typelist{ enum{ value = 1 + count<TYPES>::value }; }; + /** * Metafunction " max( sizeof(T) ) for T in TYPES " */ @@ -68,235 +91,37 @@ namespace typelist{ }; - /** apply a transformation (template) to each type in the list */ - template<class TY, template<class> class _TRANS_> - struct Apply { typedef TY List; }; - - template< class TY, class TYPES - , template<class> class _TRANS_ - > - struct Apply<Node<TY,TYPES>, _TRANS_ > { typedef Node< typename _TRANS_<TY>::Type - , typename Apply<TYPES,_TRANS_>::List - > List; }; - - - template<bool, class T, class TAIL> - struct CondNode { typedef TAIL Next; }; - - template<class T, class TAIL> - struct CondNode<true, T, TAIL> { typedef Node<T,TAIL> Next; }; - - /** filter away those types which don't fulfil a predicate metafunction */ - template< class TYPES - , template<class> class _P_ ///< a template providing a boolean member \c ::value - > - struct Filter; - - template<template<class> class _P_> - struct Filter<NullType,_P_> { typedef NullType List; }; - - template< class TY, class TYPES - , template<class> class _P_ - > - struct Filter<Node<TY,TYPES>,_P_> { typedef typename CondNode< _P_<TY>::value - , TY - , typename Filter<TYPES,_P_>::List - >::Next - List; }; - - - /** append lists-of-types */ - template<class TY1, class TY2> - struct Append { typedef Node<TY1, typename Append<TY2,NullType>::List> List; }; - - template< class TY, class TYPES - , class TAIL - > - struct Append<Node<TY,TYPES>, TAIL> { typedef Node<TY, typename Append<TYPES, TAIL>::List> List; }; - - template<class TY, class TYPES> - struct Append<NullType, Node<TY,TYPES> > { typedef Node<TY,TYPES> List; }; - - template<class TY, class TYPES> - struct Append<Node<TY,TYPES>, NullType> { typedef Node<TY,TYPES> List; }; - - template<class TY1> - struct Append<TY1,NullType> { typedef Node<TY1,NullType> List; }; - - template<class TY2> - struct Append<NullType,TY2> { typedef Node<TY2,NullType> List; }; - - template<> - struct Append<NullType,NullType> { typedef NullType List; }; - - - - - /** access the last list element */ - template<class TYPES> - struct SplitLast; - - template<> - struct SplitLast<NullType> { typedef NullType Type; - typedef NullType List; }; - template<class TY> - struct SplitLast<Node<TY,NullType> > { typedef TY Type; - typedef NullType List; }; - - template<class TY, class TYPES> - struct SplitLast<Node<TY,TYPES> > { typedef typename SplitLast<TYPES>::Type Type; - typedef typename Append< TY, - typename SplitLast<TYPES>::List - >::List - List; }; - - - - /** - * splice a typelist like an overlay - * into an base typelist, starting at given index. - * @return either the combined (spliced) List, or - * the Front/Back part before or after the Overlay - * @note using a NullType as OVERLAY allows to extract - * an arbitrary Front/Back part of the list - */ - template<class BASE, class OVERLAY, uint i=0> - struct Splice; - - template<class B, class BS, - class OVERLAY, uint i> - struct Splice<Node<B,BS>, OVERLAY, i> { typedef Node<B, typename Splice<BS, OVERLAY, i-1>::List> List; - typedef Node<B, typename Splice<BS, OVERLAY, i-1>::Front> Front; - typedef typename Splice<BS, OVERLAY, i-1>::Back Back; }; - - template<class B, class BS, - class O, class OS > - struct Splice<Node<B,BS>,Node<O,OS>,0> { typedef Node<O, typename Splice<BS,OS, 0>::List> List; - typedef NullType Front; - typedef typename Splice<BS,OS, 0>::Back Back; }; - - template<class B, class BS> - struct Splice<Node<B,BS>, NullType, 0> { typedef Node<B, BS> List; - typedef NullType Front; - typedef Node<B, BS> Back; }; - - template<class XX, uint i> - struct Splice<NullType, XX, i> { typedef NullType List; - typedef NullType Front; - typedef NullType Back; }; - - - - /** - * Allows to access various parts of a given typelist: - * Start and End, Prefix and Tail.. + * Metafunction to check if a specific type is contained + * in a given typelist. Only exact match is detected. */ - template<class TYPES> - struct Dissect; - - template<class T, class TYPES> - struct Dissect<Node<T,TYPES> > + template<typename TY, typename TYPES> + struct IsInList { - typedef Node<T,TYPES> List; ///< the complete list - typedef T Head; ///< first element - typedef Node<T,NullType> First; ///< a list containing the first element - typedef TYPES Tail; ///< remainder of the list starting with the second elm. - typedef typename SplitLast<List>::List Prefix;///< all of the list, up to but extcluding the last element - typedef typename SplitLast<List>::Type End; ///< the last element - typedef Node<End,NullType> Last; ///< a list containing the last element + enum{ value = false }; }; - template<> - struct Dissect<NullType> + template<typename TY, typename TYPES> + struct IsInList<TY, Node<TY,TYPES> > { - typedef NullType List; - typedef NullType Head; - typedef NullType First; - typedef NullType Tail; - typedef NullType Prefix; - typedef NullType End; - typedef NullType Last; + enum{ value = true }; }; - - - - /** - * prefix each of the elements, - * yielding a list-of lists-of-types - */ - template<class T, class TY> - struct PrefixAll { typedef Node< typename Append<T,TY>::List, NullType> List; }; - - template<class T> - struct PrefixAll<T, NullType> { typedef NullType List; }; - - template<class T> - struct PrefixAll<T, NodeNull> { typedef Node< typename Append<T,NodeNull>::List, NullType> List; }; - - template< class T - , class TY, class TYPES - > - struct PrefixAll<T, Node<TY,TYPES> > { typedef Node< typename Append<T,TY>::List - , typename PrefixAll<T,TYPES>::List - > List; }; - - - - - template<class TY1,class TY2> - struct Distribute { typedef typename PrefixAll<TY1,TY2>::List List; }; - - template<class TY> - struct Distribute<NullType,TY> { typedef NullType List; }; - - template< class TY, class TYPES - , class TAIL - > - struct Distribute<Node<TY,TYPES>,TAIL> { typedef typename Append< typename PrefixAll<TY,TAIL>::List - , typename Distribute<TYPES,TAIL>::List - >::List - List; }; - - - - /** use a permutation generator - * for creating a list of all possible combinations - */ - template< class X - , template<class> class _PERMU_> - struct Combine { typedef typename Distribute< typename _PERMU_<X>::List - , Node<NullType,NullType> - >::List List; }; - template< template<class> class _PERMU_> - struct Combine<NullType, _PERMU_ > { typedef Node<NullType,NullType> List; }; - - template< class TY, class TYPES - , template<class> class _PERMU_> - struct Combine<Node<TY,TYPES>,_PERMU_> { typedef typename Distribute< typename _PERMU_<TY>::List - , typename Combine<TYPES,_PERMU_>::List - >::List List; }; - - /** permutation generator for the Combine metafunction, - * yielding an "on" and "off" case - */ - template<class F> - struct FlagOnOff - { - typedef Node<F, Node<NullType,NullType> > List; + template<typename TY, typename XX, typename TYPES> + struct IsInList<TY, Node<XX,TYPES> > + { + enum{ value = IsInList<TY,TYPES>::value }; }; - - /** generate all possible on-off combinations of the given flags */ - template<class FLAGS> - struct CombineFlags - { - typedef typename Combine<FLAGS, FlagOnOff>::List List; - }; + /** convenience shortcut: query function */ + template<typename TY, typename TYPES> + bool + isInList() + { + return IsInList<TY,TYPES>::value; + } - -}} // namespace lumiera::typelist +}} // namespace lib::meta #endif diff --git a/src/lib/meta/typelist.hpp b/src/lib/meta/typelist.hpp index 5d0e11bc0..d0cbb44f3 100644 --- a/src/lib/meta/typelist.hpp +++ b/src/lib/meta/typelist.hpp @@ -40,27 +40,37 @@ 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. + ** 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 - ** - ** @see lumiera::visitor::Applicable usage example - ** @see typelisttest.cpp + ** 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 lib::visitor::Applicable usage example + ** @see control::CommandSignature more elaborate usage example (dissecting a functor signature) + ** @see TypeList_test + ** @see TypeListManip_test ** */ -#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 { @@ -119,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 398d09d53..79c76e878 100644 --- a/src/lib/meta/typeseq-util.hpp +++ b/src/lib/meta/typeseq-util.hpp @@ -41,17 +41,17 @@ */ -#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-util.hpp" +#include "lib/meta/typelist-manip.hpp" #include "lib/meta/util.hpp" -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 e6bb82209..63ae2aa47 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -21,74 +21,154 @@ */ -#ifndef LUMIERA_META_UTIL_H -#define LUMIERA_META_UTIL_H +/** @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 + + +#include <string> + + +namespace lib { +namespace meta { + -namespace lumiera { - - /* types for figuring out the overload resolution chosen by the compiler */ - - typedef char Yes_t; - struct No_t { char padding[8]; }; - - - - namespace typelist { - - /** 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 + typedef char Yes_t; + struct No_t { char more_than_one[4]; }; + + + + + /** 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; + }; + + + /** 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()))); + }; + + + /** 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 + * 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..5422e1d08 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; @@ -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 b598a198d..b09ca8e2f 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", @@ -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/nobug-resource-handle-context.hpp b/src/lib/nobug-resource-handle-context.hpp new file mode 100644 index 000000000..ac4ccca52 --- /dev/null +++ b/src/lib/nobug-resource-handle-context.hpp @@ -0,0 +1,104 @@ +/* + NOBUG-RESOURCE-HANDLE-CONTEXT.hpp - thread local stack to manage NoBug resource handles + + 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 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). + ** + ** @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 + ** + **/ + + +#ifndef LIB_NOBUG_RESOURCE_HANDLE_CONTEXT_H +#define LIB_NOBUG_RESOURCE_HANDLE_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 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. + */ + class NobugResourceHandleContext + : DiagnosticContext<nobug_resource_user*> + { + public: + }; + + +#else /* not NOBUG_ALPHA */ + + + /** + * Disabled placeholder for the Diagnostic context, not used in release builds. + */ + class NobugResourceHandleContext + : boost::noncopyable + { + + typedef nobug_resource_user* Handle; + + public: + + operator Handle () ///< payload: NoBug resource tracker user handle + { + return 0; + } + + /** accessing the innermost diagnostic context created */ + static NobugResourceHandleContext& + access () + { + UNIMPLEMENTED ("how to disable DiagnosticContext with minimum overhead"); + } + }; +#endif /* NOBUG_ALPHA? */ + + + +} // namespace lib +#endif 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 <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_NOCOPY_H +#define LIB_NOCOPY_H + +#include <boost/noncopyable.hpp> + + + +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/lib/opaque-holder.hpp b/src/lib/opaque-holder.hpp index 3cc52a607..7ffb2463b 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 { @@ -219,7 +220,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; @@ -252,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 { @@ -266,7 +269,6 @@ namespace lib { explicit Buff (SUB const& obj) { - REQUIRE (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/p.hpp b/src/lib/p.hpp index 4fef50a36..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; @@ -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 + + +} // namespace lib #endif diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp new file mode 100644 index 000000000..306576326 --- /dev/null +++ b/src/lib/polymorphic-value.hpp @@ -0,0 +1,557 @@ +/* + 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++, 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 + ** 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++, 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 + ** 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). 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 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 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 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 + ** 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 + ** 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 + ** 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. + ** 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 + ** @see lib::time::Mutation usage example + */ + + +#ifndef LIB_POLYMORPHIC_VALUE_H +#define LIB_POLYMORPHIC_VALUE_H + + +#include "lib/error.hpp" +#include "lib/meta/duck-detector.hpp" + +#include <boost/utility/enable_if.hpp> +#include <boost/static_assert.hpp> + + +namespace lib { + + + namespace polyvalue { // implementation details... + + namespace error = lumiera::error; + using boost::enable_if; + using lib::meta::Yes_t; + using lib::meta::No_t; + + struct 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 + { + public: + virtual ~CopySupport() { }; + virtual void cloneInto (void* targetBuffer) const =0; + 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; + }; + + + + /** + * helper to detect presence of a function + * to support clone operations + */ + template<typename T> + class exposes_CloneFunction + { + + META_DETECT_FUNCTION(void, cloneInto, (void*) const); + + public: + enum{ value = HasFunSig_cloneInto<T>::value + }; + }; + + /** + * 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. + * 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,EmptyBase> CopyAPI; + enum{ ADMIN_OVERHEAD = 2 * sizeof(void*) }; + + static CopyAPI& + accessCopyHandlingInterface (TY& bufferContents) + { + REQUIRE (INSTANCEOF (CopyAPI, &bufferContents)); + return dynamic_cast<CopyAPI&> (bufferContents); + } + + typedef CopyAPI AdapterAttachment; + typedef AssignmentPolicy<CopyAPI> Assignment; + }; + + + /** + * 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_CloneFunction<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; + typedef AssignmentPolicy<CopyAPI> Assignment; + }; + + }//(End)implementation details + + + + + + + + + + /** + * 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 henceforth. + */ + template + < class IFA ///< the nominal Base/Interface class for a family of types + , 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; + typedef typename _Traits::Assignment _AssignmentPolicy; + enum{ + siz = storage + _Traits::ADMIN_OVERHEAD + }; + + + /* === embedded object in buffer === */ + + /** Storage for embedded objects */ + mutable char buf_[siz]; + + IFA& + accessEmbedded() const + { + return reinterpret_cast<IFA&> (buf_); + } + + void + destroyEmbedded() + { + accessEmbedded().~IFA(); + } + + + template<class IMP> + PolymorphicValue (IMP*) + { + BOOST_STATIC_ASSERT (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 // mix-in, might be empty + { + virtual void + cloneInto (void* targetBuffer) const + { + new(targetBuffer) Adapter(*this); // forward to copy ctor + } + + virtual void + copyInto (IFA& targetBase) const + { + REQUIRE (INSTANCEOF (Adapter, &targetBase)); + Adapter& target = static_cast<Adapter&> (targetBase); + _AssignmentPolicy::assignEmbedded(target,*this); + } // forward to assignment operator + + public: /* == forwarding ctor to implementation type == */ + + 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) { } + + /* using default copy and assignment */ + }; + + + _CopyHandlingAdapter& + accessHandlingInterface () const + { + IFA& bufferContents = accessEmbedded(); + return _Traits::accessCopyHandlingInterface (bufferContents); + } + + + public: /* === PolymorphicValue public API === */ + + typedef IFA Interface; + + operator Interface& () + { + return accessEmbedded(); + } + operator Interface const& () const + { + return accessEmbedded(); + } + Interface* + operator-> () const + { + return &( accessEmbedded() ); + } + + ~PolymorphicValue() + { + destroyEmbedded(); + } + + PolymorphicValue (PolymorphicValue const& o) + { + o.accessHandlingInterface().cloneInto (&buf_); + } + + PolymorphicValue& + operator= (PolymorphicValue const& o) + { + o.accessHandlingInterface().copyInto (this->accessEmbedded()); + return *this; + } + + /* === static factory functions === */ + + template<class IMP> + static PolymorphicValue + build () + { + 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) + { + 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) + { + 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) + { + Adapter<IMP>* type_to_build_in_buffer(0); + 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.accessEmbedded() == v2.accessEmbedded(); + } + friend bool + operator!= (PolymorphicValue const& v1, PolymorphicValue const& v2) + { + return ! (v1 == v2); + } + }; + + + + +} // namespace lib +#endif 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/result.hpp b/src/lib/result.hpp index ae4f7b187..38b3ba123 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. ** @@ -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" @@ -58,11 +57,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/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp new file mode 100644 index 000000000..58f2f0433 --- /dev/null +++ b/src/lib/scoped-collection.hpp @@ -0,0 +1,616 @@ +/* + 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. + ** + ** \par usage patterns + ** 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 + ** 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 "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, + ** 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 + ** @see scoped-ptrvect.hpp quite similar, but using individual heap pointers + */ + + +#ifndef LIB_SCOPED_COLLECTION_H +#define LIB_SCOPED_COLLECTION_H + + +#include "lib/error.hpp" +#include "lib/iter-adapter.hpp" + +#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 { + + namespace error = lumiera::error; + using error::LUMIERA_ERROR_CAPACITY; + 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 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 + { + + public: + /** + * 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 + { + + mutable char buf_[siz]; + + public: + + I& + accessObj() const + { + return reinterpret_cast<I&> (buf_); + } + + void + destroy() + { + accessObj().~I(); + } + + + + +#define TYPE_AND_STORAGE_SANITY_CHECK \ + BOOST_STATIC_ASSERT (((boost::is_same<I,TY>::value \ + ||boost::is_base_of<I,TY>::value)\ + && sizeof(TY) <= siz)) + + /** Abbreviation for placement new */ +#define EMBEDDED_ELEMENT_CTOR(_CTOR_CALL_) \ + TYPE_AND_STORAGE_SANITY_CHECK; \ + 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 TYPE_AND_STORAGE_SANITY_CHECK +#undef EMBEDDED_ELEMENT_CTOR + }; + + + + + + + ~ScopedCollection () + { + clear(); + } + + explicit + ScopedCollection (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<class CTOR> + ScopedCollection (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<class TY> + ScopedCollection (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 == */ + + 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() + { + 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 default constructed */ + 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; + } + + /** 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 + * 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& + 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 I *, const ScopedCollection *> 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: + /* ==== Storage: heap allocated array of element buffers ==== */ + + typedef boost::scoped_array<ElementHolder> ElementStorage; + + size_t level_; + size_t capacity_; + ElementStorage elements_; + + + + void + __ensureSufficientCapacity() + { + if (level_ >= capacity_) + throw error::State ("ScopedCollection exceeding the initially defined capacity" + , 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 + checkPoint (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 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/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/scoped-holder.hpp b/src/lib/scoped-holder.hpp index f8eaf74d2..cc4bff08c 100644 --- a/src/lib/scoped-holder.hpp +++ b/src/lib/scoped-holder.hpp @@ -173,11 +173,21 @@ namespace lib { return *obj; } + TY& + create (TY const& o) ///< place new content object using copy ctor + { + ASSERT (!created_); + TY * obj = new(content_) TY(o); + ++created_; + return *obj; + } + void clear () { if (created_) get()->~TY(); + created_ = false; } diff --git a/src/lib/scoped-ptrvect.hpp b/src/lib/scoped-ptrvect.hpp index 8bb713082..341f7c23b 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 @@ -48,8 +50,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> @@ -113,7 +117,7 @@ namespace lib { REQUIRE (obj); try { - push_back (obj); + this->push_back (obj); return *obj; } catch(...) @@ -123,22 +127,43 @@ 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 + * @todo TICKET #856 better return a Maybe<T&> instead of a pointer? + */ + 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(); } @@ -155,9 +180,9 @@ namespace lib { typedef ConstIterType const_iterator; iterator begin() { return iterator (allPtrs()); } - const_iterator begin() const { return const_iterator (allPtrs()); } iterator end() { return iterator ( RIter() ); } - const_iterator end() const { return const_iterator (RcIter() ); } + const_iterator begin() const { return const_iterator::build_by_cast (allPtrs()); } + const_iterator end() const { return const_iterator::nil(); } @@ -188,6 +213,12 @@ namespace lib { { return RIter (_Vec::begin(), _Vec::end()); } + RIter + allPtrs () const + { + _Vec& elements = util::unConst(*this); + return RIter (elements.begin(), elements.end()); + } }; diff --git a/src/lib/simple-allocator.hpp b/src/lib/simple-allocator.hpp new file mode 100644 index 000000000..b05911b7a --- /dev/null +++ b/src/lib/simple-allocator.hpp @@ -0,0 +1,301 @@ +/* + 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 "lib/error.hpp" +#include "lib/meta/generator.hpp" +#include "lib/meta/typelist-util.hpp" +#include "lib/format-util.hpp" +#include "lib/typed-counter.hpp" +#include "include/logging.h" + +#include <boost/static_assert.hpp> +#include <memory> + + + +namespace lib { + + using lib::meta::Types; + using lib::meta::IsInList; + using lib::meta::InstantiateForEach; + + + + /* === Policies for simple custom allocator === */ + + /** + * Policy: use just plain heap allocations + */ + template<typename TY> + class CustomAllocator + : public std::allocator<TY> + { }; + + + /** + * Policy: maintain explicit per type instance count + * @note this imposes additional locking + */ + struct UseInstantiationCounting + { + template<class XX> + size_t + allocationCount() const + { + return allocCnt_.get<XX>(); + } + + template<class XX> + void + incrementCount() + { + allocCnt_.inc<XX>(); + } + + template<class XX> + void + decrementCount() + { + allocCnt_.dec<XX>(); + } + + private: + lib::TypedCounter allocCnt_; + }; + + /** + * Policy: no additional instantiation accounting + */ + struct NoInstantiationCount + { + template<class XX> size_t allocationCount() const { return 0; } + template<class XX> void incrementCount() { /* NOP */ } + template<class XX> void decrementCount() { /* NOP */ } + }; + + + + + + + /* === Allocator frontend === */ + + /** + * Frontend for explicit allocations, using a custom allocator. + * This template is to be instantiated for the collection of types + * later to be allocated through this custom memory manager/model. + * It provides convenience shortcuts for placement-construction + * and releasing of target objects. + * + * @todo currently (as of 8/09) the low-level pooled allocator + * isn't implemented; instead we do just heap allocations. + * ////////////////////////////////////////////////////////////////////////////////////////////Ticket #835 + */ + template<typename TYPES + ,class COUNTER = NoInstantiationCount ///< Policy: support instance accounting? + > + class SimpleAllocator + : InstantiateForEach< typename TYPES::List // for each of those types... + , CustomAllocator // ...mix in the custom allocator + > + , COUNTER // ...Instantiation accounting policy + { + + /** forward plain memory allocation */ + template<class XX> + XX * + allocateSlot () + { + TRACE (memory, "allocate %s", util::tyStr<XX>().c_str()); + XX * newStorage = CustomAllocator<XX>::allocate (1); + COUNTER::template incrementCount<XX>(); + return newStorage; + } + + template<class XX> + void + releaseSlot (XX* entry) + { + TRACE (memory, "release %s", util::tyStr<XX>().c_str()); + CustomAllocator<XX>::deallocate (entry, 1); + COUNTER::template decrementCount<XX>(); + } + + + template<class XX> + void + ___assertSupportedType() + { + typedef typename TYPES::List PreconfiguredTypes; + typedef IsInList<XX, PreconfiguredTypes> IsSupportedType; + + BOOST_STATIC_ASSERT (IsSupportedType::value); + } + + + + + public: /* ==== build objects with managed allocation ==== */ + +#define _EXCEPTION_SAFE_INVOKE(_CTOR_) \ + \ + ___assertSupportedType<XX>(); \ + XX* storage = allocateSlot<XX>(); \ + try \ + { \ + return (new(storage) _CTOR_ ); \ + } \ + catch(...) \ + { \ + releaseSlot<XX>(storage); \ + throw; \ + } + + template< class XX> + XX* //_____________________ + create () ///< invoke default ctor + { + _EXCEPTION_SAFE_INVOKE ( XX() ) + } + + + template< class XX, typename P1> + XX* //___________________ + create (P1& p1) ///< invoke 1-arg ctor + { + _EXCEPTION_SAFE_INVOKE ( XX (p1) ) + } + + + template< class XX + , typename P1 + , typename P2 + > + XX* //___________________ + create (P1& p1, P2& p2) ///< invoke 2-arg ctor + { + _EXCEPTION_SAFE_INVOKE ( XX (p1,p2) ) + } + + + template< class XX + , typename P1 + , typename P2 + , typename P3 + > + XX* //___________________ + create (P1& p1, P2& p2, P3& p3) ///< invoke 3-arg ctor + { + _EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3) ) + } + + + template< class XX + , typename P1 + , typename P2 + , typename P3 + , typename P4 + > + XX* //___________________ + create (P1& p1, P2& p2, P3& p3, P4& p4) ///< invoke 4-arg ctor + { + _EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4) ) + } + + + template< class XX + , typename P1 + , typename P2 + , typename P3 + , typename P4 + , typename P5 + > + XX* //___________________ + create (P1& p1, P2& p2, P3& p3, P4& p4, P5& p5) ///< invoke 5-arg ctor + { + _EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4,p5) ) + } + +#undef _EXCEPTION_SAFE_INVOKE + + + + template<class XX> + void + destroy (XX* entry) + { + if (!entry) return; + ___assertSupportedType<XX>(); + try + { + entry->~XX(); + } + catch(...) + { + lumiera_err errorID = lumiera_error(); + WARN (common_dbg, "dtor of %s failed: %s", util::tyStr(entry).c_str() + , errorID ); + } + releaseSlot<XX> (entry); + } + + + /** diagnostics */ + template<class XX> + size_t + numSlots() const + { + return COUNTER::template allocationCount<XX>(); + } + }; + + + + +} // namespace lib +#endif diff --git a/src/lib/singleton-preconfigure.hpp b/src/lib/singleton-preconfigure.hpp index 5101c634e..32973ad90 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 { @@ -71,11 +71,17 @@ namespace lib { } // namespace test } // namespace lumiera -namespace backend_interface { +namespace backend { class MediaAccessFacade; using lib::Singleton; -} // namespace backend_interface +} // namespace backend + +namespace proc { +namespace engine { + class EngineService; + using lib::Singleton; +}} @@ -97,8 +103,14 @@ namespace lib { template<> - class Singleton<backend_interface::MediaAccessFacade> - : public MockInjector<backend_interface::MediaAccessFacade> + class Singleton<backend::MediaAccessFacade> + : public MockInjector<backend::MediaAccessFacade> + { }; + + + template<> + class Singleton<proc::engine::EngineService> + : public MockInjector<proc::engine::EngineService> { }; } // 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/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/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 ===== */ diff --git a/src/lib/test/mock-injector.hpp b/src/lib/test/mock-injector.hpp new file mode 100644 index 000000000..086cd478d --- /dev/null +++ b/src/lib/test/mock-injector.hpp @@ -0,0 +1,248 @@ +/* + MOCK-INJECTOR.hpp - replacement singleton factory for injecting Test-Mock objects + + 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 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 + ** 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_MOCK_INJECTOR_H +#define LUMIERA_TEST_MOCK_INJECTOR_H + + +#include "lib/singleton-factory.hpp" +#include "lib/meta/duck-detector.hpp" + +#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; + + /** + * Special SingletonFactory allowing to inject some instance of the Singleton + * class, thus shadowing "the" (default) Singleton instance temporarily. + * This allows installing a Mock Subclass of the Singleton for running tests, + * while the Singleton can be used as usual in production code. + * @note we use the default policies or SingletonFactory + */ + template<class SI> + class MockInjector : public SingletonFactory<SI> + { + static scoped_ptr<SI> mock_; + + public: + /** Overwriting the normal Singleton creation Interface + * to return some mock if defined, falling back to the + * default Singleton creation behaviour else. + */ + SI& operator() () + { + if (mock_) + return *mock_; + else + return SingletonFactory<SI>::operator() (); + } + + void injectSubclass (SI* mockobj) + { + TRACE_IF (mockobj, test, "Singleton: installing Mock object"); + TRACE_IF (!mockobj, test, "Singleton: removing Mock object"); + mock_.reset (mockobj); + } + + + MockInjector () {}; + + // 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. */ + namespace mock { + + using boost::enable_if; + using lib::meta::Yes_t; + using lib::meta::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 + * to inject Mock implementations defined as Subclasses. + */ + template<class MOCK> + class is_Singleton_with_MockInjector + { + typedef typename SingletonAccess<MOCK>::ServiceInterface Interface; + typedef typename SingletonAccess<MOCK>::Factory SingletonFactory; + + META_DETECT_MEMBER (injectSubclass); + public: + + enum{ value = defines_ServiceInterface<MOCK>::value + && HasMember_injectSubclass<SingletonFactory>::value + }; + }; + + + /** + * Policy-Trait: How to access and inject + * a mock service implementation? + */ + template<class MOCK, class YES =void> + 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 SingletonAccess<MOCK>::Factory ServiceAccessPoint; + + static void + activateMock() + { + ServiceAccessPoint().injectSubclass (new MOCK()); + } + static 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. + * @param MOCK the concrete mock implementation to inject. It needs to + * subclass the service interface and expose a typedef \c ServiceInterace + */ + template<class MOCK> + struct Use4Test + : boost::noncopyable + { + Use4Test() + { + mock::AccessPoint<MOCK>::activateMock(); + } + + ~Use4Test() + { + mock::AccessPoint<MOCK>::deactivateMock(); + } + }; + + + +}} // namespace lib::test +#endif diff --git a/src/lib/test/mockinjector.hpp b/src/lib/test/mockinjector.hpp deleted file mode 100644 index dbc47d3ed..000000000 --- a/src/lib/test/mockinjector.hpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - MOCKINJECTOR.hpp - replacement singleton factory for injecting Test-Mock objects - - 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_TEST_MOCKINJECTOR_H -#define LUMIERA_TEST_MOCKINJECTOR_H - - -#include "lib/singleton-factory.hpp" - -#include <boost/scoped_ptr.hpp> - - -namespace lib { -namespace test{ - - using boost::scoped_ptr; - - /** - * Special SingletonFactory allowing to inject some instance of the Singleton - * class, thus shadowing "the" (default) Singleton instance temporarily. - * This allows installing a Mock Subclass of the Singleton for running tests, - * while the Singleton can be used as usual in production code. - * @note we use the default policies or SingletonFactory - */ - template<class SI> - class MockInjector : public SingletonFactory<SI> - { - scoped_ptr<SI> mock_; - - public: - /** Overwriting the normal Singleton creation Interface - * to return some mock if defined, falling back to the - * default Singleton creation behaviour else. - */ - SI& operator() () - { - if (mock_) - return *mock_; - else - return SingletonFactory<SI>::operator() (); - } - - void injectSubclass (SI* mockobj) - { - TRACE_IF (mockobj, test, "Singleton: installing Mock object"); - TRACE_IF (!mockobj, test, "Singleton: removing Mock object"); - mock_.reset (mockobj); - } - - - 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); - } - }; - - - -}} // namespace lib::test -#endif 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/test/suite.cpp b/src/lib/test/suite.cpp index dd8e35bfe..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"); } @@ -165,6 +170,7 @@ namespace test { { try { + INFO (test, "++------------------- invoking TEST: %s", showType(theTest).c()); theTest.run (cmdline); return Suite::TEST_OK; } 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.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/test-helper.hpp b/src/lib/test/test-helper.hpp index a115e3a1f..8a778840f 100644 --- a/src/lib/test/test-helper.hpp +++ b/src/lib/test/test-helper.hpp @@ -26,7 +26,7 @@ #include "lib/symbol.hpp" -#include "lib/lumitime.hpp" +#include "lib/time/timevalue.hpp" #include <typeinfo> #include <string> @@ -38,7 +38,6 @@ namespace lib { namespace test{ using lib::Literal; - using lumiera::Time; using std::string; using std::rand; @@ -104,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/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/src/lib/test/testdummy.hpp b/src/lib/test/testdummy.hpp new file mode 100644 index 000000000..27d4e79c7 --- /dev/null +++ b/src/lib/test/testdummy.hpp @@ -0,0 +1,107 @@ +/* + 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(); } + + virtual ~Dummy() + { + checksum() -= val_; + } + + virtual long + acc (int i) ///< dummy API operation + { + 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/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/src/lib/time.cpp b/src/lib/time.cpp index a7efb8009..d68b883cb 100644 --- a/src/lib/time.cpp +++ b/src/lib/time.cpp @@ -22,13 +22,13 @@ #include "lib/time.h" #include "lib/error.hpp" -#include "lib/util.hpp" +#include "lib/util-quant.hpp" extern "C" { #include "lib/tmpbuf.h" } -#include <limits.h> +#include <limits> #include <math.h> using util::floordiv; @@ -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) @@ -94,23 +102,55 @@ lumiera_frame_duration (FrameRate const& fps) } -namespace { // implementation helper +namespace { // implementation: basic frame quantisation.... - inline 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); + } + } } -long +int64_t lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) { 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) { @@ -120,7 +160,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; @@ -139,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; @@ -149,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) -{ - int total_mins = 60 * hours + mins; - int 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; -} - int lumiera_time_hours (gavl_time_t time) { @@ -187,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 521b5658d..f321e176a 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 @@ -84,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. @@ -103,9 +111,12 @@ 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); +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, @@ -125,10 +136,10 @@ 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. + * 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 @@ -139,10 +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 + * @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. @@ -173,19 +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 - */ -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/src/lib/time/control-impl.hpp b/src/lib/time/control-impl.hpp new file mode 100644 index 000000000..5ec429309 --- /dev/null +++ b/src/lib/time/control-impl.hpp @@ -0,0 +1,206 @@ +/* + 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 <tr1/functional> +#include <vector> + + +namespace lib { +namespace time { +namespace mutation { + + using std::tr1::function; + + + + + /** + * 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 (std::tr1::ref(toNotify)); + listeners_.push_back (newListener); + } + + /** disconnect any observers */ + void + disconnect() + { + 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..d05bbbe62 --- /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 lib::meta::is_sameType; + using std::tr1::placeholders::_1; + using std::tr1::function; + using std::tr1::bind; + using std::tr1::ref; + + + + namespace { // metaprogramming helpers to pick a suitable implementation branch... + + template<class T> + inline bool + isDuration() + { + return is_sameType<T,Duration>::value; + } + + template<class T> + inline bool + isTimeSpan() + { + return is_sameType<T,TimeSpan>::value; + } + + template<class T> + inline T const& + maybeMaterialise (T const& non_grid_aligned_TimeValue) + { + return non_grid_aligned_TimeValue; + } + +#ifdef LIB_TIME_TIMEQUQNT_H + inline QuTime + maybeMaterialise (QuTime const& alignedTime) + { + PQuant const& grid(alignedTime); + return QuTime(grid->materialise(alignedTime), grid); + } +#endif //--quantised-time-support + } + + + /** + * Implementation policy: how to build a new + * notification value of type \c TI, given a + * target time value entity of type \c TAR + */ + template<class TI, class TAR> + struct Builder + { + static TI + buildChangedValue (TAR const& target) + { + return TI(target); + } + }; + template<class TAR> + struct Builder<TimeSpan, TAR> + { + static TimeSpan + buildChangedValue (TAR const& target) + { + return TimeSpan (target, Duration::NIL); + } + }; + template<> + struct Builder<TimeSpan, Duration> + { + static TimeSpan + buildChangedValue (Duration const& targetDuration) + { + return TimeSpan (Time::ZERO, targetDuration); + } + }; + template<> + struct Builder<TimeSpan, TimeSpan> + { + static TimeSpan + buildChangedValue (TimeSpan const& target) + { + return target; + } + }; +#ifdef LIB_TIME_TIMEQUQNT_H + template<class TAR> + struct Builder<QuTime, TAR> + { + static QuTime + buildChangedValue (TAR const& target) + { + return QuTime (target + ,getDefaultGridFallback() //////////////////TICKET #810 + ); + } + }; + template<> + struct Builder<QuTime, QuTime> + { + static QuTime + buildChangedValue (QuTime const& target) + { + return target; + } + }; +#endif //--quantised-time-support + + + + + /** + * Policy to tie the various detail policies together + * for providing actual value change operations. + * The standard case uses the (inherited) time::Mutation + * base implementation to impose a new value onto the + * target entity and then uses the Builder policy to + * create a notification value reflecting this change. + */ + template<class TI, class TAR> + struct Link + : Mutation + , Builder<TI,TAR> + { + + template<class SRC> + static TI + processValueChange (TAR& target, SRC const& change) ///< standard case: plain value change + { + imposeChange (target, maybeMaterialise(change)); + return Link::buildChangedValue (maybeMaterialise(target)); + } + + static TI + useLengthAsChange (TAR& target, TimeSpan const& change) + { + return processValueChange(target, change.duration()); + } + + static TI + mutateLength (TimeSpan& target, Duration const& change) + { + imposeChange (target.duration(), change); + return Builder<TI,TimeSpan>::buildChangedValue(target); + } + + static TimeSpan + mutateTimeSpan (TimeSpan& target, TimeSpan const& change) + { + imposeChange (target.duration(), change.duration()); + imposeChange (target,change.start()); + return Builder<TimeSpan,TimeSpan>::buildChangedValue(target); + } + + static TI + dontChange (TAR& target) ///< @note: not touching the target + { + return buildChangedValue(target); + } + }; + + + + /** + * Policy how to impose changes onto a connected target time value entity + * This policy will be parametrised with the concrete time entity types + * involved in the usage situation of time::Control. The purpose of the + * policy is to \em bind a functor object to the concrete implementation + * of the value change applicable for this combination of types. + * This functor will then be stored within time::Control and + * invoked for each actual value change. + * @param TI the nominal (interface) type of the change, propagated to listeners + * @param SRC the actual type of the change to be imposed + * @param TAR the actual type of the target entity to receive the changes + * @note typically either SRC is identical to TI, or it is an + * time::Offset, or an int for \em nudging the target + */ + template<class TI, class SRC, class TAR> + struct Policy + { + static function<TI(SRC const&)> + buildChangeHandler (TAR& target) + { + return bind (Link<TI,TAR>::template processValueChange<SRC>, ref(target), _1 ); + } + }; + + + // special treatment of Durations as target------------------------------------ + + namespace { + template<class T> + struct canMutateDuration + { + static const bool value = is_sameType<T,Duration>::value + || is_sameType<T,Offset>::value + || is_sameType<T,int>::value; + }; + + template<class T> + struct canReceiveDuration + { + static const bool value = is_sameType<T,Duration>::value + || is_sameType<T,TimeSpan>::value; + }; + } + + + /** + * special case: a Duration target value can't be changed by plain time values. + * This specialisation is \em not used (\c disable_if ) when the given change (SRC) + * is applicable to a Duration in a sensible way. We either define explicit + * specialisations (for TimeSpan) or fall back to the default in these cases. + */ + template<class TI, class SRC> + struct Policy<TI,SRC, typename disable_if< canMutateDuration<SRC>, + Duration>::type> + { + static function<TI(SRC const&)> + buildChangeHandler (Duration& target) + { + return bind (Link<TI,Duration>::dontChange, ref(target) ); + } + }; + + /** + * special case: a Duration change value can't be imposed to a plain time value. + * In these cases, we even propagate a Duration::ZERO to the listeners. + * As above, there are exceptions to this behaviour, where a Duration change + * can sensibly be applied. + */ + template<class TAR> + struct Policy<Duration, typename disable_if< canReceiveDuration<TAR>, + Duration>::type, TAR> + { + static function<Duration(Duration const&)> + buildChangeHandler (TAR&) + { + return bind ( ignore_change_and_return_Zero ); + } + + static Duration + ignore_change_and_return_Zero() + { + return Duration::NIL; + } + }; + + template<class TI> + struct Policy<TI,TimeSpan,Duration> + { + static function<TI(TimeSpan const&)> + buildChangeHandler (Duration& target) + { + return bind (Link<TI,Duration>::useLengthAsChange, ref(target), _1 ); + } + }; + + + // special treatment for TimeSpan values--------------------------------------- + + template<class TI> + struct Policy<TI,Duration,TimeSpan> + { + static function<TI(Duration const&)> + buildChangeHandler (TimeSpan& target) + { + return bind (Link<TI,TimeSpan>::mutateLength, ref(target), _1 ); + } + }; + + template<> + struct Policy<TimeSpan,TimeSpan,TimeSpan> + { + static function<TimeSpan(TimeSpan const&)> + buildChangeHandler (TimeSpan& target) + { + return bind (Link<TimeSpan,TimeSpan>::mutateTimeSpan, ref(target), _1 ); + } + }; + + + +}}} // namespace lib::time::mutation +#endif diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp new file mode 100644 index 000000000..f7f78e4d1 --- /dev/null +++ b/src/lib/time/control.hpp @@ -0,0 +1,256 @@ +/* + CONTROL.hpp - a life time control for feedback and mutation + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file control.hpp + ** Manipulating and monitoring time entities with life changes. + ** This is an control- and callback element to handle any kind of "running" + ** time entities. This element is to be provided by the client and then attached + ** to the target time entity as a time::Mutation. Internally, a life connection to + ** the target is built, allowing both to + ** - to manipulate the target by invoking the function operator + ** - to receive change notifications by installing a callback functor. + ** + ** The actual type of the changes and modifications is specified as template parameter; + ** when later attached to some time entity as a Mutation, the actual changes to be performed + ** depend both on this change type and the type of the target time entity (double dispatch). + ** The behaviour is similar to applying a static time::Mutation + ** + ** \par relevance + ** This control element is intended to be used for all kinds of editing and monitoring + ** of time-like entities -- be it the running time display in a GUI widget, a ruler marker + ** which can be dragged, a modifiable selection or the animated playhead cursor. + ** + ** \par usage scenarios + ** The time::Control element provides mediating functionality, but doesn't assume or provide + ** anything special regarding the usage pattern or the lifecycle, beyond the ability to + ** attach listeners, attach to a (different) target and to detach from all connections. + ** Especially, no assumptions are made about which side is the server or the client + ** and who owns the time::Control element. + ** + ** Thus an interface might accept a time::Control element \em reference (e.g. the + ** lumiera::Play::Controller uses this pattern) -- meaning that the client owns the + ** Control element and might attach listeners, while the implementation (server side) + ** will attach the Control to mutate an time value entity otherwise not disclosed + ** (e.g. the playhead position of the playback process). Of course, in this case + ** the client is responsible for keeping the Control element and all listeners + ** alive, and to invoke Control#disconnect prior to destroying the element. + ** + ** Of course, the reversed usage situation would be possible as well: an interface + ** exposing a time::Control, thus allowing to attach target and listeners, while the + ** actual changes will originate somewhere within the service implementation. + ** + ** Another usage pattern would be to expose a time::Control \c const&, allowing only to + ** impose changes, but not to change the target or listener attachments. To the contrary, + ** when exposing only a time::Mutation \c const& through an interface allows only to + ** attach new target elements, but not to change listeners or feed any value changes. + ** + ** Using time::Control as an implementation building block and just exposing the + ** change (function) operators or the listener attachment through an forwarding sub + ** interface is another option. + ** + ** @note time::Control is default constructible and freely copyable. + ** + ** + ** \par changing quantised (grid aligned) time entities + ** + ** The time::Control element includes the functionality to handle grid aligned time values, + ** both as target and as change/notification value. This ability is compiled in conditionally, + ** as including mutation.hpp causes several additional includes, which isn't desirable when + ** it comes just to changing plain time values. Thus, to get these additional specialisations, + ** the LIB_TIME_TIMEQUQNT_H header guard needs to be defined, which happens automatically + ** if lib/time/mutation.hpp is included prior to lib/time/control.hpp. + ** + ** \par implementation notes + ** - the validity of a given combination of change and target is checked immediately, + ** when connecting to the target. Depending on the situation, the actual changes later + ** are subject to specific treatment (e.g. frame quantisation) + ** - by default time::Control is <b>not threadsafe</b>. But, as each change is basically + ** processed within its own call context (function invocation), parallelism is only + ** a concern with respect to the value finally visible within the target. + ** - the change notification is processed right away, after applying the change to the + ** target; of course there is a race between applying the value and building the + ** response value passed on as notification. In all cases, the effective change + ** notification value is built from the state of the target after applying + ** the change, which might or might not reflect the change value passed in. + ** + ** @todo include support for QuTimeSpan values ////////////////////TICKET #760 + ** + ** @see TimeControl_test + ** + */ + +#ifndef LIB_TIME_CONTROL_H +#define LIB_TIME_CONTROL_H + +#include "lib/time/mutation.hpp" +#include "lib/time/timevalue.hpp" +#include "lib/time/control-impl.hpp" + + + +namespace lib { +namespace time { + + + + /** + * Frontend/Interface: controller-element to retrieve + * and change running time values. time::Control is + * a mediator element, which can be attached to some + * time value entities as \em mutation, and at the + * same time allows to register listeners. When + * configured this way, \em changes may be fed + * to the function operator(s). These changes + * will be imposed to the connected target + * and the result propagated to the listeners. + * + * @see time::Mutation + * @see time::TimeSpan#accept(Mutation const&) + */ + template<class TI> + class Control + : public mutation::Mutator<TI> + { + mutation::Propagator<TI> notifyListeners_; + + virtual void change (Duration&) const; + virtual void change (TimeSpan&) const; + virtual void change (QuTime&) const; + + public: + void operator() (TI const&) const; + void operator() (Offset const&) const; + void operator() (int) const; + + + /** install a callback functor to be invoked as notification + * for any changes imposed onto the observed time entity. + * @param toNotify object with \c operator()(TI const&) */ + template<class SIG> + void connectChangeNotification (SIG const& toNotify); + + /** disconnect from observed entity and + * cease any change notification */ + void disconnect(); + }; + + + + + /* === forward to implementation === */ + + /** impose a new value to the connected target. + * If applicable, the target will afterwards reflect + * that change, and listeners will be notified, passing + * the target's new state. + * @throw error::State if not connected to a target + * @note the actual change in the target also depends + * on the concrete target type and the type of + * the change. By default, the time value is + * changed; this may include grid alignment. + */ + template<class TI> + void + Control<TI>::operator () (TI const& newValue) const + { + this->ensure_isArmed(); + notifyListeners_( + this->setVal_(newValue)); + } + + /** impose an offset to the connected target. + * If applicable, the target will be adjusted by the + * time offset, and listeners will be notified. + * @throw error::State if not connected to a target + */ + template<class TI> + void + Control<TI>::operator () (Offset const& adjustment) const + { + this->ensure_isArmed(); + notifyListeners_( + this->offset_(adjustment)); + } + + /** nudge the connected target by the given offset steps, + * using either the target's own grid (when quantised), + * or a 'natural' nudge grid + * @throw error::State if not connected to a target + */ + template<class TI> + void + Control<TI>::operator () (int offset_by_steps) const + { + this->ensure_isArmed(); + notifyListeners_( + this->nudge_(offset_by_steps)); + } + + + template<class TI> + void + Control<TI>::disconnect() + { + notifyListeners_.disconnect(); + this->unbind(); + } + + template<class TI> + template<class SIG> + void + Control<TI>::connectChangeNotification (SIG const& toNotify) + { + if (this->offset_) + { // we're already connected: thus propagate current value + TI currentValue = this->offset_(Offset::ZERO); + toNotify (currentValue); + } + notifyListeners_.attach (toNotify); + } + + + /* ==== Implementation of the Mutation interface ==== */ + + template<class TI> + void + Control<TI>::change (Duration& targetDuration) const + { + this->bind_to (targetDuration); + } + + template<class TI> + void + Control<TI>::change (TimeSpan& targetInterval) const + { + this->bind_to (targetInterval); + } + + template<class TI> + void + Control<TI>::change (QuTime& targetQuTime) const + { + this->bind_to (targetQuTime); + } + +}} // lib::time +#endif 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/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index 61bf2ab3e..32b7745db 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> #include <cstdio> @@ -129,7 +130,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()); @@ -319,7 +320,7 @@ namespace time { typedef Digxel< int, digxel::SexaFormatter> SexaDigit; ///< for displaying time components (sexagesimal) typedef Digxel<uint, digxel::HexaFormatter> HexaDigit; ///< for displaying a hex byte typedef Digxel< int, digxel::HourFormatter> HourDigit; ///< for displaying hours in H:M.S - typedef Digxel<long, digxel::CountFormatter> CountVal; ///< for displaying a counter + typedef Digxel<long, digxel::CountFormatter> CountVal; ///< for displaying a counter ///////////TICKET #882 : outch! /** special Digxel to show a sign. diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index b82a8f631..252c7145c 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,10 +86,11 @@ 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&); - static void rangeLimitStrategy (SmpteTC&); + static void applyRangeLimitStrategy (SmpteTC&); }; @@ -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); }; @@ -154,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. @@ -179,7 +187,7 @@ namespace time { std::bitset<MAXID> flags_; template<class F> - size_t + IxID typeID() const { return TypedContext<Supported>::ID<F>::get(); @@ -192,7 +200,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/grid.hpp b/src/lib/time/grid.hpp index 4053fa5b0..f509cedd4 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 @@ -62,10 +62,10 @@ 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 (FSecs, int =0) const =0; + virtual TimeValue timeOf (int64_t gridPoint) const =0; + virtual TimeValue timeOf (FSecs gridTime, int=0) const =0; }; typedef std::tr1::shared_ptr<const Grid> PGrid; diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index af3313772..03218ebb7 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -21,15 +21,16 @@ * *****************************************************/ -#include "lib/lumitime.hpp" #include "lib/time/timevalue.hpp" #include "lib/time.h" +#include "lib/util-quant.hpp" #include <limits> #include <string> #include <boost/rational.hpp> using std::string; +using util::floordiv; namespace lib { @@ -40,6 +41,13 @@ 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; + + const Time Time::ANYTIME(Time::MAX); + const Time Time::NEVER (Time::MIN); + + const Offset Offset::ZERO (Time::ZERO); + /** convenience constructor to build an @@ -58,7 +66,7 @@ namespace time { , uint hours ) : TimeValue(lumiera_build_time (millis,secs,mins,hours)) - { } + { } /** convenience constructor to build an Time value @@ -116,14 +124,31 @@ namespace time { } - /** duration of the given number of frames */ - Duration::Duration (ulong count, FrameRate const& fps) - : Offset(TimeValue (count? lumiera_frame_duration (fps/count) : _raw(Duration::NIL))) + Offset + operator* (boost::rational<int64_t> factor, Offset const& o) + { + boost::rational<int64_t> 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) + : _raw(Duration::NIL)) + { } + + /** duration of the given number of frames. + * @note always positive; count used absolute */ + Duration::Duration (int64_t count, FrameRate const& fps) + : TimeValue (count? lumiera_framecount_to_time (abs(count), fps) : _raw(Duration::NIL)) { } /** constant to indicate "no duration" */ - const Duration Duration::NIL = Offset(TimeValue(0)); + const Duration Duration::NIL (Time::ZERO); @@ -131,62 +156,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/mutation.cpp b/src/lib/time/mutation.cpp new file mode 100644 index 000000000..718af5244 --- /dev/null +++ b/src/lib/time/mutation.cpp @@ -0,0 +1,360 @@ +/* + 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/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-quant.hpp" +#include "lib/util.hpp" + +//#include <tr1/functional> + +//using std::string; +//using util::unConst; +//using util::floorwrap; +using util::isnil; + +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.... + + + + + + + /** + * concrete time value mutation: + * impose fixed new start time. + */ + class SetNewStartTimeMutation + : public ClonableMutation + { + 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 + SetNewStartTimeMutation (TimeValue t) + : newTime_(t) + { } + }; + + + /** + * concrete time value mutation: + * set a new overall duration for an extended timespan. + */ + 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) + { } + }; + + + /** + * 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, adjustment_); + } + + + virtual void + change (TimeSpan& target) const + { + imposeChange (target, adjustment_); + } + + + /** @note the re-quantisation happens automatically + * when the (changed) QuTime is materialised */ + virtual void + change (QuTime& target) const + { + imposeChange (target, adjustment_); + } + + + public: + explicit + ImposeOffsetMutation (Offset adj) + : adjustment_(adj) + { } + }; + + + /** + * 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 (PQuant(quant)->materialise(quant)) + { } + }; + + + /** + * concrete time value mutation: + * nudge target value by the given number of 'steps', + * relative to 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. + * @see mutation#imposeChange (TimeValue, int) + * @see mutation#imposeChange (QuTime, int) + */ + class NaturalNudgeMutation + : public ClonableMutation + { + int steps_; + + virtual void + change (Duration& target) const + { + imposeChange (target, steps_); + } + + + virtual void + change (TimeSpan& target) const + { + imposeChange (target, steps_); + } + + + /** @note special treatment: use the quantised time's own grid */ + virtual void + change (QuTime& target) const + { + imposeChange (target, steps_); + } + + + 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 + * 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) + { + return EncapsulatedMutation::build<SetNewStartTimeMutation> (newStartTime); + } + + + /** 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) + { + return EncapsulatedMutation::build<SetNewDuration> (changedDur); + } + + + /** 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); + } + + + /** 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 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) + { + return EncapsulatedMutation::build<NaturalNudgeMutation> (adjustment); + } + + + /** 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) + { + return EncapsulatedMutation::build<NudgeMutation> (adjustment, gridID); + } + + + + +}} // lib::time + diff --git a/src/lib/time/mutation.hpp b/src/lib/time/mutation.hpp new file mode 100644 index 000000000..f6e2f3f5f --- /dev/null +++ b/src/lib/time/mutation.hpp @@ -0,0 +1,181 @@ +/* + 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/error.hpp" +#include "lib/time/timevalue.hpp" +#include "lib/polymorphic-value.hpp" +#include "lib/symbol.hpp" + +#include <boost/noncopyable.hpp> +//#include <iostream> +//#include <boost/operators.hpp> +//#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 + + 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. + * + * @see time::TimeSpan#accept(Mutation const&) + * @todo WIP-WIP-WIP + */ + class Mutation + { + 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 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 TimeValue& imposeChange (TimeValue&, TimeValue const&); + static TimeValue& imposeChange (TimeValue&, Offset const&); + static TimeValue& imposeChange (TimeValue&, int); + static TimeValue& imposeChange (QuTime&, int); + }; + + + /* === 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 + + + + /* === 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 diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index 9c4eca790..5651df3b2 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -47,14 +47,23 @@ 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; } - }//(End) implementation helpers + PQuant + getDefaultGridFallback() + { + static PQuant globalDefaultGrid (new FixedFrameQuantiser(1)); + return globalDefaultGrid; ///////////////////////TICKET #810 + }; + + + + Grid::~Grid() { } // hint to emit the VTable here... @@ -101,6 +110,38 @@ 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); + } + + + + /** 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, @@ -128,7 +169,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_)); @@ -141,7 +182,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_))); } @@ -159,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/quantiser.hpp b/src/lib/time/quantiser.hpp index 369c48055..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 @@ -92,11 +103,14 @@ namespace time { return supportedFormats_.check<FMT>(); } + static PQuant retrieve (Symbol gridID); + TimeValue materialise (TimeValue const& raw) const; + //------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; }; @@ -123,9 +137,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; }; diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index dd4b67015..53faef575 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -21,21 +21,28 @@ * *****************************************************/ -#include "lib/lumitime.hpp" #include "lib/time/timecode.hpp" #include "lib/time/timevalue.hpp" #include "lib/time/timequant.hpp" #include "lib/time/formats.hpp" #include "lib/time.h" #include "lib/util.hpp" +#include "lib/util-quant.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,102 @@ 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])(-?\\d+)#"); // no leading [.-\d], number+'#' + 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+)?/(\\d+))?sec"); + //__no leading[./-\d] number [+-] number '/' number '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 @@ -51,7 +153,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 */ @@ -79,11 +181,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 +205,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); @@ -131,7 +233,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 +273,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..f81df7c7f 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: @@ -95,13 +102,36 @@ namespace time { FrameNr (QuTime const& quantisedTime); - // CountVal implicitly convertible to long + using TCode::operator string; + // CountVal implicitly convertible to long ///////////TICKET #882 : outch! should be a 64bit type! }; /** + * 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 +171,7 @@ namespace time { /** - * + * @warning missing implementation */ class HmsTC : public TCode @@ -166,7 +196,7 @@ namespace time { /** - * + * @warning partially missing implementation */ class Secs : public TCode 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 f0d7fc9c8..c41989c30 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. @@ -74,6 +79,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); @@ -109,7 +117,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 @@ -146,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; } @@ -170,8 +178,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; @@ -187,11 +198,19 @@ namespace time { : TimeValue(TimeVar(target) -= origin) { } + Offset (int64_t count, FrameRate const& fps); + + static const Offset ZERO; + + TimeValue abs() const { return TimeValue(std::llabs (t_)); } + + // Supporting sign flip + Offset operator- () const; }; //-- support linear offset chaining --------------- @@ -204,20 +223,40 @@ namespace time { return Offset(distance); } + template<typename INT> inline Offset - operator* (int factor, Offset const& o) + operator* (Offset const& distance, INT factor) + { + return factor*distance; + } + + template<typename INT> + inline Offset + operator* (INT factor, Offset const& o) { TimeVar distance(o); distance *= factor; return Offset(distance); } + template<typename INTX> inline Offset - operator* (Offset const& distance, int factor) + operator* (boost::rational<INTX> factor, Offset const& o) { - return factor*distance; + return boost::rational<int64_t>(factor.numerator(), factor.denominator()) * o; } + /** stretch offset by a possibly fractional factor */ + Offset + operator* (boost::rational<int64_t> factor, Offset const& o); + + + /** flip offset direction */ + inline Offset + Offset::operator- () const + { + return -1 * (*this); + } @@ -227,7 +266,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. @@ -237,18 +275,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 @@ -256,9 +291,16 @@ namespace time { /// direct assignment prohibited Time& operator= (Time const); + /// suppress possible direct conversions + Time(int); + public: static const Time MAX ; 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)) @@ -269,6 +311,7 @@ namespace time { : TimeValue(calcResult) { } + explicit Time (FSecs const& fractionalSeconds); Time ( long millis @@ -288,37 +331,74 @@ namespace time { - class TimeSpan; /** * 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. While Duration generally + * is treated as immutable value, there is the + * 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 (Time const& timeSpec) - : Offset(Offset(timeSpec).abs()) + Duration (TimeValue const& timeSpec) + : TimeValue(Offset(timeSpec).abs()) + { } + + explicit + Duration (FSecs const& timeSpan_in_secs) + : TimeValue(Offset(Time(timeSpan_in_secs)).abs()) { } Duration (TimeSpan const& interval); - Duration (ulong count, FrameRate const& fps); + Duration (int64_t count, FrameRate const& fps); static const Duration NIL; + + void accept (Mutation const&); + + /// Supporting backwards use as offset + Offset operator- () const; + }; + + //-- support using a Duration to build offsets --------------- + + inline Duration + operator+ (Duration const& base, Duration const& toAdd) + { + return Offset(base) + Offset(toAdd); + } + + template<typename INT> + inline Offset + operator* (INT factor, Duration const& dur) + { + return factor * Offset(dur); + } + + template<typename INT> + inline Offset + operator* (Duration const& dur, INT factor) + { + return factor*dur; + } + + inline Offset + Duration::operator- () const + { + return -1 * (*this); + } @@ -330,20 +410,47 @@ 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 + , boost::totally_ordered<TimeSpan> { Duration dur_; public: - TimeSpan(Time start, Duration length) + TimeSpan(TimeValue const& start, Duration const& length) : Time(start) , dur_(length) { } - ///////////TODO creating timespans needs to be more convenient.... + + 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) + { } + + + Duration& + duration() + { + return dur_; + } Duration duration() const @@ -351,12 +458,33 @@ namespace time { return dur_; } + Time + start() const + { + return *this; + } + 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 */ + 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_ || + (t1.t_==t2.t_ && t1.dur_< t2.dur_);} }; @@ -422,7 +550,7 @@ namespace time { inline Duration::Duration (TimeSpan const& interval) - : Offset(interval.duration()) + : TimeValue(interval.duration()) { } inline 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 /** diff --git a/src/lib/typed-allocation-manager.hpp b/src/lib/typed-allocation-manager.hpp index 188f120dc..e44eb01bb 100644 --- a/src/lib/typed-allocation-manager.hpp +++ b/src/lib/typed-allocation-manager.hpp @@ -63,12 +63,11 @@ -#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" -#include "lib/format.hpp" +#include "lib/format-util.hpp" #include "lib/typed-counter.hpp" #include "include/logging.h" diff --git a/src/lib/typed-counter.hpp b/src/lib/typed-counter.hpp index f208f8cdc..82ee099bb 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" @@ -66,6 +65,8 @@ namespace lib { + typedef size_t IxID; //////////////////////TICKET #863 + using std::vector; @@ -81,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) @@ -97,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) @@ -113,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); @@ -132,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/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 <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_UTIL_QUANT_H +#define LIB_UTIL_QUANT_H + +#include <cstdlib> + + + +namespace util { + + + /** helper to treat int or long division uniformly */ + template<typename I> + struct IDiv + { + I quot; + I rem; + + IDiv (I num, I den) + : quot(num/den) + , rem(num - quot*den) + { } + }; + + template<> + struct IDiv<int> + : div_t + { + IDiv<int> (int num, int den) + : div_t(div (num,den)) + { } + }; + + template<> + struct IDiv<long> + : ldiv_t + { + IDiv<long> (long num, long den) + : ldiv_t(ldiv (num,den)) + { } + }; + + 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 + * always rounds towards the next \em smaller integer, + * even for negative numbers. + * @warning floor on doubles performs way better + * @see UtilFloordiv_test + */ + template<typename I> + inline I + floordiv (I num, I den) + { + if (0 < (num^den)) + return num/den; + else + { // truncate similar to floor() + IDiv<I> 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<typename I> + inline IDiv<I> + floorwrap (I num, I den) + { + IDiv<I> 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 ce3755c52..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 <set> #include <string> @@ -57,72 +57,6 @@ namespace util { } - /** helper to treat int or long division uniformly */ - template<typename I> - struct IDiv; - - template<> - struct IDiv<int> - : div_t - { - IDiv<int> (int num, int den) - : div_t(div (num,den)) - { } - }; - - template<> - struct IDiv<long> - : ldiv_t - { - IDiv<long> (long num, long den) - : ldiv_t(ldiv (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<typename I> - inline I - floordiv (I num, I den) - { - if (0 < (num^den)) - return num/den; - else - { // truncate similar to floor() - IDiv<I> 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<typename I> - inline IDiv<I> - floorwrap (I num, I den) - { - IDiv<I> 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 ========= */ @@ -297,7 +231,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/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 8a7761263..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; @@ -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 @@ -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 16a20a2f2..8dbb29885 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 lib::meta::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/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/lumiera/main.cpp b/src/lumiera/main.cpp index 14d039b7d..69b5ecc22 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(); } @@ -63,16 +63,20 @@ main (int argc, const char* argv[]) lumiera::Option options (args); application.init (options); - session.depends (builder); +// session.depends (builder); netNode.depends (session); netNode.depends (engine); +// playOut.depends (engine); +// playOut.depends (session); // lumigui.depends (session); //////TODO commented out in order to be able to start up a dummy GuiStarterPlugin // lumigui.depends (engine); + player.depends (playOut); //////TODO dummy player, until we're able to render lumigui.depends (player); script.depends (session); script.depends (engine); application.maybeStart (session); + application.maybeStart (playOut); application.maybeStart (netNode); application.maybeStart (lumigui); application.maybeStart (script); 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/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/src/proc/asset.cpp b/src/proc/asset.cpp index 0f8560ad1..8b8865c65 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); @@ -65,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 ); } @@ -198,4 +199,4 @@ namespace asset { } -} // namespace asset +}} // namespace proc::asset diff --git a/src/proc/asset.hpp b/src/proc/asset.hpp index bef9463ac..f40721b6d 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> @@ -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 @@ -130,7 +131,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. @@ -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..73ee094f2 100644 --- a/src/proc/asset/buildinstruct.hpp +++ b/src/proc/asset/buildinstruct.hpp @@ -22,14 +22,14 @@ /** @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 ** */ -#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 b2d9b073f..bfe99a029 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> @@ -37,6 +35,7 @@ #include <boost/utility.hpp> +namespace proc { namespace asset { using std::tr1::static_pointer_cast; @@ -169,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 0d51e7fa2..445597fb8 100644 --- a/src/proc/asset/entry-id.hpp +++ b/src/proc/asset/entry-id.hpp @@ -48,17 +48,18 @@ #include "lib/hash-indexed.hpp" #include "lib/util.hpp" +#include <boost/functional/hash.hpp> #include <boost/operators.hpp> #include <iostream> #include <string> +namespace proc { namespace asset { using std::string; using std::ostream; - using lumiera::error::LUMIERA_ERROR_WRONG_TYPE; namespace idi { @@ -81,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); @@ -172,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 */ @@ -232,9 +242,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()); } @@ -277,5 +287,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..c3224ecb7 100644 --- a/src/proc/asset/inventory.hpp +++ b/src/proc/asset/inventory.hpp @@ -28,6 +28,7 @@ +namespace proc { namespace asset { @@ -40,8 +41,8 @@ namespace asset { {}; - typedef lumiera::P<Inventory> PInv; + typedef P<Inventory> PInv; -} // namespace asset +}} // namespace proc::asset #endif diff --git a/src/proc/asset/media.cpp b/src/proc/asset/media.cpp index 53cd1f1fd..a80628159 100644 --- a/src/proc/asset/media.cpp +++ b/src/proc/asset/media.cpp @@ -21,13 +21,16 @@ * *****************************************************/ -#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" #include "proc/asset/unknown.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/session/mobjectfactory.hpp" +#include "backend/media-access-facade.hpp" +#include "lib/time/timevalue.hpp" #include "lib/util.hpp" #include "include/logging.h" @@ -36,6 +39,10 @@ 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; @@ -43,12 +50,13 @@ using boost::smatch; using boost::regex_search; using std::tr1::dynamic_pointer_cast; +namespace error = lumiera::error; -namespace asset - { +namespace proc { +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 +124,7 @@ namespace asset } - lumiera::Time + Duration Media::getLength() const { return len_; @@ -132,6 +140,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. */ @@ -141,7 +150,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)) { @@ -155,10 +164,12 @@ namespace asset else { if (isnil (key.name)) key.name=extractName(file); - TODO ("file exists?"); - TODO ("extract media file properties"); - Time length(25); - TODO ("detecting and wiring multichannel compound media!"); + + MediaAccessFacade& maf = MediaAccessFacade::instance(); + MediaDesc& handle = maf.queryFile(key.name); + Duration length = handle.length; + + //////////////////////////////////////////////////////////TICKET #841 detecting and wiring multichannel compound media pM = new Media (key,file,length); } ASSERT (pM); @@ -219,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); } @@ -239,4 +250,4 @@ namespace asset -} // namespace asset +}} // namespace asset diff --git a/src/proc/asset/media.hpp b/src/proc/asset/media.hpp index d3b2c9024..3d3c0546c 100644 --- a/src/proc/asset/media.hpp +++ b/src/proc/asset/media.hpp @@ -35,15 +35,15 @@ #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" #include "proc/mobject/mobject.hpp" #include "proc/mobject/session/clip.hpp" +namespace proc { namespace asset { class Clip; @@ -51,8 +51,7 @@ namespace asset { class MediaFactory; class ProcPatt; - using lumiera::P; - using lumiera::Time; + using lib::time::Duration; template<> @@ -71,13 +70,13 @@ namespace asset { class Media : public Asset { string filename_; - const Time len_; + const Duration len_; 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; @@ -105,11 +104,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; @@ -122,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!!! }; @@ -142,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); @@ -152,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); }; @@ -162,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..1b0bbaa13 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; @@ -79,7 +80,7 @@ namespace asset { -} // namespace asset +}} // namespace proc::asset @@ -94,6 +95,7 @@ namespace asset { //#include "proc/asset/sequence.hpp" +namespace proc { namespace asset { using meta::Descriptor; @@ -104,4 +106,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 77e08b71a..8b6ffb7d1 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 @@ -58,14 +58,13 @@ #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" +namespace proc { namespace asset { class Meta; @@ -120,7 +119,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()); } @@ -161,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 ad3599a80..d72878f03 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 { @@ -74,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) @@ -82,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; } @@ -126,6 +130,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. @@ -136,7 +143,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_)) { @@ -154,7 +161,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)); } @@ -170,4 +177,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 cced6e638..0f424aece 100644 --- a/src/proc/asset/proc.hpp +++ b/src/proc/asset/proc.hpp @@ -35,14 +35,13 @@ #ifndef ASSET_PROC_H #define ASSET_PROC_H -#include "pre_a.hpp" - #include "proc/asset.hpp" #include "lib/factory.hpp" -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" +namespace proc { namespace asset { @@ -77,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); @@ -126,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..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 @@ -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..b0a8fe20f 100644 --- a/src/proc/asset/struct-scheme.hpp +++ b/src/proc/asset/struct-scheme.hpp @@ -45,6 +45,11 @@ using boost::format; +namespace lumiera { + class StreamType; +} + +namespace proc { namespace mobject { namespace session { @@ -52,9 +57,6 @@ namespace session { class Clip; }} -namespace lumiera { - class StreamType; -} namespace asset{ @@ -166,5 +168,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 4fd1b7f2b..773ddf2c8 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" @@ -70,6 +68,7 @@ +namespace proc { namespace asset { using std::string; @@ -163,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 ca25adf74..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"); @@ -38,7 +39,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..."); @@ -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 fa3f5a291..54f234c71 100644 --- a/src/proc/asset/unknown.hpp +++ b/src/proc/asset/unknown.hpp @@ -25,13 +25,17 @@ #define ASSET_UNKNOWN_H #include "proc/asset/media.hpp" +#include "lib/time/timevalue.hpp" +namespace proc { 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,11 +46,11 @@ 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: - virtual Media::PMedia getOrg() throw(lumiera::error::Invalid); + virtual Media::PMedia getOrg(); }; @@ -55,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 new file mode 100644 index 000000000..12e660e67 --- /dev/null +++ b/src/proc/asset/viewer.cpp @@ -0,0 +1,71 @@ +/* + Viewer - asset corresponding to a viewer element in the GUI + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/asset/viewer.hpp" +//#include "proc/mobject/session/track.hpp" +//#include "proc/mobject/placement.hpp" +//#include "proc/mobject/session/mobjectfactory.hpp" +//#include "proc/mobject/session/binding.hpp" +//#include "proc/assetmanager.hpp" + + +namespace proc { +namespace asset { + +// using lib::AutoRegistered; + + + + /** @todo anything significant to do here??? */ + Viewer::Viewer (const Asset::Ident& idi) + : Struct (idi) + { + UNIMPLEMENTED ("anything regarding Viewer Assets"); + } + + +//PViewer +//Viewer::create (Asset::Ident const& idi) +//{ +// REQUIRE (getRegistry, "can't create a Timeline prior to session initialisation"); +// +// PTimeline newElement (AssetManager::instance().wrap (*new Viewer(idi))); +// getRegistry().append (newElement); +// +// ENSURE (newElement); +// ENSURE (getRegistry().isRegistered (*newElement)); +// return newElement; +//} + + + void + Viewer::unlink () + { +// AutoRegistered<Timeline>::detach(); + Struct::unlink(); + } + + + + +}} // namespace proc::asset diff --git a/src/proc/asset/viewer.hpp b/src/proc/asset/viewer.hpp new file mode 100644 index 000000000..618e29aa5 --- /dev/null +++ b/src/proc/asset/viewer.hpp @@ -0,0 +1,107 @@ +/* + VIEWER.hpp - asset corresponding to a viewer element in the GUI + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file viewer.hpp + ** structural element corresponding to a viewer in the GUI. + ** This Asset marks an attachment point, allowing other (output producing) + ** elements to be connected to ("viewer attachment"). The typical standard case + ** is a Timeline: It features a set of global pipes (busses), which collect all + ** produced data. Yet without an explicit output connection, this data can't be + ** generated, because in Lumiera, actual output is always retrieved ("pulled") + ** starting from an output sink. Thus, in order to \em perform (play, render) + ** the timeline, an "view connection" needs to be established: this connection + ** is represented by an session::BindingMO, linking the Timeline to an + ** asset::Viewer. Consequently, the same output mapping and translation + ** mechanism used for Sequence-Timeline- (and VirtualClip-)Bindings + ** is employed in this situation to figure out the real output port. + ** + ** @todo WIP-WIP-WIP as of 5/11 + ** + ** @see Session + ** @see Timeline + ** @see StructFactory + ** + */ + + +#ifndef ASSET_VIEWER_H +#define ASSET_VIEWER_H + +#include "proc/asset/struct.hpp" +//#include "proc/mobject/mobject.hpp" +//#include "proc/mobject/placement.hpp" +#include "proc/mobject/mobject-ref.hpp" +//#include "proc/mobject/session/binding.hpp" ////TODO avoidable?? +#include "lib/p.hpp" +#include "lib/element-tracker.hpp" + + +//#include <vector> +//#include <string> + +//using std::vector; +//using std::string; + +namespace proc { +namespace mobject { +namespace session { + + class Binding; + typedef MORef<Binding> RBinding; +}} + + +namespace asset { + + + class Viewer; + typedef P<Viewer> PViewer; + + + /** + * TODO type comment + */ + class Viewer + : public Struct +// , public lib::AutoRegistered<Viewer> + { + + Viewer (Ident const&); /////TODO ctor params???? + + public: + /** create and register a new Timeline instance */ +// static PTimeline create (Asset::Ident const& idi, RBinding const& sequenceBinding); + + protected: + virtual void unlink (); + + }; + + + + +///////////////////////////TODO currently just fleshing the API + + +}} // namespace proc::asset +#endif diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index 6677b0cca..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 { /** @@ -111,11 +112,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); @@ -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 d5102eeb4..61567c582 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" @@ -54,7 +52,7 @@ using std::string; using std::list; - +namespace proc { namespace asset { class DB; @@ -122,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 ebf5ca48f..6e6e2e491 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 ** */ @@ -42,11 +41,11 @@ - /* common types frequently used... */ + /* frequently used common types... */ #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 @@ -61,7 +60,7 @@ namespace lumiera { /* additional global configuration goes here... */ - + } // namespace lumiera @@ -69,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/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..61c7bf70d 100644 --- a/src/proc/control/argument-tuple-accept.hpp +++ b/src/proc/control/argument-tuple-accept.hpp @@ -63,12 +63,13 @@ +namespace proc { namespace control { namespace bind_arg { // internals.... - using namespace lumiera::typelist; + using namespace lib::meta; // @@ -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 e71a60a10..17b53a822 100644 --- a/src/proc/control/command-argument-holder.hpp +++ b/src/proc/control/command-argument-holder.hpp @@ -52,9 +52,9 @@ +namespace proc { namespace control { - using lib::TypedAllocationManager; using lib::InPlaceBuffer; using std::string; @@ -279,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 90b29435c..118beb68a 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. ** @@ -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" @@ -75,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" @@ -89,17 +88,18 @@ +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; @@ -214,7 +214,7 @@ namespace control { friend bool compare (ParamAccessor const&, ParamAccessor const&) { - return true;; + return true; } }; @@ -303,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 b3e777c4c..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). ** @@ -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" @@ -67,7 +66,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" @@ -77,6 +76,7 @@ +namespace proc { namespace control { using std::tr1::shared_ptr; @@ -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; @@ -326,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 7d3c18536..bec6be23d 100644 --- a/src/proc/control/command-impl.hpp +++ b/src/proc/control/command-impl.hpp @@ -55,9 +55,9 @@ #include <tr1/functional> +namespace proc { namespace control { - using lib::TypedAllocationManager; using std::tr1::function; using std::tr1::shared_ptr; @@ -238,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 660814098..fd4ee784a 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" @@ -54,9 +53,10 @@ +namespace proc { namespace control { - using namespace lumiera::typelist; + using namespace lib::meta; @@ -221,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 9a4f2d13d..b80259a09 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" @@ -56,6 +55,7 @@ +namespace proc { namespace control { using std::ostream; @@ -156,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 1a94ee13f..c749a2dbc 100644 --- a/src/proc/control/command-registry.hpp +++ b/src/proc/control/command-registry.hpp @@ -57,11 +57,9 @@ #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" -#include "lib/format.hpp" #include "include/logging.h" #include "lib/util.hpp" @@ -79,6 +77,7 @@ +namespace proc { namespace control { using boost::hash; @@ -269,5 +268,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 3c9df666f..93ac35205 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) @@ -46,40 +46,33 @@ #ifndef CONTROL_COMMAND_SIGNATURE_H #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> +namespace proc { 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; + using lib::meta::FunctionSignature; + using lib::meta::FunctionTypedef; + using lib::meta::Types; + using lib::meta::Append; + using lib::meta::SplitLast; + - /** - * Metaprogramming helper for building Command function signatures. + * Metaprogramming helper for building Command function signatures. * The complete definition context of any command is templated to the signature * of the actual command operation and to the memento type. The typedefs embedded - * within CommandSignature<SIG,MEM> allows accepting suitable typed functions + * within CommandSignature<SIG,MEM> allows for accepting suitable typed functions * to implement the command in question. */ template<typename SIG, typename MEM> @@ -93,7 +86,7 @@ namespace control { public: typedef typename FunctionTypedef<void, Args>::Sig OperateSig; - typedef typename FunctionTypedef<MEM, Args>::Sig CaptureSig; + typedef typename FunctionTypedef<MEM, Args>::Sig CaptureSig; typedef typename FunctionTypedef<void, ExtendedArgs>::Sig UndoOp_Sig; typedef Args CmdArgs; typedef MEM Memento; @@ -103,16 +96,23 @@ namespace control { /** - * Type analysis helper template. + * Type analysis helper template. * Used for dissecting a given type signature to derive * the related basic operation signature, the signature of a possible Undo-function * and the signature necessary for capturing undo information. The implementation * relies on re-binding an embedded type defining template, based on the actual * case, as identified by the structure of the given parameter signature. + * + * To use this template, it is instantiated with the signature of a functor object + * in question. Depending on the actual situation, the compiler will then either + * pick Case1 or Case2 -- thus allowing the client in any case to pick up the + * correct signatures for Operation, Capture and Undo-function from the + * public typedefs within \c UndoSignature */ template<typename SIG> class UndoSignature { + // preparation: dissect the function signature into arguments and result typedef typename FunctionSignature< function<SIG> >::Args Args; typedef typename FunctionSignature< function<SIG> >::Ret Ret; @@ -152,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 86632febc..5ec653634 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" @@ -68,12 +68,13 @@ +namespace proc { 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 @@ -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 f414e25a1..331243480 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" @@ -53,6 +52,7 @@ +namespace proc { namespace control { using std::string; @@ -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> @@ -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 19d3d7d5f..daaa58f8a 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" @@ -50,6 +49,7 @@ +namespace proc { namespace control { namespace { // concrete command handling patterns @@ -239,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 0afa07b8a..e01e3dcb2 100644 --- a/src/proc/control/mediaimpllib.hpp +++ b/src/proc/control/mediaimpllib.hpp @@ -25,10 +25,11 @@ #define CONTROL_MEDIAIMPLLIB_H -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" +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..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> @@ -53,12 +53,13 @@ #include <string> +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 @@ -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 a4161aa42..14f76d09e 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" @@ -44,6 +43,7 @@ +namespace proc { namespace control { // using lib::Symbol; @@ -75,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 8c4d50672..5b386c99b 100644 --- a/src/proc/control/stypemanager.cpp +++ b/src/proc/control/stypemanager.cpp @@ -27,9 +27,9 @@ #include "include/lifecycle.h" +namespace proc { namespace control { - using lumiera::StreamType; using lib::Symbol; /* ======= stream type manager lifecycle ==========*/ @@ -114,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 ======= @@ -122,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 0f40408cc..d59120b65 100644 --- a/src/proc/control/stypemanager.hpp +++ b/src/proc/control/stypemanager.hpp @@ -21,20 +21,20 @@ */ -#ifndef CONTROL_STYPEMANAGER_H -#define CONTROL_STYPEMANAGER_H +#ifndef PROC_CONTROL_STYPEMANAGER_H +#define PROC_CONTROL_STYPEMANAGER_H -#include "lib/streamtype.hpp" +#include "proc/streamtype.hpp" #include "lib/singleton.hpp" #include <boost/scoped_ptr.hpp> +namespace proc { namespace control { using lib::Symbol; - using lumiera::StreamType; class STypeManager @@ -103,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 a5593f9c7..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,9 +49,10 @@ #include <boost/noncopyable.hpp> +namespace proc { namespace control { -// using lumiera::P; +// using lib::P; // using lumiera::Query; // using std::string; @@ -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/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 new file mode 100644 index 000000000..b8fc66ee3 --- /dev/null +++ b/src/proc/engine/buffer-local-key.hpp @@ -0,0 +1,129 @@ +/* + 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 proc { +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 + { + union OpaqueData + { + uint64_t _as_number; + void* _as_pointer; + }; + + OpaqueData privateID_; + + public: + explicit + LocalKey (uint64_t opaqueValue=0) + { + privateID_._as_number = opaqueValue; + } + + LocalKey (void* impl_related_ptr) + { + privateID_._as_number = 0; + privateID_._as_pointer = impl_related_ptr; + } + + + 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_._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) + { + privateID_ = o.privateID_; + return *this; + } + + /** but Key assignments are acceptable */ + friend class metadata::Key; + }; + + +}} // namespace proc::engine +#endif diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp new file mode 100644 index 000000000..7aad2ceeb --- /dev/null +++ b/src/proc/engine/buffer-metadata.hpp @@ -0,0 +1,794 @@ +/* + 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 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 + ** prominent example is the frame cache, which obviously needs to keep track of + ** the buffers after the render engine is finished, while the engine code + ** just accesses yet another buffer to place the results of calculations. + ** + ** These additional distinctions and properties are associated with the help of the + ** BufferDescriptor, embedded into each BuffHandle. While the engine just uses these + ** handles in the way of a pointer, the buffer descriptor acts as an additional tag + ** attached to the buffer access, allowing to re-access a context within the + ** buffer provider implementation. + ** + ** @see buffer-provider.hpp + ** @see BufferMetadata_test + ** @see BufferProviderProtocol_test + */ + +#ifndef PROC_ENGINE_BUFFR_METADATA_H +#define PROC_ENGINE_BUFFR_METADATA_H + + +#include "lib/error.hpp" +#include "lib/symbol.hpp" +#include "lib/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> + + +namespace proc { +namespace engine { + + using lib::HashVal; + using lib::Literal; + using util::for_each; + + namespace error = lumiera::error; + + namespace metadata { + class Key; + class Entry; + } + class BufferMetadata; + + + + + /** + * Buffer states + * usable within BufferProvider + * and stored within the metadata + */ + enum BufferState + { 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 + }; + + + + + + + namespace { // internal constants to mark the default case + + const LocalKey UNSPECIFIC; + const TypeHandler RAW_BUFFER; + + inline bool + nontrivial (TypeHandler const& toVerify) + { + return RAW_BUFFER != toVerify; + } + + inline bool + nontrivial (LocalKey const& toVerify) + { + return UNSPECIFIC != toVerify; + } + } + + + + /* === Metadata Implementation === */ + + namespace metadata { + + using error::LUMIERA_ERROR_LIFECYCLE; + using error::LUMIERA_ERROR_BOTTOM_VALUE; + + namespace { // details of hash calculation + template<typename VAL> + HashVal + chainedHash(HashVal accumulatedHash, VAL changedValue) + { + boost::hash_combine (accumulatedHash, changedValue); + return accumulatedHash; + } + } + + + /** + * 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_; + HashVal hashID_; + + protected: + size_t storageSize_; + TypeHandler instanceFunc_; + LocalKey specifics_; + + + public: + /** build a standard basic key describing a kind of Buffer. + * @param familyID basic hash seed value to distinguish + * families of buffer types managed by + * different BufferProvider instances + * @param storageSize fundamental info: buffer size + */ + Key (HashVal familyID, size_t storageSize) + : parent_(familyID) + , hashID_(chainedHash (familyID, storageSize)) + , storageSize_(storageSize) + , instanceFunc_(RAW_BUFFER) + , specifics_(UNSPECIFIC) + { } + + // standard copy operations permitted + + /** create a derived buffer type description. + * Using a different storage size than the parent type, + * all else remaining the same + */ + Key (Key const& parent, size_t differingStorageSize) + : parent_(parent.hashID_) + , hashID_(chainedHash (parent_, differingStorageSize)) + , storageSize_(differingStorageSize) // differing from parent + , instanceFunc_(parent.instanceFunc_) + , specifics_(parent.specifics_) + { } + + + /** create a derived buffer type description. + * Using different ctor and dtor functions, + * all else remaining the same as with parent + */ + Key (Key const& parent, TypeHandler const& differingTypeHandlerFunctions) + : parent_(parent.hashID_) + , hashID_(chainedHash (parent_, differingTypeHandlerFunctions)) + , storageSize_(parent.storageSize_) + , instanceFunc_(differingTypeHandlerFunctions) // differing from parent + , specifics_(parent.specifics_) + { } + + + /** create a derived buffer type description. + * Using a different private ID than the parent type, + * all else remaining the same + */ + Key (Key const& parent, LocalKey anotherTypeSpecificInternalID) + : parent_(parent.hashID_) + , hashID_(chainedHash (parent_, anotherTypeSpecificInternalID)) + , storageSize_(parent.storageSize_) + , instanceFunc_(parent.instanceFunc_) + , specifics_(anotherTypeSpecificInternalID) // differing from parent + { } + + + /** 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, 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; + } + + 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_; } + + HashVal parentKey() const { return parent_;} + operator HashVal() const { return hashID_;} + }; + + + /** + * 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::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. + */ + class Entry + : public Key + { + BufferState state_; + void* buffer_; + + protected: + Entry (Key const& parent, void* bufferPtr =0, LocalKey const& implID =UNSPECIFIC) + : Key (Key::forEntry (parent, bufferPtr, implID)) + , state_(bufferPtr? LOCKED:NIL) + , buffer_(bufferPtr) + { } + + /// BufferMetadata is allowed to create + friend class engine::BufferMetadata; + + // standard copy operations permitted + + public: + /** is this Entry currently associated to a + * concrete buffer? Is this buffer in use? */ + bool + isLocked() const + { + ASSERT (!buffer_ || (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 NIL == state_ && !buffer_; + } + + + BufferState + state() const + { + return state_; + } + + void* + access() + { + __must_not_be_NIL(); + __must_not_be_FREE(); + + ENSURE (buffer_); + return buffer_; + } + + /** Buffer state machine */ + Entry& + mark (BufferState newState) + { + __must_not_be_NIL(); + + 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) + ||(state_ == BLOCKED && newState == FREE)) + { + // allowed transition + if (newState == FREE) + invokeEmbeddedDtor_and_clear(); + if (newState == LOCKED) + invokeEmbeddedCtor(); + state_ = newState; + return *this; + } + + throw error::Fatal ("Invalid buffer state transition."); + } + + Entry& + lock (void* newBuffer) + { + __must_be_FREE(); + buffer_ = newBuffer; + 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 + * constructor function, which typically builds some + * content object into the buffer by placement new. */ + void + invokeEmbeddedCtor() + { + __buffer_required(); + 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() + { + __buffer_required(); + if (nontrivial (instanceFunc_)) + instanceFunc_.destroyAttached (buffer_); + buffer_ = 0; + } + + private: + void + __must_not_be_NIL() const + { + if (NIL == state_) + throw error::Fatal ("Buffer metadata entry with state==NIL encountered." + "State transition logic broken (programming error)" + , LUMIERA_ERROR_LIFECYCLE); + } + + 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 ); + } + + 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 + * 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 + { + 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) + { + 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) + { + 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); + } + ERROR_LOG_AND_IGNORE (engine,"Shutdown of BufferProvider metadata store") + + 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 + + + + + + /* ===== 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 + { + Literal id_; + HashVal family_; + + metadata::Table table_; + + + 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. + */ + BufferMetadata (Literal implementationID) + : id_(implementationID) + , family_(hash_value(id_)) + { } + + /** combine the distinguishing properties + * into a single type key, which will be known/remembered + * from that point on. Properties are combined according to + * a fixed type specialisation order, with the buffer size + * forming the base level, possible TypeHandler functors the + * second level, and implementation defined LocalKey entries + * the third level. All these levels describe abstract type + * keys, not entries for concrete buffers. The latter are + * always created as children of a known type key. + */ + Key + key ( size_t storageSize + , TypeHandler instanceFunc =RAW_BUFFER + , LocalKey specifics =UNSPECIFIC) + { + REQUIRE (storageSize); + Key typeKey = trackKey (family_, storageSize); + + if (nontrivial(instanceFunc)) + typeKey = trackKey (typeKey, instanceFunc); + + if (nontrivial(specifics)) + typeKey = trackKey (typeKey, specifics); + + return typeKey; + } + + /** create a sub-type, using a different type/handler functor */ + Key + key (Key const& parentKey, TypeHandler const& instanceFunc) + { + return trackKey (parentKey, instanceFunc); + } + + /** create a sub-type, + * using a different private-ID (implementation defined) */ + Key + key (Key const& parentKey, LocalKey specifics) + { + return trackKey (parentKey, specifics); + } + + /** 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, void* concreteBuffer, LocalKey const& implID =UNSPECIFIC) + { + Key derivedKey = Key::forEntry (parentKey, concreteBuffer); + Entry* existing = table_.fetch (derivedKey); + + return existing? *existing + : markLocked (parentKey,concreteBuffer,implID); + } + + /** 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. + * @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 + * @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. + */ + Entry& + lock (Key const& parentKey + ,void* concreteBuffer + ,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, implID); + 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 && 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->lock (concreteBuffer); + } + + /** 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) + { + Entry* entry = table_.fetch (hashID); + if (!entry) + throw error::Invalid ("Attempt to access an unknown buffer metadata entry"); + + return *entry; + } + + bool + isKnown (HashVal key) const + { + return bool(table_.fetch (key)); + } + + bool + isLocked (HashVal key) const + { + const Entry* entry = table_.fetch (key); + return entry + && entry->isLocked(); + } + + + + /* == 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, LocalKey const& implID =UNSPECIFIC) + { + if (!buffer) + throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?" + , error::LUMIERA_ERROR_BOTTOM_VALUE); + + return this->lock(parentKey, buffer, implID, 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; + + 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 (HashVal(entry)); + } + + + + 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; + table_.store (Entry (key, NULL)); + } + + Entry& + store_and_lock (Entry const& metadata) + { + Entry& newEntry = table_.store (metadata); + try + { + newEntry.invokeEmbeddedCtor(); + ENSURE (LOCKED == newEntry.state()); + ENSURE (newEntry.access()); + } + catch(...) + { + newEntry.mark(FREE); + throw; + } + return newEntry; + } + }; + + + + + +}} // namespace proc::engine +#endif diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp new file mode 100644 index 000000000..e4224556a --- /dev/null +++ b/src/proc/engine/buffer-provider.cpp @@ -0,0 +1,316 @@ +/* + BufferProvider - Abstraction for Buffer management during playback/render + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/error.hpp" +#include "proc/engine/buffer-provider.hpp" +#include "proc/engine/buffer-metadata.hpp" +#include "lib/util.hpp" + +using util::isSameObject; + +namespace proc { +namespace engine { + + + namespace { // impl. details and definitions + + const uint DEFAULT_DESCRIPTOR = 0; + + } + + 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)) + { } + + BufferProvider::~BufferProvider() { } + + + /** @internal verify the given descriptor. + * @return true if it corresponds to a buffer + * currently locked and usable by client code + */ + bool + BufferProvider::verifyValidity (BufferDescriptor const& bufferID) const + { + return meta_->isLocked (bufferID); + } + + + BufferDescriptor + BufferProvider::getDescriptorFor (size_t storageSize) + { + return BufferDescriptor (*this, meta_->key (storageSize)); + } + + + BufferDescriptor + BufferProvider::getDescriptorFor(size_t storageSize, TypeHandler specialTreatment) + { + return BufferDescriptor (*this, meta_->key (storageSize, specialTreatment)); + } + + + size_t + BufferProvider::getBufferSize (HashVal typeID) const + { + metadata::Key& typeKey = meta_->get (typeID); + return typeKey.storageSize(); + } + + + /** 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) + { + metadata::Key& typeKey = meta_->get (typeID); + metadata::Entry& entry = meta_->markLocked(typeKey, storage, implID); + + return BuffHandle (BufferDescriptor(*this, entry), storage); + } + + + /** 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& handle) + { + metadata::Entry& metaEntry = meta_->get (handle.entryID()); + mark_emitted (metaEntry.parentKey(), metaEntry.localKey()); + metaEntry.mark(EMITTED); + } + + + /** 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& handle) + try { + metadata::Entry& metaEntry = meta_->get (handle.entryID()); + metaEntry.mark(FREE); // might invoke embedded dtor function + detachBuffer (metaEntry.parentKey(), metaEntry.localKey()); + meta_->release (metaEntry); + } + 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) + { + 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 + } + + + /** @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) + 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 + BufferProvider::was_created_by_this_provider (BufferDescriptor const& descr) const + { + return isSameObject (*this, *descr.provider_); + } + + + + + + /* === BufferDescriptor and BuffHandle === */ + + bool + BufferDescriptor::verifyValidity() const + { + return provider_->verifyValidity(*this); + } + + + size_t + BufferDescriptor::determineBufferSize() const + { + return provider_->getBufferSize (*this); + } + + + void + BuffHandle::emit() + { + REQUIRE (isValid()); + descriptor_.provider_->emitBuffer(*this); + } + + + void + BuffHandle::release() + { + if (pBuffer_) + { + REQUIRE (isValid()); + descriptor_.provider_->releaseBuffer(*this); + pBuffer_ = 0; + } + ENSURE (!isValid()); + } + + + void + BuffHandle::emergencyCleanup() + { + 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 + } + + + + +}} // namespace engine diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp new file mode 100644 index 000000000..e0aaff78d --- /dev/null +++ b/src/proc/engine/buffer-provider.hpp @@ -0,0 +1,170 @@ +/* + BUFFER-PROVIDER.hpp - Abstraction for Buffer management during playback/render + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file buffer-provider.hpp + ** Abstraction to represent buffer management and lifecycle within the render engine. + ** It turns out that -- throughout the render engine implementation -- we never need + ** direct access to the buffers holding media data. Buffers are just some entity to be \em managed, + ** i.e. "allocated", "locked" and "released"; the actual meaning of these operations is an implementation detail. + ** The code within the render engine just pushes around BufferHandle objects, which act as a front-end, + ** being created by and linked to a BufferProvider implementation. There is no need to manage the lifecycle + ** of buffers automatically, because the use of buffers is embedded into the render calculation cycle, + ** which follows a rather strict protocol anyway. Relying on the capabilities of the scheduler, + ** the sequence of individual jobs in the engine ensures... + ** - that the availability of a buffer was ensured prior to planning a job ("buffer allocation") + ** - that a buffer handle was obtained ("locked") prior to any operation requiring a buffer + ** - that buffers are marked as free ("released") after doing the actual calculations. + ** + ** @see state.hpp + ** @see output-slot.hpp + */ + +#ifndef PROC_ENGINE_BUFFR_PROVIDER_H +#define PROC_ENGINE_BUFFR_PROVIDER_H + + +#include "lib/error.hpp" +#include "lib/symbol.hpp" +#include "proc/engine/buffhandle.hpp" +#include "proc/engine/type-handler.hpp" +#include "proc/engine/buffer-local-key.hpp" + +#include <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> + + +namespace proc { +namespace engine { + + using boost::scoped_ptr; + using lib::Literal; + + + 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 + * - optionally announcing the required buffer(s) beforehand + * - "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 + { + scoped_ptr<BufferMetadata> meta_; + + + 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 ABC + + + uint announce (uint count, BufferDescriptor const&); + + BuffHandle lockBuffer (BufferDescriptor const&); + void emitBuffer (BuffHandle const&); + void releaseBuffer (BuffHandle const&); + + 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); + BufferDescriptor getDescriptorFor(size_t storageSize, TypeHandler specialTreatment); + + template<typename BU> + BufferDescriptor getDescriptor(); + + + + /* === API for BuffHandle internal access === */ + + bool verifyValidity (BufferDescriptor const&) const; + size_t getBufferSize (HashVal typeID) const; + + protected: + BuffHandle buildHandle (HashVal typeID, void* storage, LocalKey const&); + + bool was_created_by_this_provider (BufferDescriptor const&) 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() + { + BufferDescriptor attach_object_automatically = getDescriptor<BU>(); + return lockBuffer (attach_object_automatically); + } + + + /** 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() + { + return getDescriptorFor (sizeof(BU), TypeHandler::create<BU>()); + } + + +}} // namespace proc::engine +#endif diff --git a/src/proc/engine/buffhandle-attach.hpp b/src/proc/engine/buffhandle-attach.hpp new file mode 100644 index 000000000..f0447bcad --- /dev/null +++ b/src/proc/engine/buffhandle-attach.hpp @@ -0,0 +1,130 @@ +/* + 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 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 + ** 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 proc { +namespace engine { + + + + + /* === BuffHandle Implementation === */ + + +#define _EXCEPTION_SAFE_INVOKE(_CTOR_) \ + try \ + { \ + return *new(pBuffer_) _CTOR_; \ + } \ + catch(...) \ + { \ + emergencyCleanup(); /* EX_FREE */ \ + pBuffer_ = 0; \ + throw; \ + } + + /** 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 proc::engine +#endif diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index 6cdca586c..ec8228ca7 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -21,18 +21,29 @@ */ /** @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 - ** 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. - ** - ** @see nodewiring-def.hpp - ** @see nodeoperation.hpp - ** @see bufftable.hpp storage for the buffer table + ** 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 + ** 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. + ** + ** 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 */ @@ -41,29 +52,98 @@ #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; + 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. + */ + class BufferDescriptor + { + protected: + BufferProvider* provider_; + HashVal subClassification_; + + BufferDescriptor(BufferProvider& manager, HashVal detail) + : provider_(&manager) + , subClassification_(detail) + { } + + friend class BufferProvider; + friend class BuffHandle; + + public: + // using standard copy operations + + bool verifyValidity() const; + size_t determineBufferSize() const; + + operator HashVal() const { return subClassification_; } + }; + + /** * 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. */ - struct BuffHandle - : lib::BoolCheckable<BuffHandle> + class BuffHandle + : public lib::BoolCheckable<BuffHandle> { - typedef lumiera::StreamType::ImplFacade::DataBuffer Buff; + typedef StreamType::ImplFacade::DataBuffer Buff; + + BufferDescriptor descriptor_; + Buff* pBuffer_; + + + public: typedef Buff* PBuff; - PBuff - operator->() const - { - return pBuffer_; - } + /** @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(); + void release(); + + + 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 { @@ -74,49 +154,32 @@ namespace engine { bool isValid() const { - return pBuffer_; + return bool(pBuffer_) + && descriptor_.verifyValidity(); } + HashVal + entryID() const + { + return HashVal(descriptor_); + } - //////////////////////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) - { } + size_t + size() const + { + return descriptor_.determineBufferSize(); + } private: - PBuff pBuffer_; - long sourceID_; - }; - - - /** - * Buffer Type information. - * Given a BufferDescriptor, it is possible to allocate a buffer - * of suitable size and type by using State::allocateBuffer(). - */ - struct BufferDescriptor - { - lumiera::StreamType& sType_; - }; - - - class ProcNode; - typedef ProcNode* PNode; - - - struct ChannelDescriptor ///////TODO collapse this with BufferDescriptor? - { - BufferDescriptor bufferType; - }; - - struct InChanDescriptor : ChannelDescriptor - { - PNode dataSrc; ///< the ProcNode to pull this input from - uint srcChannel; ///< output channel to use on the predecessor node + template<typename BU> + void takeOwnershipFor(); + void takeOwnershipFor(BufferDescriptor const& type); + + void emergencyCleanup(); }; -} // namespace engine + +}} // namespace proc::engine #endif diff --git a/src/proc/engine/bufftable-obsolete.hpp b/src/proc/engine/bufftable-obsolete.hpp new file mode 100644 index 000000000..665726804 --- /dev/null +++ b/src/proc/engine/bufftable-obsolete.hpp @@ -0,0 +1,188 @@ +/* + 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/channel-descriptor.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 proc { +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; + }; + + class BufferDescriptor; + + /** 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 proc::engine +#endif diff --git a/src/proc/engine/bufftable.hpp b/src/proc/engine/bufftable.hpp index 3682b51ff..14ea61977 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> @@ -35,6 +36,7 @@ +namespace proc { namespace engine { using std::pair; @@ -48,115 +50,110 @@ namespace engine { * data buffers. The tables are supposed to be implemented as bare "C" arrays, * thus the array of real buffer pointers can be fed directly to the * processing function of the respective node. + * + * @todo this whole design is a first attempt and rather clumsy. It should be reworked + * to use a single contiguous memory area and just layer the object structure on top + * (by using placement new). Yet the idea of an stack-like organisation should be retained */ struct BuffTable { typedef BuffHandle * PHa; typedef BuffHandle::PBuff * PBu; - typedef pair<PHa const,PBu const> Chunk; - PHa outHandle; - PHa inHandle; - PBu outBuff; - PBu inBuff; - }; - - - class BuffTableStorage - { - vector<BuffHandle> hTab_; - vector<BuffHandle::PBuff> pTab_; - size_t level_; + struct StorageChunk + { }; - 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) + template<uint count> + struct Storage { - 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]); - } + enum{size = count * sizeof(StorageChunk)}; + }; - void - release (uint slots) + class Builder { - ASSERT (slots <= level_); - REQUIRE (level_ <= hTab_.size()); - REQUIRE (level_ <= pTab_.size()); - - level_ -= slots; - } + public: + Builder& announce (uint count, BufferDescriptor const& type); + BuffTable& build(); + }; - bool - level_check (BuffTable::Chunk& prev_level) - { - return prev_level.first == &hTab_[level_] - && prev_level.second == &pTab_[level_]; - } - }; - - - /** - * to be allocated on the stack while evaluating a ProcNode#pull() call. - * The "current" State (StateProxy) maintains a BuffTableStorage (=pool), - * which can be used to crate such chunks. The claiming and releasing of - * slots in the BuffTableStorage is automatically tied to BuffTableChunk - * object's lifecycle. - */ - class BuffTableChunk - : public BuffTable, - boost::noncopyable - { - const uint siz_; - BuffTable::Chunk tab_; - BuffTableStorage& sto_; + static Builder& prepare (const size_t STORAGE_SIZE, void* storage); - public: - BuffTableChunk (WiringDescriptor const& wd, BuffTableStorage& storage) - : siz_(wd.nrI + wd.nrO), - tab_(storage.claim (siz_)), - sto_(storage) - { - const uint nrO(wd.nrO); - - // Setup the public visible table locations - this->outHandle = &tab_.first[ 0 ]; - this->inHandle = &tab_.first[nrO]; - this->outBuff = &tab_.second[ 0 ]; - this->inBuff = &tab_.second[nrO]; - } + void lockBuffers(); + void releaseBuffers(); - ~BuffTableChunk () - { - sto_.release (siz_); - ASSERT ( sto_.level_check (tab_), - "buffer management logic broken."); - } + typedef vector<BuffHandle> BuffHandleTable; + typedef lib::RangeIter<BuffHandleTable::iterator> iterator; + + iterator buffers(); + iterator inBuffers(); + iterator outBuffers(); }; + /* === Implementation === */ -} // namespace engine + 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"); + } + + + + + + +}} // namespace proc::engine #endif diff --git a/src/proc/engine/calc-stream.hpp b/src/proc/engine/calc-stream.hpp new file mode 100644 index 000000000..d476d3e1a --- /dev/null +++ b/src/proc/engine/calc-stream.hpp @@ -0,0 +1,118 @@ +/* + CALC-STREAM.hpp - abstraction representing a series of scheduled calculations + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + + +#ifndef PROC_ENGINE_CALC_STREAM_H +#define PROC_ENGINE_CALC_STREAM_H + + +#include "lib/error.hpp" +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +// +//#include <boost/noncopyable.hpp> +//#include <boost/scoped_ptr.hpp> +//#include <string> +#include <vector> + + +namespace proc { +namespace engine{ + +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// 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 + }; + + + + + /********************************************************* + * 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 + { + RenderEnvironmentClosure* env_; + + protected: + CalcStream (RenderEnvironmentClosure& engine) + : env_(&engine) + { } + + friend class EngineService; + + + public: + CalcStream() + : env_(0) + { } + + CalcStream (CalcStream const& o) + : env_(o.env_) + { } + + ~CalcStream() { } + + }; + + typedef std::vector<CalcStream> CalcStreams; + + + + + +} // namespace engine +} // namespace proc +#endif diff --git a/src/proc/engine/channel-descriptor.hpp b/src/proc/engine/channel-descriptor.hpp new file mode 100644 index 000000000..5133bc5f8 --- /dev/null +++ b/src/proc/engine/channel-descriptor.hpp @@ -0,0 +1,76 @@ +/* + 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 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-obsolete.hpp storage for the buffer table + ** @see engine::RenderInvocation + */ + +#ifndef ENGINE_CHANNEL_DESCRIPTOR_H +#define ENGINE_CHANNEL_DESCRIPTOR_H + + +#include "lib/error.hpp" +#include "proc/streamtype.hpp" +#include "lib/bool-checkable.hpp" + + +namespace proc { +namespace engine { + + namespace error = lumiera::error; + using error::LUMIERA_ERROR_LIFECYCLE; + + typedef size_t HashVal; ////////////TICKET #722 + + class BuffHandle; + class BufferProvider; + class BufferDescriptor; + + + + 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 + }; + + + +}} // namespace proc::engine +#endif diff --git a/src/proc/engine/diagnostic-buffer-provider.cpp b/src/proc/engine/diagnostic-buffer-provider.cpp new file mode 100644 index 000000000..e681cdc0c --- /dev/null +++ b/src/proc/engine/diagnostic-buffer-provider.cpp @@ -0,0 +1,108 @@ +/* + 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 "lib/meta/function.hpp" + +#include "proc/engine/diagnostic-buffer-provider.hpp" +#include "proc/engine/tracking-heap-block-provider.hpp" + + +namespace proc { +namespace engine { + + + /** Storage for the diagnostics frontend */ + lib::Singleton<DiagnosticBufferProvider> DiagnosticBufferProvider::diagnostics; + + + + + DiagnosticBufferProvider::DiagnosticBufferProvider() + : pImpl_() + { } + + DiagnosticBufferProvider::~DiagnosticBufferProvider() { } + + + BufferProvider& + DiagnosticBufferProvider::build() + { + return diagnostics().reset(); + } + + + DiagnosticBufferProvider& + DiagnosticBufferProvider::access (BufferProvider const& provider) + { + if (!diagnostics().isCurrent (provider)) + throw error::Invalid("given Provider doesn't match (current) diagnostic data record." + "This might be an lifecycle error. Did you build() this instance beforehand?"); + + return diagnostics(); + } + + + + + TrackingHeapBlockProvider& + DiagnosticBufferProvider::reset() + { + pImpl_.reset(new TrackingHeapBlockProvider()); + return *pImpl_; + } + + bool + DiagnosticBufferProvider::isCurrent (BufferProvider const& implInstance) + { + return &implInstance == pImpl_.get(); + } + + + + + + /* === diagnostic API === */ + + bool + DiagnosticBufferProvider::buffer_was_used (uint bufferID) const + { + return pImpl_->access_emitted(bufferID).was_used(); + } + + + bool + DiagnosticBufferProvider::buffer_was_closed (uint bufferID) const + { + return pImpl_->access_emitted(bufferID).was_closed(); + } + + + void* + DiagnosticBufferProvider::accessMemory (uint bufferID) const + { + return pImpl_->access_emitted(bufferID).accessMemory(); + } + + +}} // namespace engine diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp new file mode 100644 index 000000000..9d1f3f303 --- /dev/null +++ b/src/proc/engine/diagnostic-buffer-provider.hpp @@ -0,0 +1,111 @@ +/* + 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 + ** A facility for writing unit-tests targeting the BufferProvider interface. + ** + ** @see buffer-provider-protocol-test.cpp + */ + +#ifndef PROC_ENGINE_DIAGNOSTIC_BUFFR_PROVIDER_H +#define PROC_ENGINE_DIAGNOSTIC_BUFFR_PROVIDER_H + + +#include "lib/error.hpp" +#include "lib/singleton.hpp" +#include "lib/util.hpp" +#include "proc/engine/type-handler.hpp" +#include "proc/engine/buffer-provider.hpp" + +#include <boost/scoped_ptr.hpp> +#include <boost/noncopyable.hpp> + + +namespace proc { +namespace engine { + + namespace error = lumiera::error; + + + /** + * simple BufferProvider implementation + * with additional allocation tracking + */ + class TrackingHeapBlockProvider; + + + /******************************************************************** + * Helper for unit tests: Buffer provider reference implementation. + * + * @todo write type comment + */ + class DiagnosticBufferProvider + : boost::noncopyable + { + + boost::scoped_ptr<TrackingHeapBlockProvider> pImpl_; + static lib::Singleton<DiagnosticBufferProvider> diagnostics; + + + TrackingHeapBlockProvider& reset(); + bool isCurrent (BufferProvider const&); + + + DiagnosticBufferProvider(); + ~DiagnosticBufferProvider(); + + friend class lib::singleton::StaticCreate<DiagnosticBufferProvider>; + + public: + /** build a new Diagnostic Buffer Provider instance, + * discard the existing one. Use the static query API + * for investigating collected data. */ + static BufferProvider& build(); + + + /** access the diagnostic API of the buffer provider + * @throw error::Invalid if the given provider doesn't allow + * for diagnostic access or wasn't registered beforehand. + */ + static DiagnosticBufferProvider& + access (BufferProvider const& ); + + + + + /* === diagnostic API === */ + + bool buffer_was_used (uint bufferID) const; + bool buffer_was_closed (uint bufferID) const; + void* accessMemory (uint bufferID) const; + bool all_buffers_released() const; + + + + private: + + }; + + + +}} // namespace proc::engine +#endif diff --git a/src/proc/engine/trafo.cpp b/src/proc/engine/dispatch-table.cpp similarity index 73% rename from src/proc/engine/trafo.cpp rename to src/proc/engine/dispatch-table.cpp index 636e59e4e..7b5a5bb82 100644 --- a/src/proc/engine/trafo.cpp +++ b/src/proc/engine/dispatch-table.cpp @@ -1,8 +1,8 @@ /* - Trafo - transforming processing Node + DispatchTable - implementation of frame job creation Copyright (C) Lumiera.org - 2008, Hermann Vosseler <Ichthyostega@web.de> + 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 @@ -21,13 +21,18 @@ * *****************************************************/ -#include "proc/engine/trafo.hpp" +#include "proc/engine/dispatch-table.hpp" +//#include "lib/frameid.hpp" +//#include "proc/state.hpp" -namespace engine - { + +namespace proc { +namespace engine { + /** */ - - - -} // 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 <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_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 new file mode 100644 index 000000000..37ded0273 --- /dev/null +++ b/src/proc/engine/dispatcher.cpp @@ -0,0 +1,74 @@ +/* + Dispatcher - translating calculation streams into frame jobs + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/engine/dispatcher.hpp" +//#include "lib/frameid.hpp" +//#include "proc/state.hpp" + + + +namespace proc { +namespace engine { + + Dispatcher::~Dispatcher() { } // emit VTables and Typeinfo here.... + FrameSequencer::~FrameSequencer() { } + + + /** */ + Dispatcher::JobBuilder + Dispatcher::onCalcStream (ModelPort modelPort, uint channel) + { + UNIMPLEMENTED ("build coordinates of frame to render"); + } + + + /** */ + FrameCoord + 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<void(TimeAnchor)> delayedAction) + { + UNIMPLEMENTED ("wrap already planned jobs, appending a continuation to pick up later"); + return *this; + } + + + + + +}} // namespace engine diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp new file mode 100644 index 000000000..d329039d3 --- /dev/null +++ b/src/proc/engine/dispatcher.hpp @@ -0,0 +1,117 @@ +/* + DISPATCHER.hpp - translating calculation streams into frame jobs + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef PROC_ENGINE_DISPATCHER_H +#define PROC_ENGINE_DISPATCHER_H + +#include "proc/common.hpp" +//#include "proc/state.hpp" +#include "proc/mobject/model-port.hpp" +#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 <boost/noncopyable.hpp> +#include <tr1/functional> + + +namespace proc { +namespace engine { + + using std::tr1::function; + using mobject::ModelPort; + using lib::time::TimeSpan; + using lib::time::FSecs; + using lib::time::Time; +// +// class ExitNode; + + /** + * 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 + : public FrameLocator + { + struct JobBuilder + { + Dispatcher& dispatcher_; + TimeAnchor refPoint_; + ModelPort modelPort_; + uint channel_; + + /////TODO need storage for the continuation + + FrameCoord relativeFrameLocation (TimeAnchor refPoint, uint frameCountOffset =0); + + JobBuilder& establishNextJobs (TimeAnchor refPoint); + + JobBuilder& prepareContinuation (function<void(TimeAnchor)> delayedAction); + + operator JobPlanningSequence() + { + TODO ("build the continuation job if necessary, wrap the sequence"); + + return JobPlanningSequence( + relativeFrameLocation(refPoint_), dispatcher_); + } + + }; + + public: + virtual ~Dispatcher(); ///< this is an interface + + JobBuilder onCalcStream (ModelPort modelPort, uint channel); + + + protected: + virtual FrameCoord locateRelative (FrameCoord, 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; + }; + + + +}} // namespace proc::engine +#endif diff --git a/src/proc/engine/engine-diagnostics.hpp b/src/proc/engine/engine-diagnostics.hpp new file mode 100644 index 000000000..be4e50859 --- /dev/null +++ b/src/proc/engine/engine-diagnostics.hpp @@ -0,0 +1,113 @@ +/* + ENGINE-DIAGNOSTICS.hpp - diagnostic facility to investigate engine operation + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file engine-diagnostics.hpp + ** An facility to check and monitor engine operations. + ** Once created, an EngineDiagnostics object connects to the EngineService + ** interface to activate additional tracing facilities within the engine, + ** allowing to watch and verify the creation of individual jobs and further + ** engine state parameters. + ** + ** @see EngineInterface + ** @see engine-interface-test.cpp + ** @see calc-stream-test.cpp + */ + + +#ifndef PROC_ENGINE_ENGINE_DIAGNOSTICS_H +#define PROC_ENGINE_ENGINE_DIAGNOSTICS_H + + +#include "lib/error.hpp" +#include "proc/engine/engine-service.hpp" +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "proc/engine/calc-stream.hpp" +//#include "proc/mobject/model-port.hpp" +#include "proc/play/timings.hpp" +//#include "proc/play/output-slot.hpp" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +//#include "lib/polymorphic-value.hpp" +//#include "lib/singleton.hpp" +// +#include <boost/noncopyable.hpp> +//#include <boost/scoped_ptr.hpp> +//#include <string> + + +namespace proc { +namespace engine{ + +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// using lumiera::DummyPlayer; + using proc::play::Timings; + + + + + + /****************************************************** + * Render engine diagnostic facility. Creating an instance + * will activate additional tracing facilities within the + * render engine; results may be investigated through + * EngineDiagnostics public functions. The object acts + * like a smart handle, i.e. the tracing facilities will + * be disabled and disconnected when going out of scope. + */ + class EngineDiagnostics + : boost::noncopyable + { + EngineService& engine_; + + public: + EngineDiagnostics (EngineService& e) + : engine_(e) + { + UNIMPLEMENTED ("attach tracing connector"); + engine_.activateTracing(); + } + + ~EngineDiagnostics() + { + TODO ("detach tracing connector"); + engine_.disableTracing(); + } + + /** */ + bool + has_scheduled_jobs_for (Timings const& timings) + { + UNIMPLEMENTED ("Engine Diagnostics: query scheduled jobs"); + } + }; + + + + + + +} // namespace engine +} // namespace proc +#endif diff --git a/src/proc/engine/engine-service-mock.cpp b/src/proc/engine/engine-service-mock.cpp new file mode 100644 index 000000000..5cb7e8ed2 --- /dev/null +++ b/src/proc/engine/engine-service-mock.cpp @@ -0,0 +1,77 @@ +/* + EngineServiceMock - dummy render engine implementation for test/development + + 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 "proc/engine/engine-service-mock.hpp" +#include "proc/engine/worker/dummy-tick.hpp" + +//#include <string> +//#include <memory> +//#include <tr1/functional> +//#include <boost/scoped_ptr.hpp> + + + +namespace proc { +namespace engine{ + +// using std::string; +// using lumiera::Subsys; +// using std::auto_ptr; +// using boost::scoped_ptr; +// using std::tr1::bind; + + + namespace { // hidden local details of the service implementation.... + + } // (End) hidden service impl details + + + + + + + /** + * Initialise a mock render engine. + * This dummy implementation manages a collection of + * "Processors", each running in a separate thread. + */ + EngineServiceMock::EngineServiceMock() + : processors_() + { } + + + + /** special engine configuration for mock/testing operation. + */ + RenderEnvironmentClosure& + EngineServiceMock::configureCalculation () + { + UNIMPLEMENTED ("represent *this as RenderEnvironmentClosure)"); + RenderEnvironmentClosure* todo_fake(0); ////KABOOOM + + return *todo_fake; + } + + + +}} // 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..16990cab9 --- /dev/null +++ b/src/proc/engine/engine-service-mock.hpp @@ -0,0 +1,105 @@ +/* + ENGINE-SERVICE-MOCK.hpp - dummy render engine implementation for test/development + + 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 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/scoped-ptrvect.hpp" +// +#include <boost/noncopyable.hpp> +//#include <boost/scoped_ptr.hpp> +//#include <string> + + +namespace proc { +namespace node { class DummyTick; } + +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; + + + + + + /**************************************************************** + * 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 + { + lib::ScopedPtrVect<node::DummyTick> processors_; + + + public: + + EngineServiceMock(); + + + protected: + virtual RenderEnvironmentClosure& configureCalculation (); + }; + + + + +}} // namespace proc::engine +#endif diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp new file mode 100644 index 000000000..bffada11e --- /dev/null +++ b/src/proc/engine/engine-service.cpp @@ -0,0 +1,179 @@ +/* + EngineService - primary service access point for using the renderengine + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/engine/engine-service.hpp" + +//#include <string> +//#include <memory> +//#include <tr1/functional> +//#include <boost/scoped_ptr.hpp> + + + +namespace proc { +namespace engine{ + +// using std::string; +// using lumiera::Subsys; +// using std::auto_ptr; +// using boost::scoped_ptr; +// using std::tr1::bind; + + + namespace { // hidden local details of the service implementation.... + + } // (End) hidden service impl details + + + + /** storage for the EngineService interface object */ + lib::Singleton<EngineService> EngineService::instance; + + + + /** */ + EngineService::EngineService() + { } + + + + /** 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 list of standard calculation streams"); + } + + + + /** */ + CalcStreams + EngineService::calculateBackground(ModelPort mPort, + Timings nominalTimings, + Quality serviceQuality) + { + UNIMPLEMENTED ("build calculation streams for background rendering"); + } + + + /** @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 + */ + RenderEnvironmentClosure& + EngineService::configureCalculation () + { + UNIMPLEMENTED ("represent *this as RenderEnvironmentClosure)"); + RenderEnvironmentClosure* todo_fake(0); ////KABOOOM + + return *todo_fake; + } + + + + + /* ===== Quality-of-Service ===== */ + + + EngineService::Quality::~Quality() { } // emit vtables here... + + enum CalcType { + PLAYBACK, + RENDER, + BACKGROUND + }; + + + class DefaultQoS + : public EngineService::Quality + { + CalcType type_; + + public: + DefaultQoS (CalcType type) + : type_(type) + { } + }; + + class PriorityQoS + : public DefaultQoS + { + + public: + PriorityQoS () + : DefaultQoS(PLAYBACK) + { } + }; + + class Compromise + : public DefaultQoS + { + + public: + Compromise (CalcType type) + : DefaultQoS(type) + { } + }; + + + EngineService::QoS_Definition EngineService::QoS_DEFAULT = QoS_Definition::build<DefaultQoS> (PLAYBACK); + EngineService::QoS_Definition EngineService::QoS_BACKGROUND = QoS_Definition::build<DefaultQoS> (BACKGROUND); + EngineService::QoS_Definition EngineService::QoS_COMPROMISE = QoS_Definition::build<Compromise> (PLAYBACK); + EngineService::QoS_Definition EngineService::QoS_PERFECT_RESULT = QoS_Definition::build<DefaultQoS> (RENDER); + EngineService::QoS_Definition EngineService::QoS_SYNC_PRIORITY = QoS_Definition::build<PriorityQoS>(); + + + +}} // namespace proc::engine diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp new file mode 100644 index 000000000..98d775419 --- /dev/null +++ b/src/proc/engine/engine-service.hpp @@ -0,0 +1,173 @@ +/* + ENGINE-SERVICE.hpp - primary service access point for using the renderengine + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler <Ichthyostega@web.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file engine-service.hpp + ** Access point for the (core) calculation service of the render engine. + ** This Proc-Layer internal service is provided for use by the Player subsystem. + ** The actual implementation is forwarded to backend services (especially the scheduler). + ** The EngineService singleton has no state beyond the jobs currently managed by the + ** scheduler; when the latter isn't available, any invocation will throw. + ** + ** The central concept provided through this facade interface is the <i>calculation stream</i>. + ** This represents a series of calculations, expected to happen in a timely fashion and in order + ** to deliver a frame data stream onto an opened output connection. On the implementation side, + ** a calculation stream will be translated into a series of jobs invoking render nodes, + ** to be executed through the scheduler in the backend layer. + ** + ** While the individual CalcStram is simple, linear and unmodifiable, any CalcStream may be + ** \em superseded by a new definition. In this case, the engine will care for a seamless + ** switch and continuation; under the hood, there is a mechanism to discard resources + ** tied to the original CalcStream, once the switch to the new definition is complete. + ** + ** @see EngineInterface_test + ** @see CalcStream_test + ** @see proc::play::PlayerService + */ + + +#ifndef PROC_ENGINE_ENGINE_SERVICE_H +#define PROC_ENGINE_ENGINE_SERVICE_H + + +#include "lib/error.hpp" +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +#include "proc/engine/calc-stream.hpp" +#include "proc/mobject/model-port.hpp" +#include "proc/play/timings.hpp" +#include "proc/play/output-slot.hpp" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +#include "lib/polymorphic-value.hpp" +#include "lib/singleton.hpp" +// +#include <boost/noncopyable.hpp> +//#include <boost/scoped_ptr.hpp> +//#include <string> + + +namespace proc { +namespace engine{ + +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// using lumiera::DummyPlayer; + using mobject::ModelPort; + using proc::play::Timings; + + typedef proc::play::OutputSlot::Allocation OutputConnection; + + + + + + /****************************************************** + * A service to schedule series of calculations, + * delivering the rendered data into an external output + * sink in a timely fashion. Actually the CalculationStream + * instances provided through this (facade) interface are + * backed by jobs executed through the scheduler in the + * backend layer. The implementation of this service is + * responsible for creating the right job entries in the + * correct order and to enqueue these into the scheduler. + */ + class EngineService + : boost::noncopyable + { + + + /* The following typedefs allow to hand out predefined + * Quality-of-Service strategy definitions as value objects, + * without disclosing implementation details here in this header. + */ + enum{ QoS_IMPL_SIZE = sizeof(size_t) }; /////////////////////////////////////////TODO is this correct?? + + + public: + /************************************************************* + * Quality-of-Service definition for an Render Engine usage. + * This strategy defines how to decide between conflicting goals like + * - timely delivery + * - image quality + * - niceness and resource usage + */ + class Quality + { + public: + virtual ~Quality(); ///< this is an Interface + }; + + + typedef lib::polyvalue::CloneValueSupport<Quality> _Clonable_QoS_Strategy; + typedef lib::PolymorphicValue<Quality, QoS_IMPL_SIZE, _Clonable_QoS_Strategy> QoS_Definition; + + static QoS_Definition QoS_DEFAULT; + static QoS_Definition QoS_BACKGROUND; + static QoS_Definition QoS_SYNC_PRIORITY; + static QoS_Definition QoS_PERFECT_RESULT; + static QoS_Definition QoS_COMPROMISE; + + + /** access point to the Engine Interface. + * @internal this is an facade interface for internal use + * by the player. Client code should use the Player. + */ + static lib::Singleton<EngineService> instance; + + + virtual ~EngineService() { } + + EngineService(); + + CalcStreams + calculate(ModelPort mPort, + Timings nominalTimings, + OutputConnection& output, + Quality serviceQuality =QoS_DEFAULT); + + CalcStreams + calculateBackground(ModelPort mPort, + Timings nominalTimings, + Quality serviceQuality =QoS_BACKGROUND); + + + private: + CalcStream activateCalculation (RenderEnvironmentClosure&); + + protected: + virtual RenderEnvironmentClosure& configureCalculation (); + + void activateTracing(); + void disableTracing(); ///< EX_FREE + + friend class EngineDiagnostics; + }; + + + + + + +} // namespace engine +} // namespace proc +#endif diff --git a/src/proc/engine/frame-coord.hpp b/src/proc/engine/frame-coord.hpp new file mode 100644 index 000000000..e90a2bc7b --- /dev/null +++ b/src/proc/engine/frame-coord.hpp @@ -0,0 +1,122 @@ +/* + FRAME-COORD.hpp - unique distinct coordinates of a frame to be calculated + + 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. + +*/ + + +#ifndef PROC_ENGINE_FRAME_COORD_H +#define PROC_ENGINE_FRAME_COORD_H + +#include "proc/common.hpp" +//#include "proc/state.hpp" +#include "proc/mobject/model-port.hpp" +#include "lib/time/timevalue.hpp" +#include "lib/time/timequant.hpp" + + + +namespace proc { +namespace engine { + + using mobject::ModelPort; +//using lib::time::TimeSpan; + using lib::time::Duration; +//using lib::time::FSecs; + using lib::time::TimeVar; +// +// 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//////////////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?? + * + * @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 + { + + public: + TimeVar absoluteNominalTime; + int64_t absoluteFrameNumber; + + ModelPort modelPort; + uint channelNr; + + + FrameCoord() + { + UNIMPLEMENTED ("anything regarding the Node Invocation"); + } + + // using default copy operations + + + Duration remainingRealTime() + { + UNIMPLEMENTED ("deterine the real wall clock time amount left until deadline"); + ////////////////////////TODO the coordinates can't answer that question! Who else can? + } + + + }; + + + + /** + * 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 new file mode 100644 index 000000000..c45bf1389 --- /dev/null +++ b/src/proc/engine/job-planning.hpp @@ -0,0 +1,374 @@ +/* + JOB-PLANNING.hpp - steps to prepare and build render jobs + + 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. + +*/ + + +#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/time-anchor.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 <boost/noncopyable.hpp> + + +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 + { + JobTicket::ExplorationState 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 + * using an inactive state core (default constructed) + */ + 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 for + * the job in question. + */ + JobPlanning (JobTicket::ExplorationState const& startingPoint, FrameCoord requestedFrame) + : plannedOperations_(startingPoint) + , point_to_calculate_(requestedFrame) + { } + + // using the standard copy operations + + + /** cast and explicate this job planning information + * to create a frame job descriptor, ready to be scheduled + */ + operator Job() + { + 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 (point_to_calculate_.channelNr) + ,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. + * 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_); + } + + + /* === 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_.pullNext(); + } + }; + + + + + + class PlanningState + : public lib::IterStateWrapper<JobPlanning> + { + typedef lib::IterStateWrapper<JobPlanning> _Iter; + + + public: + /** inactive evaluation */ + PlanningState() + : _Iter() + { } + + explicit + PlanningState (JobPlanning const& startingPoint) + : _Iter(startingPoint) // note: invoking copy ctor on state core + { } + + // 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 */ + PlanningState & + wrapping (JobPlanning const& startingPoint) + { + integrate (startingPoint, this->stateCore()); + return *this; + } + + PlanningState & + usingSequence (PlanningState const& prerequisites) + { + if (isnil (prerequisites)) + 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 an investigation + // (which is a JobPlanning) can stand-in for the sequence of prerequisites + } + + + + /** 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; + } + + + + + /** + * 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, + * 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 + { + 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; + typedef JobPlanning * pointer; + + + PlanningStepGenerator(FrameCoord startPoint, FrameLocator& locator) + : locationGenerator_(&locator) + , currentLocation_(startPoint) + { } + + // default copyable + + + /* === Iteration control API for IterStateWrapper== */ + + friend bool + checkPoint (PlanningStepGenerator const& gen) + { + UNIMPLEMENTED ("determine planing chunk size"); /// return bool(seq.feed()); + } + + friend JobPlanning& + yield (PlanningStepGenerator const& gen) + { + return unConst(gen).use_current_location_as_planning_start(); + } + + friend void + iterNext (PlanningStepGenerator & gen) + { + gen.currentLocation_ = gen.locationGenerator_->getNextFrame (gen.currentLocation_); + } + }; + + + + + typedef PlanningState (*SIG_expandPrerequisites) (JobPlanning const&); + + typedef lib::IterExplorer<PlanningStepGenerator + ,lib::iter_explorer::RecursiveSelfIntegration> JobPlanningChunkStartPoint; + + typedef JobPlanningChunkStartPoint::FlatMapped<SIG_expandPrerequisites>::Type ExpandedPlanningSequence; + + + + /** + * 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 + : public ExpandedPlanningSequence + { + + public: +// JobPlanningSequence() { } + + JobPlanningSequence(engine::FrameCoord startPoint, FrameLocator& locator) + : ExpandedPlanningSequence( + JobPlanningChunkStartPoint( + PlanningStepGenerator(startPoint,locator)) + >>= expandPrerequisites) + { } + + }; + + + +}} // namespace proc::engine +#endif diff --git a/src/proc/engine/job-ticket.cpp b/src/proc/engine/job-ticket.cpp new file mode 100644 index 000000000..317bccc18 --- /dev/null +++ b/src/proc/engine/job-ticket.cpp @@ -0,0 +1,101 @@ +/* + JobTicket - execution plan for render jobs + + 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 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: + + + }; + + + + /** */ + 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 new file mode 100644 index 000000000..45ff53b47 --- /dev/null +++ b/src/proc/engine/job-ticket.hpp @@ -0,0 +1,264 @@ +/* + JOB-TICKET.hpp - execution plan for render jobs + + 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. + +*/ + + +#ifndef PROC_ENGINE_JOB_TICKET_H +#define PROC_ENGINE_JOB_TICKET_H + +#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/linked-elements.hpp" +#include "lib/iter-adapter.hpp" +#include "lib/util.hpp" + +#include <boost/noncopyable.hpp> +#include <stack> + + +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; + + /** + * 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: + virtual ~JobClosure(); ///< this is an interface + + + virtual void invokeJobOperation (JobParameter parameter) =0; + virtual void signalFailure (JobParameter parameter) =0; + + virtual JobKind getJobKind() const =0; + virtual bool verify (Time nominalJobTime) const =0; + }; + + + + /** + * 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 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. + * 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). + * This way, the JobTicket acts as <i>higher order function:</i> a function + * generating on invocation another, specific function (= the job). + * + * @todo 1/12 WIP-WIP-WIP defining the invocation sequence and render jobs + */ + class JobTicket + : boost::noncopyable + { + struct Provision + { + Provision* next; + }; + + struct Prerequisite + { + Prerequisite* next; + JobTicket* descriptor; + }; + + struct Prerequisites ///< per channel + { + Prerequisites* next; + LinkedElements<Prerequisite> requiredJobs_; + }; + + + + LinkedElements<Provision> channelConfig_; + LinkedElements<Prerequisites> requirement_; + + + public: + class ExplorationState; + friend class ExplorationState; + + + JobTicket() + { + UNIMPLEMENTED ("job representation, planning and scheduling"); + } + + + ExplorationState startExploration() const; + ExplorationState discoverPrerequisites (uint channelNr) const; + + Job createJobFor (FrameCoord coordinates); + + + bool + isValid() const + { + if (channelConfig_.size() != requirement_.size()) + return false; + + UNIMPLEMENTED ("validity self check"); + } + }; + + + class JobTicket::ExplorationState + { + typedef LinkedElements<Prerequisite>::iterator SubTicketSeq; + typedef std::stack<SubTicketSeq> SubTicketStack; //////////////////////////TODO use a custom container to avoid heap allocations + + SubTicketStack toExplore_; + + public: + ExplorationState() { } + + ExplorationState (Prerequisites& prerequisites) + { + if (!isnil (prerequisites.requiredJobs_)) + toExplore_.push (prerequisites.requiredJobs_.begin()); + } + + // using default copy operations + + + bool + empty() const + { + return toExplore_.empty(); + } + + + void + pullNext() + { + 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) + { + if (subExploration.empty()) return; + + pushAllPrerequisites (subExploration.toExplore_); + } + + + JobTicket* + operator->() const + { + 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::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 + + /////////////////////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 + } + + + + inline JobTicket::ExplorationState + JobTicket::discoverPrerequisites (uint channelNr) const + { + REQUIRE (channelNr < requirement_.size()); + + return ExplorationState (requirement_[channelNr]); + } + + + + +}} // namespace proc::engine +#endif diff --git a/src/proc/engine/job.cpp b/src/proc/engine/job.cpp new file mode 100644 index 000000000..8db0a5c77 --- /dev/null +++ b/src/proc/engine/job.cpp @@ -0,0 +1,132 @@ +/* + Job - render job closure + + 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 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" + + +namespace proc { +namespace engine { + + namespace { // Details... + + inline JobClosure& + myClosure (const Job * const self) + { + ASSERT (self); + ASSERT (self->jobClosure); + return *static_cast<JobClosure*> (self->jobClosure); + } + + } // (END) Details... + + +// using mobject::Placement; +// using mobject::session::Effect; + + + /** @todo WIP-WIP 2/12 + */ + void + Job::triggerJob () const + { + myClosure(this).invokeJobOperation (parameter); + } + + + void + Job::signalFailure () const + { + UNIMPLEMENTED ("how to organise job failure and abortion"); + } + + + /** 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()); + return myClosure(this).getJobKind(); + } + + + /** 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 + +namespace { + using proc::engine::Job; + + inline Job& + forwardInvocation (lumiera_jobDefinition& jobDef) + { + Job& job = static_cast<Job&> (jobDef); + + REQUIRE (job.isValid()); + return job; + } +} + + + +extern "C" { /* ==== implementation C interface for job invocation ======= */ + +void +lumiera_job_invoke (lumiera_jobDefinition jobDef) +{ + forwardInvocation(jobDef).triggerJob(); +} + +void +lumiera_job_failure (lumiera_jobDefinition jobDef) +{ + forwardInvocation(jobDef).signalFailure(); +} +} diff --git a/src/proc/engine/job.hpp b/src/proc/engine/job.hpp new file mode 100644 index 000000000..e9cfddcbd --- /dev/null +++ b/src/proc/engine/job.hpp @@ -0,0 +1,216 @@ +/* + JOB.hpp - render job closure + + 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. + +*/ + + +#ifndef PROC_ENGINE_JOB_H +#define PROC_ENGINE_JOB_H + + +#include <gavl/gavl.h> + + +typedef void* LList; ////////////////////////////////////TODO +typedef uint64_t InvocationInstanceID; /////////////////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 + }; + +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. + * 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 + { + 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; +typedef lumiera_jobParameter* LumieraJobParameter; + + +/** complete definition of an individual job */ +struct lumiera_jobDefinition_struct + { + 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; + + +/** + * descriptor record used by the scheduler to organise job invocation. + * 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; + }; +typedef struct lumiera_jobDescriptor_struct lumiera_jobDescriptor; +typedef lumiera_jobDescriptor* LumieraJobDescriptor; + + + + + +#ifdef __cplusplus /* ============== C++ Interface ================= */ + +//#include "proc/common.hpp" +#include "lib/error.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::Duration; +//using lib::time::FSecs; +using lib::time::TimeValue; +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 + * 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 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 2/12 WIP-WIP-WIP defining the invocation sequence and render jobs + */ + class Job + : public lumiera_jobDefinition + { + + public: + + Job() + { + UNIMPLEMENTED ("job creation, planning and scheduling"); + } + + // using standard copy operations + + + void triggerJob() const; + void signalFailure() const; + + + Time + getNominalTime() const + { + return Time (TimeValue(parameter.nominalTime)); + } + + InvocationInstanceID + getInvocationInstanceID() const + { + return this->parameter.invoKey; + } + + JobKind getKind() const; + bool isValid() const; + }; + + +}} // namespace proc::engine + + + + +extern "C" { +#endif /* =========================== CL Interface ===================== */ + + +/** trigger execution of a specific job, + * assuming availability of all prerequisites */ +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 job invocations, even after + * missing deadlines or aborting a sequence of jobs */ +void lumiera_job_failure (lumiera_jobDefinition); + + + +#ifdef __cplusplus +} +#endif +#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 d2973550f..364f370a8 100644 --- a/src/proc/engine/mask.hpp +++ b/src/proc/engine/mask.hpp @@ -24,19 +24,20 @@ #ifndef ENGINE_MASK_H #define ENGINE_MASK_H -#include "proc/engine/trafo.hpp" +#include "proc/engine/procnode.hpp" +namespace proc { namespace engine { - class Mask : public Trafo + class Mask : public ProcNode {}; -} // 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 dd9b17ea4..18f1bb75e 100644 --- a/src/proc/engine/nodeinvocation.hpp +++ b/src/proc/engine/nodeinvocation.hpp @@ -55,12 +55,13 @@ #include "proc/state.hpp" #include "proc/engine/procnode.hpp" -#include "proc/engine/buffhandle.hpp" -#include "proc/engine/bufftable.hpp" +#include "proc/engine/channel-descriptor.hpp" +#include "proc/engine/bufftable-obsolete.hpp" +namespace proc { namespace engine { @@ -172,6 +173,7 @@ namespace engine { }; + ////////////TICKET #249 this strategy should better be hidden within the BuffHandle ctor (and type-erased after creation) struct AllocBufferFromParent ///< using the parent StateAdapter for buffer allocations : Invocation { @@ -179,7 +181,7 @@ namespace engine { : Invocation(sta, w, outCh) {} virtual BuffHandle - allocateBuffer (BufferDescriptor const& bd) { return parent_.allocateBuffer(bd); } ////////////TODO: actually implement the "allocate from parent" logic! + allocateBuffer (const lumiera::StreamType* ty) { return parent_.allocateBuffer(ty); } ////////////TODO: actually implement the "allocate from parent" logic! }; struct AllocBufferFromCache ///< using the global current State, which will delegate to Cache @@ -189,7 +191,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); } }; @@ -231,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 ce587862b..26031d989 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -59,8 +59,8 @@ #include "proc/state.hpp" #include "proc/engine/procnode.hpp" -#include "proc/engine/buffhandle.hpp" -#include "proc/engine/bufftable.hpp" +#include "proc/engine/channel-descriptor.hpp" +#include "proc/engine/bufftable-obsolete.hpp" #include "proc/engine/nodeinvocation.hpp" #include "lib/meta/util.hpp" @@ -70,6 +70,7 @@ +namespace proc { namespace engine { namespace config { @@ -82,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) @@ -105,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"); } }; @@ -245,7 +246,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) { @@ -276,12 +277,12 @@ 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> 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; }; @@ -289,7 +290,7 @@ namespace config { struct Strategy ; - template<char INPLACE_ign> + template<uint INPLACE_ign> struct Strategy< Config<CACHING,PROCESS,INPLACE_ign> > : QueryCache< AllocBufferTable< @@ -301,7 +302,7 @@ namespace config { OperationBase > > > > > > > { }; - template<char INPLACE_ign> + template<uint INPLACE_ign> struct Strategy< Config<PROCESS,INPLACE_ign> > : AllocBufferTable< PullInput< @@ -335,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 62ce24abc..aec901789 100644 --- a/src/proc/engine/nodewiring-config.hpp +++ b/src/proc/engine/nodewiring-config.hpp @@ -52,21 +52,24 @@ #define ENGINE_NODEWIRING_CONFIG_H -#include "lib/util.hpp" #include "lib/meta/configflags.hpp" +#include "lib/util.hpp" #include <tr1/functional> #include <bitset> #include <map> +namespace proc { 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 /** @@ -124,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 @@ -147,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; @@ -169,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]; @@ -181,5 +184,5 @@ namespace config { -}} // namespace engine::config +}}} // namespace proc::engine::config #endif diff --git a/src/proc/engine/nodewiring-def.hpp b/src/proc/engine/nodewiring-def.hpp index ef0173820..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 @@ -50,6 +50,7 @@ +namespace proc { namespace engine { using lib::RefArray; @@ -108,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) @@ -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 f1e989816..47b4acab9 100644 --- a/src/proc/engine/nodewiring.cpp +++ b/src/proc/engine/nodewiring.cpp @@ -26,19 +26,20 @@ #include "proc/engine/nodeoperation.hpp" #include "proc/engine/nodewiring-config.hpp" -#include "lib/meta/typelist-util.hpp" +#include "lib/meta/typelist-manip.hpp" +namespace proc { 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; @@ -116,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. */ @@ -149,4 +150,4 @@ namespace engine { } -} // namespace engine +}} // namespace proc::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 25dabab66..38d7152a3 100644 --- a/src/proc/engine/pluginadapter.hpp +++ b/src/proc/engine/pluginadapter.hpp @@ -24,10 +24,11 @@ #ifndef ENGINE_PLUGINADAPTER_H #define ENGINE_PLUGINADAPTER_H -#include "proc/engine/trafo.hpp" +#include "proc/engine/procnode.hpp" +namespace proc { namespace engine { @@ -37,12 +38,12 @@ 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 { ///////////// }; -} // namespace engine +}} // namespace proc::engine #endif diff --git a/src/proc/engine/procnode.hpp b/src/proc/engine/procnode.hpp index 757524914..5e0e4167b 100644 --- a/src/proc/engine/procnode.hpp +++ b/src/proc/engine/procnode.hpp @@ -40,19 +40,21 @@ #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" #include "proc/mobject/parameter.hpp" +#include "proc/engine/channel-descriptor.hpp" #include "lib/frameid.hpp" #include "lib/ref-array.hpp" #include <vector> +#include <boost/noncopyable.hpp> +namespace proc { namespace engine { using std::vector; @@ -66,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; @@ -96,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 @@ -104,8 +118,6 @@ namespace engine { virtual BuffHandle callDown (State& currentProcess, uint requiredOutputNr) const =0; - friend class ProcNode; - }; @@ -119,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<double> Param; //////TODO: just a placeholder for automation as of 6/2008 vector<Param> params; @@ -172,5 +185,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 459eb3f93..deb2b27f7 100644 --- a/src/proc/engine/render-invocation.hpp +++ b/src/proc/engine/render-invocation.hpp @@ -37,11 +37,12 @@ //#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" +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 c3030271f..abe036bef 100644 --- a/src/proc/engine/renderengine.hpp +++ b/src/proc/engine/renderengine.hpp @@ -31,7 +31,13 @@ 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 proc { namespace engine { @@ -54,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 718d3dfdb..63c039e0f 100644 --- a/src/proc/engine/rendergraph.hpp +++ b/src/proc/engine/rendergraph.hpp @@ -26,11 +26,21 @@ #include "proc/common.hpp" #include "proc/state.hpp" +#include "lib/time/timevalue.hpp" +/////////////////////////////TODO 7/11 this is a piece of debris, left over from the first attempt to complete the render nodes network. +/////////////////////////////TODO Meanwhile the intention is to treat the render nodes network more like a data structure, +/////////////////////////////TODO consequently this will become some kind of root or anchor point for this network + +namespace proc { namespace engine { + using lib::time::TimeSpan; + using lib::time::FSecs; + using lib::time::Time; + class ExitNode; /** @@ -41,15 +51,19 @@ 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"); + } }; -} // 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 cb32f3b57..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 { @@ -38,7 +39,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"); } @@ -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 7125790f7..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 { @@ -38,7 +39,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); @@ -56,5 +57,5 @@ namespace engine { -} // namespace engine +}} // 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..7d2b5cc63 --- /dev/null +++ b/src/proc/engine/time-anchor.hpp @@ -0,0 +1,144 @@ +/* + TIME-ANCHOR.hpp - current render evaluation time point closure + + 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. + +*/ + + +#ifndef PROC_ENGINE_TIME_ANCHOR_H +#define PROC_ENGINE_TIME_ANCHOR_H + +#include "proc/common.hpp" +//#include "proc/state.hpp" +#include "backend/real-clock.hpp" +#include "lib/time/timevalue.hpp" +#include "proc/play/timings.hpp" + +#include <boost/rational.hpp> + + + +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; + + /** + * 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 <i>evaluation closure</i>, + * 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 + * 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 + */ + class TimeAnchor + { + play::Timings timings_; + int64_t anchorPoint_; + Time relatedRealTime_; + + Time + expectedTimeofArival (play::Timings const& timings, Offset engineLatency) + { + 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.outputLatency; + } + + + TimeAnchor (play::Timings timings, Offset engineLatency, int64_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, int64_t startFrame) + { + const boost::rational<uint> DEFAULT_LATENCY_FACTOR (1,3); + Offset startDelay(timings.outputLatency + + timings.getFrameDurationAt(startFrame) * DEFAULT_LATENCY_FACTOR + ); + + return TimeAnchor (timings,startDelay,startFrame); + } + + //////////////////////////////////////////////////////////////////////////////////////////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"); + } + }; + + + +}} // namespace proc::engine +#endif 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..ed056bbce --- /dev/null +++ b/src/proc/engine/tracking-heap-block-provider.cpp @@ -0,0 +1,326 @@ +/* + 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/bool-checkable.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 <algorithm> +#include <vector> + +//using util::for_each; +using util::and_all; +using std::vector; +using lib::ScopedHolder; +using lib::ScopedPtrVect; + + + +namespace proc { +namespace engine { + + namespace error = lumiera::error; + + + namespace { // implementation helpers... + + using diagn::Block; + + /** helper to find Block entries + * based on their raw memory address */ + inline bool + identifyBlock (Block const& inQuestion, void* storage) + { + return storage == &inQuestion; + } + + /** 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* blockLocation) + { + typename VEC::iterator pos + = std::find_if (vec.begin(),vec.end() + ,search_for_block_using_this_storage(blockLocation)); + if (pos!=vec.end()) + return &(*pos); + else + return NULL; + } + } + + + + namespace diagn { + + typedef ScopedPtrVect<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 + : public lib::BoolCheckable<BlockPool> + { + uint maxAllocCount_; + size_t memBlockSize_; + PoolHolder blockList_; + + public: + BlockPool() + : maxAllocCount_(0) // unlimited by default + , 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() + { + 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() + { + return blockList_->manage (new Block(memBlockSize_)); + } + + + Block* + find (void* blockLocation) + { + return pick_Block_by_storage (*blockList_, blockLocation); + } + + + Block* + transferResponsibility (Block* allocatedBlock) + { + return blockList_->detach (allocatedBlock); + } + + + size_t + size() const + { + return blockList_->size(); + } + + bool + isValid() const + { + return blockList_; + } + + private: + bool + verify_all_children_idle() + { + 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(); + } + }; + + } + + + + namespace { // Details of allocation and accounting + + const uint MAX_BUFFERS = 50; + + diagn::Block emptyPlaceholder(0); + + } // (END) Details of allocation and accounting + + + + /** + * @internal create a memory tracking BufferProvider, + */ + TrackingHeapBlockProvider::TrackingHeapBlockProvider() + : BufferProvider ("Diagnostic_HeapAllocated") + , pool_(new diagn::PoolTable) + , outSeq_() + { } + + TrackingHeapBlockProvider::~TrackingHeapBlockProvider() + { + INFO (proc_mem, "discarding %zu diagnostic buffer entries", outSeq_.size()); + } + + + /* ==== Implementation of the BufferProvider interface ==== */ + + uint + TrackingHeapBlockProvider::prepareBuffers(uint requestedAmount, HashVal typeID) + { + diagn::BlockPool& responsiblePool = getBlockPoolFor (typeID); + return responsiblePool.prepare_for (requestedAmount); + } + + + BuffHandle + TrackingHeapBlockProvider::provideLockedBuffer(HashVal typeID) + { + diagn::BlockPool& blocks = getBlockPoolFor (typeID); + diagn::Block& newBlock = blocks.createBlock(); + return buildHandle (typeID, newBlock.accessMemory(), &newBlock); + } + + + void + TrackingHeapBlockProvider::mark_emitted (HashVal typeID, LocalKey const& implID) + { + 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); + outSeq_.manage (pool.transferResponsibility (block4buffer)); + } + + + /** mark a buffer as officially discarded */ + void + TrackingHeapBlockProvider::detachBuffer (HashVal typeID, LocalKey const& implID) + { + diagn::Block* block4buffer = locateBlock (typeID, implID); + REQUIRE (block4buffer, "releasing a buffer not allocated through this provider"); + block4buffer->markReleased(); + } + + + + /* ==== Implementation details ==== */ + + size_t + TrackingHeapBlockProvider::emittedCnt() const + { + return outSeq_.size(); + } + + diagn::Block& + TrackingHeapBlockProvider::access_emitted (uint bufferID) + { + if (!withinOutputSequence (bufferID)) + return emptyPlaceholder; ////////////////////////////////TICKET #856 + else + return outSeq_[bufferID]; + } + + bool + TrackingHeapBlockProvider::withinOutputSequence (uint bufferID) const + { + if (bufferID >= MAX_BUFFERS) + throw error::Fatal ("hardwired internal limit for test buffers exceeded"); + + return bufferID < outSeq_.size(); + } + + diagn::BlockPool& + TrackingHeapBlockProvider::getBlockPoolFor (HashVal typeID) + { + 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); + diagn::Block* block4buffer = pool.find (storage); ////////////////////////////////TICKET #856 + return block4buffer? block4buffer + : searchInOutSeqeuence (storage); + } + + diagn::Block* + TrackingHeapBlockProvider::searchInOutSeqeuence (void* blockLocation) + { + return pick_Block_by_storage (outSeq_, blockLocation); ////////////////////////////////TICKET #856 + } + + + + +}} // 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..a6db03431 --- /dev/null +++ b/src/proc/engine/tracking-heap-block-provider.hpp @@ -0,0 +1,188 @@ +/* + 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 <tr1/unordered_map> +#include <boost/scoped_ptr.hpp> +#include <boost/scoped_array.hpp> + + +namespace proc { +namespace engine { + + namespace error = lumiera::error; + + using lib::ScopedPtrVect; + + namespace diagn { + + using boost::scoped_ptr; + using boost::scoped_array; + + + /** + * Helper for implementing a diagnostic BufferProvider: + * A block of heap allocated storage, with the capability + * to store some additional tracking information. + */ + class Block + : boost::noncopyable + { + scoped_array<char> storage_; + + bool was_released_; + + public: + explicit + Block(size_t bufferSize) + : storage_(bufferSize? new char[bufferSize] : NULL) + , was_released_(false) + { } + + bool + was_used() const + { + return bool(storage_); + } + + bool + was_closed() const + { + return was_released_; + } + + void* + accessMemory() const + { + REQUIRE (storage_, "Block was never prepared for use"); + return storage_.get(); + } + + void + markReleased() + { + was_released_ = true; + } + }; + + 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. + * + * 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 + { + scoped_ptr<diagn::PoolTable> pool_; + ScopedPtrVect<diagn::Block> outSeq_; + + public: + /* === BufferProvider interface === */ + + 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(); + virtual ~TrackingHeapBlockProvider(); + + size_t emittedCnt() const; + + diagn::Block& access_emitted (uint bufferID); + + template<typename TY> + TY& accessAs (uint bufferID); + + private: + bool withinOutputSequence (uint bufferID) const; + diagn::BlockPool& getBlockPoolFor (HashVal typeID); + diagn::Block* locateBlock (HashVal typeID, void*); + diagn::Block* searchInOutSeqeuence (void* storage); + }; + + + + /** 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 (!withinOutputSequence (bufferID)) + throw error::Invalid ("Buffer with the given ID not yet emitted"); + + diagn::Block& memoryBlock = access_emitted (bufferID); + TY* converted = reinterpret_cast<TY*> (memoryBlock.accessMemory()); + + REQUIRE (converted); + return *converted; + } + + + + +}} // namespace proc::engine +#endif 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 diff --git a/src/proc/engine/type-handler.hpp b/src/proc/engine/type-handler.hpp new file mode 100644 index 000000000..c3835d683 --- /dev/null +++ b/src/proc/engine/type-handler.hpp @@ -0,0 +1,183 @@ +/* + 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 proc { +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 proc::engine +#endif diff --git a/src/proc/engine/worker/dummy-image-generator.cpp b/src/proc/engine/worker/dummy-image-generator.cpp new file mode 100644 index 000000000..577da2f94 --- /dev/null +++ b/src/proc/engine/worker/dummy-image-generator.cpp @@ -0,0 +1,168 @@ +/* + DummyImageGenerator - creating test output frames for simulated playback + + Copyright (C) Lumiera.org + 2009, Joel Holdsworth <joel@airwebreathe.org.uk>, + 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/worker/dummy-image-generator.hpp" + + +namespace proc { +namespace node { + + + + 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/play/dummy-image-generator.hpp b/src/proc/engine/worker/dummy-image-generator.hpp similarity index 60% rename from src/proc/play/dummy-image-generator.hpp rename to src/proc/engine/worker/dummy-image-generator.hpp index c893962b8..e364423ef 100644 --- a/src/proc/play/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/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 <joel@airwebreathe.org.uk>, + 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-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 <tr1/functional> +#include <limits> + + +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<void(void)> 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<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::node +#endif + diff --git a/src/proc/engine/worker/tick-service.hpp b/src/proc/engine/worker/tick-service.hpp new file mode 100644 index 000000000..1237727e3 --- /dev/null +++ b/src/proc/engine/worker/tick-service.hpp @@ -0,0 +1,126 @@ +/* + TICK-SERVICE.hpp - issuing timed callbacks + + Copyright (C) Lumiera.org + 2009, Joel Holdsworth <joel@airwebreathe.org.uk>, + 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 tick-service.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_PLAY_TICKSERVICE_H +#define PROC_PLAY_TICKSERVICE_H + + +#include "lib/error.hpp" +#include "backend/thread-wrapper.hpp" + +#include <tr1/functional> +#include <limits> + + +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 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::node +#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 6b44fbd36..e1339d05e 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> @@ -31,6 +32,7 @@ namespace proc { using std::string; using lumiera::Subsys; + using lumiera::Option; class BuilderSubsysDescriptor @@ -39,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; @@ -75,14 +77,14 @@ namespace proc { operator string () const { return "Session"; } bool - shouldStart (lumiera::Option&) + shouldStart (Option&) { - TODO ("determine, if an existing Session schould be loaded"); + 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; @@ -103,9 +105,55 @@ 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 (Option&) + { + TODO ("extract options about specific output systems to be brought up"); + return false; + } + + bool + start (Option&, Subsys::SigTerm termination) + { + this->completedSignal_ = termination; + return play::OutputDirector::instance().connectUp(); + } + + SigTerm completedSignal_; + + + void + triggerShutdown () throw() + { + play::OutputDirector::instance().triggerDisconnect (completedSignal_); + } + + + bool + checkRunningState () throw() + { + return play::OutputDirector::instance().isOperational(); + } + }; + namespace { lib::Singleton<BuilderSubsysDescriptor> theBuilderDescriptor; lib::Singleton<SessionSubsysDescriptor> theSessionDescriptor; + lib::Singleton<PlayOutSubsysDescriptor> thePlayOutDescriptor; } @@ -125,6 +173,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..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 { @@ -44,6 +49,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 @@ -55,11 +62,19 @@ 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 + * 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/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..2f9dc2078 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 @@ -93,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) , @@ -105,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 lib::visitor::Tool<void, InvokeCatchAllFunction> + { lumiera::WrapperPtr currentWrapper_; public: @@ -146,7 +146,7 @@ namespace mobject { } template<class TAR> - lumiera::P<TAR> + P<TAR> getPtr () { P<TAR>* pP = currentWrapper_.get<P<TAR>*>(); @@ -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> { }; @@ -206,5 +206,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 bf836d7d1..6c1065396 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 @@ -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..895a2fe53 100644 --- a/src/proc/mobject/builder/segmentation-tool.cpp +++ b/src/proc/mobject/builder/segmentation-tool.cpp @@ -24,18 +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"); } @@ -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..617445c18 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 { @@ -62,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<Pipe> PID; -// typedef ID<Struct> StID; public: @@ -83,5 +85,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 ac03f32a1..457fe8f31 100644 --- a/src/proc/mobject/mobject-ref.hpp +++ b/src/proc/mobject/mobject-ref.hpp @@ -59,9 +59,7 @@ #ifndef MOBJECT_MOBJECT_REF_H #define MOBJECT_MOBJECT_REF_H -//#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" @@ -71,8 +69,11 @@ ///////////////////////////////////////////TODO: define an C-API representation here, make the header multilingual! +namespace proc { namespace mobject { + namespace error = lumiera::error; + class MObject; @@ -107,8 +108,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-> (); @@ -118,8 +119,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_; @@ -148,7 +149,7 @@ namespace mobject { /** resolves the referred placement to an * ExplicitPlacement and returns the found start time */ - lumiera::Time const& + lib::time::Time getStartTime() { return pRef_.resolve().time; @@ -165,8 +166,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 ( @@ -390,5 +391,5 @@ namespace mobject { } -} // namespace mobject +}} // namespace proc::mobject #endif diff --git a/src/proc/mobject/mobject.cpp b/src/proc/mobject/mobject.cpp index f55c588aa..1a563391d 100644 --- a/src/proc/mobject/mobject.cpp +++ b/src/proc/mobject/mobject.cpp @@ -23,10 +23,13 @@ #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 proc { namespace mobject { using ::NOBUG_FLAG(memory); @@ -39,7 +42,7 @@ namespace mobject { MObject::MObject() - : length_() + : length_(Time::ZERO) , shortID_() { } @@ -58,4 +61,4 @@ namespace mobject { -} // namespace mobject +}} // namespace mobject diff --git a/src/proc/mobject/mobject.hpp b/src/proc/mobject/mobject.hpp index a8ea73313..c860c52a0 100644 --- a/src/proc/mobject/mobject.hpp +++ b/src/proc/mobject/mobject.hpp @@ -24,13 +24,12 @@ #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" #include "proc/asset.hpp" //TODO finally not needed? +#include "lib/p.hpp" +#include "lib/time/timevalue.hpp" #include <boost/noncopyable.hpp> #include <boost/operators.hpp> @@ -43,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); @@ -68,10 +68,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 +92,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 @@ -108,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 43aa6f5b8..ab5c3ca55 100644 --- a/src/proc/mobject/output-designation.hpp +++ b/src/proc/mobject/output-designation.hpp @@ -26,14 +26,17 @@ #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" } +namespace proc { namespace mobject { + namespace mp = lib::meta; + class MObject; template<class MX> @@ -46,7 +49,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,9 +57,11 @@ 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 + * + * @see OutputMapping_test */ class OutputDesignation { @@ -98,10 +103,12 @@ namespace mobject { }; private: - enum { - SPEC_SIZ = lumiera::typelist::maxSize< - lumiera::typelist::Types<PID,lumiera_uid,uint>::List>::value - }; + 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; @@ -112,5 +119,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..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<Pipe>. - ** 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. ** @@ -63,6 +63,7 @@ +namespace proc { namespace mobject { namespace { // Helper to extract and rebind definition types @@ -103,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 * @@ -400,5 +403,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 d5fb4fa59..bd859e52b 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" @@ -74,7 +73,11 @@ -namespace mobject { +namespace proc { + namespace error = lumiera::error; + + namespace mobject { + class MObject; @@ -210,7 +213,7 @@ namespace mobject { { return access(id_).isValid(); } - catch (lumiera::error::Invalid&) {} + catch (error::Invalid&) {} return false; } @@ -263,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! @@ -285,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 5cd8206dc..641f9746f 100644 --- a/src/proc/mobject/placement.hpp +++ b/src/proc/mobject/placement.hpp @@ -67,8 +67,9 @@ #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" #include "proc/asset/pipe.hpp" //////////////TICKET #109 : get rid of this @@ -76,6 +77,7 @@ #include <tr1/memory> +namespace proc { namespace mobject { namespace session{ class MObjectFactory; } @@ -123,7 +125,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 @@ -282,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 53abf0466..7c6b9ef70 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. @@ -54,7 +57,7 @@ namespace session { public: - Time& + Duration& getLength() { return length_; @@ -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..eb8ef4bac 100644 --- a/src/proc/mobject/session/bus-mo.hpp +++ b/src/proc/mobject/session/bus-mo.hpp @@ -21,17 +21,18 @@ */ -#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; - typedef lumiera::P<Pipe> PPipe; + typedef P<Pipe> PPipe; } @@ -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 af388fd9b..bb5fdeb74 100644 --- a/src/proc/mobject/session/clip.cpp +++ b/src/proc/mobject/session/clip.cpp @@ -25,7 +25,13 @@ #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 proc { namespace mobject { namespace session { @@ -33,9 +39,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 +55,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 +63,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())); } @@ -78,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 6a6251103..c6e2a3ba9 100644 --- a/src/proc/mobject/session/clip.hpp +++ b/src/proc/mobject/session/clip.hpp @@ -21,12 +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; @@ -36,6 +38,8 @@ namespace mobject { namespace session { using asset::Media; + using lib::time::TimeVar; + typedef P<Media> PMedia; typedef P<asset::Clip> PClipAsset; @@ -67,7 +71,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 @@ -98,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.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..f37c624be 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,12 @@ +namespace proc { namespace mobject { namespace session { - using lumiera::P; + using lib::P; using boost::scoped_ptr; namespace impl { class DefsRegistry; } @@ -124,5 +125,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..d72c92014 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; @@ -235,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(); @@ -340,5 +340,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..0913a56ba 100644 --- a/src/proc/mobject/session/fixture.hpp +++ b/src/proc/mobject/session/fixture.hpp @@ -39,15 +39,21 @@ using std::tr1::shared_ptr; +namespace proc { namespace mobject { 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<ExplicitPlacement> content_; boost::scoped_ptr<Segmentation> partitioning_; @@ -74,5 +80,5 @@ namespace session { -}} // namespace mobject::session +}}} // namespace proc::mobject::session #endif diff --git a/src/proc/mobject/session/compoundclip.cpp b/src/proc/mobject/session/generator-mo.cpp similarity index 56% rename from src/proc/mobject/session/compoundclip.cpp rename to src/proc/mobject/session/generator-mo.cpp index 6de47a052..a57ff41c9 100644 --- a/src/proc/mobject/session/compoundclip.cpp +++ b/src/proc/mobject/session/generator-mo.cpp @@ -1,8 +1,8 @@ /* - CompoundClip - compound of several clips (multichannel) + 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,18 +21,26 @@ * *****************************************************/ -#include "proc/mobject/session/compoundclip.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" -namespace mobject - { +//using lib::time::Mutation; +//using util::isnil; - namespace session - { - - /** */ +namespace proc { +namespace mobject { +namespace session { + /** new clip-MO linked with the given asset::Clip. + * Initially, this clip will cover the whole source media length. + */ + + + + +}}} // namespace mobject::session - - } // namespace mobject::session - -} // namespace mobject diff --git a/src/proc/mobject/session/generator-mo.hpp b/src/proc/mobject/session/generator-mo.hpp new file mode 100644 index 000000000..c0c64a4f9 --- /dev/null +++ b/src/proc/mobject/session/generator-mo.hpp @@ -0,0 +1,92 @@ +/* + GENERATOR-MO.hpp - a (Test)data generator + + 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_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; +} + +namespace mobject { +namespace session { + + using asset::Media; + using lib::time::TimeVar; + + typedef P<Media> PMedia; + typedef P<asset::Clip> PClipAsset; + + + /** + * A lksjaf + */ + class GeneratorMO + : public AbstractMO + { + string + initShortID() const + { + return buildShortID("Generator"); + } + + 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. + */ + + + GeneratorMO (); + friend class MObjectFactory; + + + public: + bool isValid() const; + + + DEFINE_PROCESSABLE_BY (builder::BuilderTool); + + }; + + typedef Placement<Clip> PClipMO; + + + +}}} // 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 a3c286af8..7955ada11 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 @@ -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 { @@ -64,7 +65,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 @@ -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 1c98c3df1..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 { @@ -189,7 +190,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))); @@ -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 04feae332..52b0f2add 100644 --- a/src/proc/mobject/session/locatingpin.hpp +++ b/src/proc/mobject/session/locatingpin.hpp @@ -43,11 +43,12 @@ -#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" +#include "lib/time/timevalue.hpp" #include <utility> #include <tr1/memory> @@ -56,6 +57,7 @@ using boost::scoped_ptr; +namespace proc { namespace asset { class Pipe; } namespace mobject { @@ -96,7 +98,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 +123,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 +149,8 @@ protected: */ struct LocatingSolution { - Time minTime; - Time maxTime; + TimeVar minTime; + TimeVar maxTime; Track minTrack; // TODO don't use Tracks Track maxTrack; bool impo; @@ -184,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 ea366b6ac..4eda27cf0 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" @@ -34,6 +33,7 @@ #include <stack> +namespace proc { namespace mobject { namespace session { @@ -397,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 6e4ab424d..b451e7c7e 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" @@ -64,6 +63,7 @@ #include <tr1/functional> +namespace proc { namespace mobject { namespace session { @@ -114,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..8b12ece8f 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 { @@ -89,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"); @@ -574,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) { } @@ -728,4 +727,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 51dde737d..f6a416628 100644 --- a/src/proc/mobject/session/placement-index.hpp +++ b/src/proc/mobject/session/placement-index.hpp @@ -102,12 +102,12 @@ -#ifndef MOBJECT_PLACEMENT_INDEX_H -#define MOBJECT_PLACEMENT_INDEX_H +#ifndef PROC_MOBJECT_PLACEMENT_INDEX_H +#define PROC_MOBJECT_PLACEMENT_INDEX_H -//#include "pre.hpp" #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" @@ -118,6 +118,7 @@ #include <vector> +namespace proc { namespace mobject { class MObject; @@ -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 == */ @@ -327,5 +329,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..43a290549 100644 --- a/src/proc/mobject/session/query-resolver.hpp +++ b/src/proc/mobject/session/query-resolver.hpp @@ -21,14 +21,15 @@ */ -#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" #include "lib/bool-checkable.hpp" #include "lib/typed-counter.hpp" #include "lib/iter-adapter.hpp" +#include "lib/nocopy.hpp" #include "lib/util.hpp" #include <boost/noncopyable.hpp> @@ -41,24 +42,17 @@ //using std::vector; //using std::string; +namespace proc { namespace mobject { namespace session { + using lib::IxID; using util::unConst; using boost::noncopyable; using boost::scoped_ptr; 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; @@ -75,7 +69,7 @@ namespace session { * Query ABC */ class Goal - : no_copy_by_client + : util::no_copy_by_client { public: virtual ~Goal() ; @@ -88,7 +82,7 @@ namespace session { struct QueryID { Kind kind; - size_t type; + IxID type; }; QueryID const& @@ -158,7 +152,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(); @@ -237,7 +231,7 @@ namespace session { friend bool - hasNext (PReso const&, Result const& pos) ////TICKET #410 + checkPoint (PReso const&, Result const& pos) { return bool(pos); } @@ -335,5 +329,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 46b2a6d4b..09eb45bfe 100644 --- a/src/proc/mobject/session/relativelocation.hpp +++ b/src/proc/mobject/session/relativelocation.hpp @@ -30,9 +30,12 @@ +namespace proc { namespace mobject { namespace session { + using lib::time::Offset; + /** * @todo just a design sketch, nothing finished yet. @@ -56,14 +59,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? @@ -77,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..f48054802 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 { @@ -41,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)"); @@ -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_); } @@ -340,4 +340,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..9ef7e8a0d 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 3a0835c87..593c81d94 100644 --- a/src/proc/mobject/session/segment.hpp +++ b/src/proc/mobject/session/segment.hpp @@ -28,41 +28,41 @@ #include "proc/common.hpp" #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 lumiera::Time Time; - - /** begin of this timeline segment. */ - Time start; - /** duration (span) of this timeline segment. */ - Time length; - - /** 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. + * + * @todo 1/2012 Just a Placeholder. The real thing is not yet implemented. + * @see http://lumiera.org/wiki/renderengine.html#Fixture + */ + 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?? + + /////////////////////////////////////////////////TODO: placeholder code + }; + + + +}}} // 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..08dac8c93 100644 --- a/src/proc/mobject/session/segmentation.hpp +++ b/src/proc/mobject/session/segmentation.hpp @@ -32,29 +32,32 @@ 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 + * + * @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<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 d0e4c4cc4..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 { @@ -67,7 +68,7 @@ namespace session { SessionImplAPI* SessManagerImpl::operator-> () throw() { - if (!pImpl_) + if (!pSess_) try { // create empty default configured session this->reset(); @@ -80,7 +81,7 @@ namespace session { } - return pImpl_.get(); + return pSess_.get(); } @@ -201,8 +202,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 } @@ -210,7 +211,7 @@ namespace session { SessManagerImpl::~SessManagerImpl () { - TODO ("verify sane lifecycle"); + //////////////////////////////////////// TICKET #845 verify sane session manager lifecycle here Session::initFlag = false; } @@ -219,7 +220,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 +230,7 @@ namespace session { SessManagerImpl::clear () { Lock sync(this); - pImpl_->clear(); + pSess_->clear(); } @@ -242,7 +243,7 @@ namespace session { { Lock sync(this); lifecycle_->shutDown(); - pImpl_.reset(); + pSess_.reset(); } @@ -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 14af631c6..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 { @@ -42,7 +43,7 @@ namespace session { : public SessManager , public lib::Sync<> { - scoped_ptr<SessionImplAPI> pImpl_; + scoped_ptr<SessionImplAPI> pSess_; scoped_ptr<LifecycleAdvisor> lifecycle_; SessManagerImpl() throw(); @@ -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..d1a73760e 100644 --- a/src/proc/mobject/session/session-impl.cpp +++ b/src/proc/mobject/session/session-impl.cpp @@ -28,8 +28,8 @@ #include "proc/mobject/mobject.hpp" #include "lib/error.hpp" -using namespace lumiera; +namespace proc { namespace mobject { namespace session { @@ -149,4 +149,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..623c0aee8 100644 --- a/src/proc/mobject/session/session-service-defaults.hpp +++ b/src/proc/mobject/session/session-service-defaults.hpp @@ -41,12 +41,13 @@ +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 @@ -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..004668778 100644 --- a/src/proc/mobject/session/session-service-fetch.hpp +++ b/src/proc/mobject/session/session-service-fetch.hpp @@ -44,12 +44,13 @@ +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. @@ -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..9545d7bae 100644 --- a/src/proc/mobject/session/session-services.hpp +++ b/src/proc/mobject/session/session-services.hpp @@ -82,11 +82,12 @@ +namespace proc { namespace mobject { namespace session { - using lumiera::typelist::InstantiateChained; - using lumiera::typelist::Types; + using lib::meta::InstantiateChained; + using lib::meta::Types; /** @@ -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..583f7f3e9 100644 --- a/src/proc/mobject/session/session.cpp +++ b/src/proc/mobject/session/session.cpp @@ -40,14 +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 @@ -138,4 +141,4 @@ namespace mobject { -} // namespace mobject +}} // namespace proc::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 73e811b90..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 { @@ -32,7 +33,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(); @@ -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 60fb8afa5..f11bf99a6 100644 --- a/src/proc/mobject/session/track.hpp +++ b/src/proc/mobject/session/track.hpp @@ -26,20 +26,23 @@ #include "proc/mobject/session/meta.hpp" #include "proc/asset/entry-id.hpp" +#include "lib/time/timevalue.hpp" +namespace proc { namespace mobject { namespace session { //////////////////////////////////////////////////////TICKET #637 - - using lumiera::P; - - 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; @@ -49,9 +52,12 @@ namespace asset { //////////////////////////////////////////////////////TI namespace mobject { namespace session { - using asset::TrackID; - + 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 +79,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_; @@ -104,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 14542b629..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 { @@ -90,7 +91,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_); } }; @@ -162,5 +163,5 @@ namespace test { -}} // namespace mobject::test +}}} // namespace proc::mobject::test #endif diff --git a/src/proc/play/dummy-image-generator.cpp b/src/proc/play/dummy-image-generator.cpp deleted file mode 100644 index 44cf97cd1..000000000 --- a/src/proc/play/dummy-image-generator.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - DummyImageGenerator - creating test output frames for simulated playback - - Copyright (C) Lumiera.org - 2009, Joel Holdsworth <joel@airwebreathe.org.uk>, - 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-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 proc diff --git a/src/proc/mobject/session/simpleclip.cpp b/src/proc/play/dummy-play-connection.cpp similarity index 56% rename from src/proc/mobject/session/simpleclip.cpp rename to src/proc/play/dummy-play-connection.cpp index e404532bf..065cd0b40 100644 --- a/src/proc/mobject/session/simpleclip.cpp +++ b/src/proc/play/dummy-play-connection.cpp @@ -1,8 +1,8 @@ /* - SimpleClip - Elementary clip (single media stream only) + DummyPlayConnection.hpp - simplified test setup for playback 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,18 +21,30 @@ * *****************************************************/ -#include "proc/mobject/session/simpleclip.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" -namespace mobject - { +//#include <string> +//#include <memory> +//#include <tr1/functional> +//#include <boost/scoped_ptr.hpp> - namespace session - { + + +namespace proc { +namespace play { + + + namespace { // hidden local details of the service implementation.... - /** */ - - - - } // namespace mobject::session - -} // namespace mobject + } // (End) hidden service impl details + + + + + /** */ + + +}} // namespace proc::play diff --git a/src/proc/play/dummy-play-connection.hpp b/src/proc/play/dummy-play-connection.hpp new file mode 100644 index 000000000..37681ce0f --- /dev/null +++ b/src/proc/play/dummy-play-connection.hpp @@ -0,0 +1,277 @@ +/* + 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 + ** 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 <i>policy based design</i> 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 <b>core interface</b> 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 <b>test support interface</b> 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 + */ + + +#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 "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 <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 lib::time::Duration; + + typedef lib::IterSource<mobject::ModelPort>::iterator ModelPorts; + + + struct PlayTestFrames_Strategy + { + + }; + + + 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<Pipe> PID; + typedef asset::ID<Struct> 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<Timeline> ("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<ModelPort> 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, + * 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 DEF> + class DummyPlayConnection + : boost::noncopyable + { + SimulatedBuilderContext mockBuilder_; + + public: + + ModelPorts + provide_testModelPorts() + { + return mockBuilder_.getAllModelPorts(); + } + + 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"); + } + }; + + + + +}} // namespace proc::play +#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/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..d5f942b3e 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,15 +64,15 @@ namespace proc { using lumiera::Display; using lumiera::DummyPlayer; + using proc::node::DummyImageGenerator; + using proc::node::TickService; - 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 + * 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/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.hpp b/src/proc/play/output-manager.hpp new file mode 100644 index 000000000..696350e1a --- /dev/null +++ b/src/proc/play/output-manager.hpp @@ -0,0 +1,82 @@ +/* + OUTPUT-MANAGER.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-manager.hpp + ** A global service to handle all external output connections. + ** + ** @see output-manager-test.cpp ////TODO + */ + + +#ifndef PROC_PLAY_OUTPUT_MANAGER_H +#define PROC_PLAY_OUTPUT_MANAGER_H + + +#include "lib/error.hpp" +#include "proc/play/output-slot.hpp" +#include "proc/mobject/model-port.hpp" + +#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; + + + LUMIERA_ERROR_DECLARE(CANT_PLAY); ///< unable to build playback or render process for this configuration + + + + + + + + + /****************************************************** + * Management of external Output connections. + * + * @todo need a concrete implementation + * @todo need a unit-test + * @todo write Type comment + */ + class OutputManager + : boost::noncopyable + { + public: + virtual ~OutputManager() { } + + + virtual OutputSlot& getOutputFor (mobject::ModelPort port) =0; + }; + + typedef shared_ptr<OutputManager> POutputManager; + + +}} // namespace proc::play +#endif diff --git a/src/proc/play/output-slot-connection.hpp b/src/proc/play/output-slot-connection.hpp new file mode 100644 index 000000000..ed61ef864 --- /dev/null +++ b/src/proc/play/output-slot-connection.hpp @@ -0,0 +1,239 @@ +/* + 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/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/sync.hpp" + +#include <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> +//#include <string> +#include <tr1/functional> +#include <vector> +//#include <tr1/memory> + + +namespace proc { +namespace play { + + using proc::engine::BuffHandle; +//using proc::engine::BufferProvider; +//using lib::time::Time; +//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; + + + + + /** @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. + * @note typically the concrete connection is noncopyable + */ + class OutputSlot::Connection + { + public: + virtual ~Connection(); + + 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; + }; + + + + /** + * 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 + { + public: + virtual ~ConnectionState() { } + + virtual Connection& access (uint) const =0; + }; + + + /** + * 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 + { + typedef lib::ScopedCollection<CON> Connections; + typedef OutputSlot::OpenedSinks OpenedSinks; + + Connections connections_; + + + /* == Allocation Interface == */ + + OpenedSinks + getOpenedSinks() + { + //////////////////////////TICKET #878 not re-entrant, lifecycle isn't clear + REQUIRE (this->isActive()); + return transform (eachElm(connections_), connectOutputSink); + } + + Timings + getTimingConstraints() + { + UNIMPLEMENTED ("find out about timing constraints"); //////////////////////////TICKET #831 + } + + bool + isActive() const + { + return 0 < connections_.size(); + } + + CON& + access (uint chanNr) const + { + return connections_[chanNr]; + } + + + protected: /* == API for OutputSlot-Impl == */ + 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); + } + + typedef typename Connections::ElementHolder& ConnectionStorage; + + /** factory function to build the actual + * connection handling objects per channel */ + virtual void buildConnection(ConnectionStorage) =0; + + + ConnectionStateManager(uint numChannels) + : connections_(numChannels) + { } + + public: + virtual + ~ConnectionStateManager() + { } + + + + private: // Implementation details + + static DataSink + connectOutputSink (CON& connection) + { + TRACE (test, "activating Con=%p", &connection ); + + DataSink newSink; + newSink.activate(&connection, shutdownConnection); + return newSink; + } + + static void + shutdownConnection (OutputSlot::Connection* toClose) + { + REQUIRE (toClose); + toClose->shutDown(); + } + }; + + + + +}} // namespace proc::play +#endif diff --git a/src/proc/play/output-slot.cpp b/src/proc/play/output-slot.cpp new file mode 100644 index 000000000..87eaf5051 --- /dev/null +++ b/src/proc/play/output-slot.cpp @@ -0,0 +1,119 @@ +/* + 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 "lib/error.hpp" +#include "proc/play/output-slot.hpp" +#include "proc/play/output-slot-connection.hpp" + +#include <boost/noncopyable.hpp> +#include <vector> + + +namespace proc { +namespace play { + + using std::vector; + + namespace error = lumiera::error; + + + + + OutputSlot::~OutputSlot() { } // emit VTables here.... + + OutputSlot::Allocation::~Allocation() { } + + OutputSlot::Connection::~Connection() { } + + + + + + /** whether this output slot is occupied + * @return true if currently unconnected and + * able to connect and handle output data + */ + bool + OutputSlot::isFree() const + { + return ! this->state_; + } + + + /** 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."); + + state_.reset (this->buildState()); + return *state_; + } + + + void + OutputSlot::disconnect() + { + 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); + } + + + + +}} // 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..3b34a1fa1 --- /dev/null +++ b/src/proc/play/output-slot.hpp @@ -0,0 +1,177 @@ +/* + 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 + ** + ** 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 + */ + + +#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 "proc/engine/buffer-provider.hpp" +#include "proc/play/timings.hpp" +#include "lib/iter-source.hpp" + +#include <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> + + +namespace proc { +namespace play { + + using proc::engine::BuffHandle; + using proc::engine::BufferProvider; + using lib::time::TimeValue; + using lib::time::Time; + + using boost::scoped_ptr; + + + + class DataSink; + + typedef int64_t FrameID; + + + + /******************************************************************************** + * Interface: Generic output sink. + * 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 + { + + protected: + + /** active connections through this OutputSlot */ + class ConnectionState; + + scoped_ptr<ConnectionState> state_; + + /** build the \em connected state, + * based on the existing configuration + * within this concrete OutputSlot */ + virtual ConnectionState* buildState() =0; + + + public: + virtual ~OutputSlot(); + + typedef lib::IterSource<DataSink>::iterator OpenedSinks; + + class Allocation + { + public: + virtual OpenedSinks getOpenedSinks() =0; + virtual bool isActive() const =0; + + virtual Timings getTimingConstraints() =0; + + protected: + ~Allocation(); ///< never to be managed by clients directly + }; + + /** established output channel */ + class Connection; + + + /** can this OutputSlot be allocated? */ + bool isFree() const; + + /** claim this slot for exclusive use */ + Allocation& allocate(); + + /** disconnect from this OutputSlot + * @warning may block until DataSinks are gone */ + void disconnect(); + + private: + + }; + + + + class DataSink + : public lib::Handle<OutputSlot::Connection> + { + + public: + BuffHandle lockBufferFor(FrameID); + void emit(FrameID, BuffHandle const&, TimeValue currentTime = Time::ANYTIME); + }; + + + + + +}} // namespace proc::play +#endif diff --git a/src/proc/play/play-controller.cpp b/src/proc/play/play-controller.cpp new file mode 100644 index 000000000..5eb14b9f2 --- /dev/null +++ b/src/proc/play/play-controller.cpp @@ -0,0 +1,229 @@ +/* + 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; + using proc::play::PlayProcess; + + + + /* ==== 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"); + } + + + /** @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_); + } + + + +} + + +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 new file mode 100644 index 000000000..03db62378 --- /dev/null +++ b/src/proc/play/play-controller.hpp @@ -0,0 +1,70 @@ +/* + PLAY-CONTROLLER.hpp - frontend handle 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.hpp + ** Functionality for driving and controlling an ongoing playback or render. + ** + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef PROC_PLAY_PLAY_CONTROLLER_H +#define PROC_PLAY_PLAY_CONTROLLER_H + + +#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> + + +namespace proc { +namespace play { + + 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.cpp b/src/proc/play/play-process.cpp new file mode 100644 index 000000000..159e111df --- /dev/null +++ b/src/proc/play/play-process.cpp @@ -0,0 +1,91 @@ +/* + PlayProcess - state frame for an ongoing play/render 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. + +* *****************************************************/ + + +#include "proc/play/play-process.hpp" +#include "proc/play/play-service.hpp" +#include "proc/play/render-configurator.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; + using lib::transform; + using lib::append_all; + + + namespace { // Implementation details... + + + } // (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); + } + + + + /** + * Factory: Initialise and configure a new PlayProcess. + * The caller gets to own and manage the returned process entry. + */ + PlayProcess* + 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(); + } + + + + + + /** */ + Feed::Feed (engine::CalcStreams const& newActiveRenderingConnections) + : renderStreams_(newActiveRenderingConnections) + { } + + + +}} // namespace proc::play diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp new file mode 100644 index 000000000..afdedb3c9 --- /dev/null +++ b/src/proc/play/play-process.hpp @@ -0,0 +1,146 @@ +/* + PLAY-PROCESS.hpp - state frame for an ongoing play/render 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-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 + */ + + +#ifndef PROC_PLAY_PLAY_PROCESS_H +#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" +//#include "lib/singleton-ref.hpp" +#include "proc/mobject/model-port.hpp" +#include "proc/engine/calc-stream.hpp" +#include "lib/iter-source.hpp" +#include "lib/util.hpp" +// +#include <boost/noncopyable.hpp> +//#include <boost/scoped_ptr.hpp> +#include <tr1/functional> +//#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; + using std::tr1::function; + + typedef lib::IterSource<ModelPort>::iterator ModelPorts; + + 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 + { + engine::CalcStreams renderStreams_; + + public: + + /** 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&); + }; + + typedef function<Feed(ModelPort)> FeedBuilder; + typedef std::vector<Feed> OutputFeeds; + + + /****************************************************** + * 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 PlayProcess + : boost::noncopyable + { + OutputFeeds outputFeeds_; + + PlayProcess (OutputFeeds feeds); + + public: + static PlayProcess* + initiate (ModelPorts dataGenerators, FeedBuilder); + }; + + + + +}} // namespace proc::play +#endif diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp new file mode 100644 index 000000000..580b0e6a4 --- /dev/null +++ b/src/proc/play/play-service.cpp @@ -0,0 +1,264 @@ +/* + PlayService - interface: render- and playback control + + 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/play-facade.h" +#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/memory> +//#include <boost/scoped_ptr.hpp> + + + +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> Play::facade; + + + +}//(End) namespace lumiera + + +namespace proc { +namespace play { + +//using std::string; +//using lumiera::Subsys; +//using std::auto_ptr; +//using boost::scoped_ptr; +//using std::tr1::bind; + using lib::Sync; + using lib::RecursiveLock_NoWait; + using std::tr1::weak_ptr; + using std::tr1::bind; +//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.... + + + + + + + + } // (End) hidden service impl details + + using lumiera::Play; + + typedef POutputManager Output; + + + + class ProcessTable + : public Sync<RecursiveLock_NoWait> + { + typedef weak_ptr<PlayProcess> Entry; + typedef std::vector<Entry> ProcTable; + + /** @note holding just weak references + * to any ongoing playback processes */ + ProcTable processes_; + + public: + + Play::Controller + establishProcess (PlayProcess* newProcess) + { + Play::Controller frontend; + try { + frontend.activate (newProcess, bind (&ProcessTable::endProcess, this, _1 )); + } + catch(...) + { + delete newProcess; + throw; + } + + Lock sync(this); + processes_.push_back (frontend); // keeping a weak-reference + return frontend; + } + + + bool + isActive() const + { + return ! and_all (processes_, isDead); + } + + private: + void + endProcess (PlayProcess* dyingProcess) + { + /////////////////////////////////////////////TICKET #867 : somehow ensure sane abort of all attached calculation efforts + delete dyingProcess; + + Lock sync(this); + remove_if (processes_, isDead); + } + + static bool + isDead (Entry const& e) + { + return e.expired(); + } + }; + + + + + PlayService::~PlayService() + { + 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()); + } + + + /** 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() + : facadeAccess_(*this, "Player") + , pTable_(new ProcessTable) + { } + + + + + + /** + * @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 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 + * calculated media data to the outputs. + */ + Play::Controller + PlayService::connect (ModelPorts dataGenerators, POutputManager outputPossibilities) + { + Timings playbackTimings (lib::time::FrameRate::PAL); //////////////////////////////////////////////////////TICKET #875 + + return pTable_->establishProcess( + PlayProcess::initiate(dataGenerators, + buildRenderConfiguration(outputPossibilities, playbackTimings))); + } + + + + /** */ + + + + LUMIERA_ERROR_DEFINE (CANT_PLAY, "unable to build playback or render process for this configuration"); + + + +}} // 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 Viewer 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 new file mode 100644 index 000000000..daa494122 --- /dev/null +++ b/src/proc/play/play-service.hpp @@ -0,0 +1,127 @@ +/* + PLAY-SERVICE.hpp - interface: render- and playback control + + 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-service.hpp + ** 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 + ** + */ + + +#ifndef PROC_PLAY_PLAY_SERVICE_H +#define PROC_PLAY_PLAY_SERVICE_H + + +#include "lib/error.hpp" +#include "include/play-facade.h" +#include "common/interface-facade-link.hpp" + +#include <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> +//#include <string> + + +namespace proc { +namespace play { + + using std::string; + using lumiera::facade::InterfaceFacadeLink; +//using lumiera::Subsys; +//using lumiera::Display; +//using lumiera::DummyPlayer; + using boost::scoped_ptr; + + +// class DummyImageGenerator; +// class TickService; + class ProcessTable; + class RenderConfigurator; + + + + + /****************************************************** + * 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 */ + virtual Controller connect(ModelPorts, POutputManager); + + + public: + PlayService(); /////TODO Subsys::SigTerm terminationHandle); + + ~PlayService(); /////TODO notifyTermination_(&error_); } + + }; + + + LUMIERA_ERROR_DECLARE (CANT_PLAY); ///< unable to build playback or render process for this configuration + + + + +}} // namespace proc::play +#endif diff --git a/src/proc/play/render-configurator.cpp b/src/proc/play/render-configurator.cpp new file mode 100644 index 000000000..0edb8b070 --- /dev/null +++ b/src/proc/play/render-configurator.cpp @@ -0,0 +1,166 @@ +/* + 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 "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/memory> +#include <tr1/functional> +//#include <boost/scoped_ptr.hpp> + + + +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::shared_ptr; + using std::tr1::bind; + using std::tr1::placeholders::_1; + using engine::EngineService; + + typedef EngineService::QoS_Definition RenderQuality; + + + namespace { // Implementation details... + +// using std::tr1::bind; +// using std::tr1::function; +// using std::tr1::placeholders::_1; +// using lib::transform; + + class LumieraRenderProcessBuilder + : public RenderConfigurator + { + + 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) + { + OutputSlot::Allocation& activeOutputConnection = output.allocate(); + Timings nominalTimings = activeOutputConnection.getTimingConstraints() + .constrainedBy(playbackTimings_); + + return activateEngine (port, nominalTimings, activeOutputConnection, renderQuality_); + } + + + engine::CalcStreams + activateEngine (ModelPort port, Timings timings, OutputSlot::Allocation& activeOutputConnection, RenderQuality quality) + { + return EngineService::instance().calculate (port, timings, activeOutputConnection, quality); + } + + + public: + LumieraRenderProcessBuilder (POutputManager outputManager, Timings playbackSpeed) + : outputResolver_(outputManager) + , playbackTimings_(playbackSpeed) + , renderQuality_(EngineService::QoS_DEFAULT) + { } + + }; + + + + /** @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) + { + return new LumieraRenderProcessBuilder (outputPossibilities, playTimings); + } + + + } // (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)); + } + + + + + /** @internal this builder function is used by the PlayService + * when it comes to creating a new PlayProcess. The generated ConnectFunction + * 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 + * 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 playTimings) + { + shared_ptr<RenderConfigurator> specialConfig (how_to_render (outputPossibilities,playTimings)); + + 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 new file mode 100644 index 000000000..4ba8cc665 --- /dev/null +++ b/src/proc/play/render-configurator.hpp @@ -0,0 +1,130 @@ +/* + 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/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" +// +#include <boost/noncopyable.hpp> +//#include <boost/scoped_ptr.hpp> +//#include <string> +#include <tr1/functional> +#include <vector> + + +namespace proc { +namespace play { + +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// using lumiera::DummyPlayer; +// using util::isnil; + using std::tr1::function; + 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 + : boost::noncopyable + { + + public: + virtual ~RenderConfigurator(); ///< this is an interface + + Feed buildActiveFeed (ModelPort); + + 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, + * 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; + + }; + + + /** Factory function to build a RenderConfigurator + * 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 + */ + RenderConfigurator::ConnectFunction + buildRenderConfiguration (POutputManager outputPossibilities, Timings playbackTimings); + + + + + +}} // namespace proc::play +#endif diff --git a/src/proc/engine/projector.cpp b/src/proc/play/sound/jack-output.cpp similarity index 75% rename from src/proc/engine/projector.cpp rename to src/proc/play/sound/jack-output.cpp index 3b75702ed..4fc576d0d 100644 --- a/src/proc/engine/projector.cpp +++ b/src/proc/play/sound/jack-output.cpp @@ -1,8 +1,8 @@ /* - Projector - video ProcNode for scaling and translating image data + JackOutput - Jack audio connection client 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,14 +21,11 @@ * *****************************************************/ -#include "proc/engine/projector.hpp" - -namespace engine - { - - /** */ - - - - -} // namespace engine +#include "proc/play/sound/jack-output.hpp" + +namespace proc { +namespace play { +namespace sound { + + +}}} // namespace proc::play::sound diff --git a/src/pre_a.hpp b/src/proc/play/sound/jack-output.hpp similarity index 51% rename from src/pre_a.hpp rename to src/proc/play/sound/jack-output.hpp index 153fbb01a..d793662d3 100644 --- a/src/pre_a.hpp +++ b/src/proc/play/sound/jack-output.hpp @@ -1,10 +1,8 @@ /* - PRE_A.hpp - precompiled header (including assets) - + JACK-OUTPUT.hpp - Jack audio connection client Copyright (C) Lumiera.org - 2008, Christian Thaeter <ct@pipapo.org> - 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 @@ -22,27 +20,34 @@ */ -/** @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. +/** @file jack-output.hpp ** - ** @see pre.hpp + ** @see lumiera::Display + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example */ -#ifndef LUMIERA_PRE_A_HPP -#define LUMIERA_PRE_A_HPP + +#ifndef PROC_PLAY_SOUND_JACK_OUTPUT_H +#define PROC_PLAY_SOUND_JACK_OUTPUT_H -#include <string> -#include <vector> -#include <map> -#include <tr1/memory> -#include <tr1/functional> -#include <boost/format.hpp> - -#include "proc/common.hpp" -#include "proc/asset.hpp" +//#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> -#endif /*LUMIERA_PRE_A_HPP*/ +namespace proc { +namespace play { +namespace sound { + + +}}} // namespace proc::play::sound +#endif diff --git a/src/proc/play/tick-service.hpp b/src/proc/play/tick-service.hpp deleted file mode 100644 index a67116020..000000000 --- a/src/proc/play/tick-service.hpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - TICK-SERVICE.hpp - issuing timed callbacks - - Copyright (C) Lumiera.org - 2009, Joel Holdsworth <joel@airwebreathe.org.uk>, - 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 tick-service.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 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. - ** - ** @see proc::play::DummyPlayerService - ** - */ - - -#ifndef PROC_PLAY_TICKSERVICE_H -#define PROC_PLAY_TICKSERVICE_H - - -#include "backend/thread-wrapper.hpp" - -#include <tr1/functional> -#include <limits> - - -namespace proc { - namespace play { - - 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..."); - } - - }; - - - - - } // namespace play - -} // namespace proc -#endif // PROC_PLAY_TICKSERVICE_H - diff --git a/src/proc/play/timings.cpp b/src/proc/play/timings.cpp new file mode 100644 index 000000000..6e57fe6ad --- /dev/null +++ b/src/proc/play/timings.cpp @@ -0,0 +1,139 @@ +/* + Timings - timing specifications for a frame quantised data stream + + 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 "proc/play/timings.hpp" +#include "lib/time/formats.hpp" +#include "lib/time/timequant.hpp" + + + +namespace proc { +namespace play { + + + using lib::time::PQuant; + using lib::time::Time; + + 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) + , outputLatency (Duration::NIL) + { + 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 getFrameDurationAt(frameNr); + } + + + Duration + Timings::getFrameDurationAt (int64_t refFrameNr) const + { + return Offset (grid_->timeOf(refFrameNr), grid_->timeOf(refFrameNr + 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 return Duration::NIL + * @todo implement real support for variable frame rates + */ ////////////////////////////////////////////////////////TICKET #236 + Duration + Timings::constantFrameTimingsInterval (TimeValue startPoint) const + { + return Duration (Time::ANYTIME); + } + + + Time + Timings::getTimeDue() const + { + if (TIMEBOUND == playbackUrgency) + { + UNIMPLEMENTED ("scheduled delivery spec"); + } + else + return Time::NEVER; + } + + + uint + Timings::getPlanningChunkSize() const + { + UNIMPLEMENTED ("how to control the engine evaluation chunk size"); + } + + + 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 new file mode 100644 index 000000000..e0e4a9c34 --- /dev/null +++ b/src/proc/play/timings.hpp @@ -0,0 +1,145 @@ +/* + 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 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. + ** 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 lib { +namespace time{ + class Quantiser; + typedef std::tr1::shared_ptr<const Quantiser> PQuant; +}} + +namespace proc { +namespace play { + + using lib::time::FrameRate; + using lib::time::TimeValue; + using lib::time::Duration; + using lib::time::Offset; + using lib::time::Time; +//using std::string; + +//using std::vector; +//using std::tr1::shared_ptr; +//using boost::scoped_ptr; + + enum PlaybackUrgency { + ASAP, + NICE, + TIMEBOUND + }; + + + + /***************************************************************************** + * Generic frame timing specification. Defines the expected delivery interval, + * optionally also the expected quality-of-service + * + * @note copyable value class + * + * @todo write type comment + * /////////////////////////////////////////////////////////////////TODO: WIP seems to become some kind of abstracted Grid representation! + */ + class Timings + { + lib::time::PQuant grid_; + + public: + PlaybackUrgency playbackUrgency; + Duration outputLatency; + + Timings (FrameRate fps); + + // default copy acceptable + + TimeValue getOrigin() const; + + 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 + * @return duration after this starting point, where it's safe + * 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; + + /** 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); + + }; + + + +}} // namespace proc::play +#endif 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 ca0a6a44c..e2e7414f4 100644 --- a/src/proc/state.hpp +++ b/src/proc/state.hpp @@ -25,15 +25,14 @@ #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" #include <cstddef> +namespace proc { namespace engine { using lumiera::FrameID; @@ -54,6 +53,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 +62,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; @@ -110,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 93% rename from src/lib/streamtype.hpp rename to src/proc/streamtype.hpp index 745f50d86..29dcffa5d 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_H +#define PROC_STREAMTYPE_H #include "lib/symbol.hpp" @@ -42,7 +42,7 @@ #include <boost/noncopyable.hpp> -namespace lumiera { +namespace proc { using lib::Symbol; @@ -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 { }; @@ -185,5 +190,5 @@ namespace lumiera { -} // namespace lumiera +} // namespace proc #endif 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/src/tool/SConscript b/src/tool/SConscript index 1cd45d0d6..2d326feea 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -3,25 +3,31 @@ ## 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']) envSvg.Append(LIBS=support_lib) +envSnd = env.Clone() +envSnd.mergeConf(['alsa']) -luidgen = env.Program('luidgen', 'luidgen.c', LIBS=support_lib, install=True) ## for generating Lumiera-UIDs + +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.... -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... - + luidgen - + rsvg - ] +tools = [ env.Program('hello-world','hello.c', install=True) #### hello world (checks C build) + + outputProbe #### for output connection tests + + luidgen + + rsvg + ] +Export('tools') # Rendering the SVG Icons depends on rsvg-convert -env.Depends(artifacts['icons'], rsvg) +env.Depends(icons, rsvg) diff --git a/src/tool/alsa.c b/src/tool/alsa.c new file mode 100644 index 000000000..d210693dd --- /dev/null +++ b/src/tool/alsa.c @@ -0,0 +1,169 @@ +/* + ALSA - sound output backend using the Advanced Linux Sound Architecture + + Copyright (C) Lumiera.org + 2011, Odin Omdal Hørthe <odin.omdal@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 "alsa.h" + +#include <alsa/asoundlib.h> + +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/proc/engine/projector.hpp b/src/tool/alsa.h similarity index 55% rename from src/proc/engine/projector.hpp rename to src/tool/alsa.h index b93552e4d..30fdb75f7 100644 --- a/src/proc/engine/projector.hpp +++ b/src/tool/alsa.h @@ -1,8 +1,8 @@ /* - PROJECTOR.hpp - video ProcNode for scaling and translating image data + ALSA.h - sound output backend using the Advanced Linux Sound Architecture Copyright (C) Lumiera.org - 2008, Hermann Vosseler <Ichthyostega@web.de> + 2011, Odin Omdal Hørthe <odin.omdal@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 @@ -20,27 +20,35 @@ */ +/** @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 ENGINE_PROJECTOR_H -#define ENGINE_PROJECTOR_H -#include "proc/engine/trafo.hpp" +#ifndef TOOL_ALSA_H +#define TOOL_ALSA_H + + +#include <stdlib.h> -namespace engine - { +size_t audio_offset(); - /** - * Special video processing node used to scale and translate image data. - */ - class Projector : public Trafo - { - ////////////TODO adapt ctor - }; +void audio_init(); +size_t audio_write(const void* data, size_t amount); -} // namespace engine -#endif + +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 new file mode 100644 index 000000000..e8a684705 --- /dev/null +++ b/src/tool/main.c @@ -0,0 +1,53 @@ +/* + OutputProbe - tool to investigate external output connections + + Copyright (C) Lumiera.org + 2011, Odin Omdal Hørthe <odin.omdal@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 "alsa.h" + +#include <unistd.h> +#include <stdint.h> +#include <stdio.h> + + +#define SAMPLE_RATE 44100 + +int16_t quiet[SAMPLE_RATE], noisy[SAMPLE_RATE]; + +void main () { + + for (int i=0; i<SAMPLE_RATE; i++) + { + quiet[i] = 0; + noisy[i] = i%30000; + } + audio_start(44100, 2); + + for (int i=0; i<10; i++) + { + audio_write(noisy, SAMPLE_RATE); + printf("=================================\n"); + audio_write(quiet, SAMPLE_RATE); + printf("\n"); + } + + audio_stop(); +} 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 <cworth@cworth.org> + Caleb Moore <c.moore@student.unsw.edu.au> + Dom Lachowicz <cinamod@hotmail.com> + 2008, Joel Holdsworth <joel@airwebreathe.org.uk> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 <cinamod@hotmail.com> - Copyright (C) 2005 Caleb Moore <c.moore@student.unsw.edu.au> - Copyright (C) 2008 Joel Holdsworth <joel@airwebreathe.org.uk> - - 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 <cworth@cworth.org>, - Caleb Moore <c.moore@student.unsw.edu.au>, - Dom Lachowicz <cinamod@hotmail.com>, - Joel Holdsworth <joel@airwebreathe.org.uk> -*/ #ifndef N_ #define N_(X) X @@ -40,183 +36,203 @@ #include <librsvg/rsvg.h> #include <librsvg/rsvg-cairo.h> + #ifdef CAIRO_HAS_PS_SURFACE -#include <cairo-ps.h> + #include <cairo-ps.h> #endif #ifdef CAIRO_HAS_PDF_SURFACE -#include <cairo-pdf.h> + #include <cairo-pdf.h> #endif #ifdef CAIRO_HAS_SVG_SURFACE -#include <cairo-svg.h> + #include <cairo-svg.h> #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_("<int>")}, - {"height", 'h', 0, G_OPTION_ARG_INT, &height, - N_("height [optional; defaults to the SVG's height]"), N_("<int>")}, - {"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_("<int>")}, + {"height", 'h', 0, G_OPTION_ARG_INT, &height, + N_("height [optional; defaults to the SVG's height]"), N_("<int>")}, + {"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; } - diff --git a/src/tool/try.cpp b/src/tool/try.cpp deleted file mode 100644 index bdfca7100..000000000 --- a/src/tool/try.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* try.cpp - for trying out some language features.... - * scons will create the binary bin/try - * - */ - -// 8/07 - how to control NOBUG?? -// execute with NOBUG_LOG='ttt:TRACE' bin/try -// 1/08 - working out a static initialisation problem for Visitor (Tag creation) -// 1/08 - check 64bit longs -// 4/08 - comparison operators on shared_ptr<Asset> -// 4/08 - conversions on the value_type used for boost::any -// 5/08 - how to guard a downcasting access, so it is compiled in only if the involved types are convertible -// 7/08 - combining partial specialisation and subclasses -// 10/8 - abusing the STL containers to hold noncopyable values -// 6/09 - investigating how to build a mixin template providing an operator bool() -// 12/9 - tracking down a strange "warning: type qualifiers ignored on function return type" -// 1/10 - can we determine at compile time the presence of a certain function (for duck-typing)? -// 4/10 - pretty printing STL containers with python enabled GDB? -// 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> - - -#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; -} - - -int -main (int, char**) //(int argc, char* argv[]) - { - - NOBUG_INIT; - - cout << "\n\nwho am I? :" << catchMyself(); - cout << "\n.gulp.\n"; - - return 0; - } 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/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/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/40components.tests b/tests/40components.tests index 98e5c290a..24a262f81 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 @@ -329,6 +329,22 @@ return: 0 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 + + TEST "functional closure" FunctionClosure_test <<END out: List1 :-<1>-<2>-<3>- out: List2 :-<5>-<6>-<7>- @@ -356,6 +372,11 @@ return: 0 END +TEST "HashGenerator_test" HashGenerator_test <<END +return: 0 +END + + TEST "HashIndexed_test" HashIndexed_test <<END return: 0 END @@ -384,6 +405,11 @@ return: 0 END +TEST "metaprogramming helpers" MetaUtils_test <<END +return: 0 +END + + TEST "NIL object singleton" NullValue_test <<END return: 0 END @@ -470,6 +496,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}$ @@ -481,6 +508,24 @@ out: values_4_key(::[0-9]{1,2})+ END +TEST "Iterator monad variations" IterExplorer_test <<END +out-lit: 1-2-3-4-5-6-7-8 +out-lit: 0-1-2-3-4-0-1-2-3-4-5-6-0-1-2-3-4-5-6-7-8 +out-lit: 0-1-2-3-4-5-6-7-8 +out-lit: 0-1-2-3-4-5-6-7-8 +END + + +TEST "Stack-like iterator" IterStack_test <<END +return: 0 +END + + +TEST "Queue-like iterator" IterQueue_test <<END +return: 0 +END + + TEST "Iterable type detection" IterableClassification_test <<END out: can_STL_ForEach<LongVector> : Yes out: can_STL_ForEach<TimeSet> : Yes @@ -505,7 +550,17 @@ out: can_IterForEach<CustomForwardIter> : Yes END -PLANNED "RefArray_test" RefArray_test <<END +TEST "Linked iterable elements" LinkedElements_test <<END +return: 0 +END + + +TEST "Polymorphic value objects" PolymorphicValue_test <<END +return: 0 +END + + +TEST "RefArray_test" RefArray_test <<END return: 0 END @@ -585,7 +640,12 @@ out: ^\.throw some exceptions... END -TEST "ScopedPtrVect_test" ScopedPtrVect_test <<END +TEST "Managed Collection (I)" ScopedCollection_test <<END +return: 0 +END + + +TEST "Managed Collection (II)" ScopedPtrVect_test <<END return: 0 END @@ -637,6 +697,16 @@ return: 0 END +TEST "Wrapper thread-local pointers" ThreadLocal_test <<END +return: 0 +END + + +TEST "Thread-local diagnostic context" DiagnosticContext_test <<END +return: 0 +END + + TEST "Wait/Notify on Object Monitor" SyncWaiting_test <<END return: 0 END @@ -678,6 +748,31 @@ return: 0 END +TEST "Changing time specifications" TimeMutation_test <<END +return: 0 +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 + + TEST "Quantiser API basics" QuantiserBasics_test <<END return: 0 END @@ -689,11 +784,18 @@ 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 +TEST "Parsing time values" TimeParsing_test <<END +END + + TEST "Symbol_test" Symbol_test <<END out: one out: sizeof.+lib.Literal.+ = (4|8) @@ -763,6 +865,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> > @@ -784,136 +891,136 @@ return: 0 END -TEST "typelist manipulation" TypeListManipl_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: \+---<·>-\+- +TEST "typelist manipulation" TypeListManip_test <<END +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 @@ -1082,7 +1189,28 @@ return: 0 END -TEST "TypedAllocationManager" TypedAllocationManager_test <<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 + + +TEST "custom allocator(I)" SimpleAllocator_test <<END +return: 0 +END + + +TEST "custom allocator(II)" TypedAllocationManager_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 85242cd65..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 @@ -96,16 +96,16 @@ 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{8,16}\) +out: pID\(\w{6,16}\) 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/45controller.tests b/tests/45controller.tests index 20deba21c..178e0fc7b 100644 --- a/tests/45controller.tests +++ b/tests/45controller.tests @@ -37,8 +37,8 @@ END TEST "build argument accepting function" ArgumentTupleAccept_test <<END out: sizeof\( .+control.+TestClass.+ \) = 1 -out: sizeof\( .+control.+TestClass.+lumiera.Time.+ \) = (12|16) -out: 0:..:..\.000 +out: sizeof\( .+control.+TestClass.+lib.time.TimeVar.+ \) = (12|16) +out: 0:..:..\..00 return: 0 END diff --git a/tests/46engine.tests b/tests/46engine.tests index e669a11cb..f5653aab3 100644 --- a/tests/46engine.tests +++ b/tests/46engine.tests @@ -2,11 +2,47 @@ TESTING "Component Test Suite: Render Engine parts" ./test-components --group=en -PLANNED "BuffTable_test" BuffTable_test <<END +TEST "Test support: dummy frames" TestFrame_test <<END return: 0 END +TEST "Test support: dummy buffer provider" TrackingHeapBlockProvider_test <<END +return: 0 +END + + +TEST "Buffer provider diagnostics" BufferProviderProtocol_test <<END +return: 0 +END + + +TEST "buffer metadata type keys" BufferMetadataKey_test <<END +return: 0 +END + + +TEST "buffer metadata and state transitions" BufferMetadata_test <<END +END + + +PLANNED "buffer table" BuffTable_test <<END +return: 0 +END + + +PLANNED "Engine Interface basics" EngineInterface_test <<END +END + + +PLANNED "Engine calculation streams" CalcStream_test <<END +END + + +PLANNED "Frame Dispatcher Interface" DispatcherInterface_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..ce31209a6 --- /dev/null +++ b/tests/47playout.tests @@ -0,0 +1,17 @@ +TESTING "Component Test Suite: Player and Output" ./test-components --group=player + + + +PLANNED "Dummy Player setup" DummyPlayConnection_test <<END +return: 0 +END + + +TEST "Output Slot Protocol" OutputSlotProtocol_test <<END +return: 0 +END + + +PLANNED "Timing constraints" TimingConstraints_test <<END +return: 0 +END 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/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/SConscript b/tests/SConscript index 6920e912c..9c5d5c786 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -12,12 +12,11 @@ from Buildhelper import scanSubtree 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 ? +Import('env core tools config') 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,12 +24,12 @@ 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] 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) @@ -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) @@ -49,32 +50,34 @@ def testCollection(env,dir): -# but have to treat some subdirs individually. +# have to treat some subdirs individually. specials = ['plugin','lib','components'] 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 - ) +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 @@ -104,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) @@ -113,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']) 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 ed3204a9b..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" @@ -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 (); 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 e1a72636d..fc64caabb 100644 --- a/tests/common/test-config.c +++ b/tests/common/test-config.c @@ -24,16 +24,21 @@ #include "common/config.h" #include "common/configitem.h" -#include "tests/test.h" +#include "lib/test/test.h" 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 (); } 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/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/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 \ diff --git a/tests/components/backend/media-access-mock-test.cpp b/tests/components/backend/media-access-mock-test.cpp new file mode 100644 index 000000000..d79525396 --- /dev/null +++ b/tests/components/backend/media-access-mock-test.cpp @@ -0,0 +1,92 @@ +/* + MediaAccessMock(Test) - checking our Test Mock replacing the MediaAccessFacade + + 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 "backend/media-access-facade.hpp" +#include "backend/media-access-mock.hpp" + +#include "lib/test/run.hpp" +#include "lib/time/diagnostics.hpp" +#include "lib/symbol.hpp" + +#include <iostream> + +using lib::test::Use4Test; +using lib::Literal; +using std::string; +using std::cout; + + +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 + { + typedef MediaAccessFacade MAF; + + virtual void run(Arg) + { + Use4Test<MediaAccessMock> within_this_scope; + + queryScenario ("test-1"); + queryScenario ("test-2"); + } + + + /** 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/backend/media-access-mock.cpp b/tests/components/backend/media-access-mock.cpp new file mode 100644 index 000000000..4a50542b9 --- /dev/null +++ b/tests/components/backend/media-access-mock.cpp @@ -0,0 +1,155 @@ +/* + MediaAccessMock - a test (stub) target object for testing the factories + + 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 mediaacessmock.cpp + ** Mock implementation of the Interface normally used to query media file + ** information from the data backend. The Mock implementation instead holds + ** a map of fixed response which will be delivered when querying some magic + ** filenames. + ** + ** @see mediaaccessmocktest.cpp validating the Mock + ** @see MediaAccessFactory the real thing + ** + */ + + +#include "backend/media-access-mock.hpp" +#include "proc/mobject/session/testclip.hpp" + +#include "lib/util.hpp" +#include "lib/singleton.hpp" +#include "lib/time/mutation.hpp" +#include "lib/symbol.hpp" + +#include <vector> +#include <map> + +using lumiera::error::Invalid; +using lib::time::Mutation; +using lib::time::Duration; +using lib::Literal; +using util::isnil; +using std::string; +using std::vector; +using std::map; + + +namespace backend { +namespace test { + + typedef MediaAccessFacade::ChanHandle ChanHandle; + + + namespace { // implementation details + + 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 proc::mobject::session::test::LENGTH_TestClip; + + + struct TestCases : map<string,Response> + { + TestCases () + { + // ----------------------------------------------------------------------TESTCASES + (*this)["test-1"].globalLength(LENGTH_TestClip).channel("video","ID"); + + (*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 + } + + bool known (string key) + { + const_iterator i = find (key); + return (i != end()); + } + }; + + // instantiate TestCasses table + lib::Singleton<TestCases> testCases; + + } // (end) implementation namespace + + + + MediaDesc& + MediaAccessMock::queryFile (string const& name) const + { + if (isnil (name)) + throw Invalid ("empty filename passed to MediaAccessFacade."); + + 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; + } + + + ChanDesc + MediaAccessMock::queryChannel (MediaDesc& h, uint chanNo) const + { + Response const& res (*reinterpret_cast<Response*> (&h)); + + if (res.channels.size() <= chanNo) + return NULLResponse; + else + return res.channels[chanNo]; + } + + +}} // namespace backend::test diff --git a/tests/components/backend/mediaaccessmock.hpp b/tests/components/backend/media-access-mock.hpp similarity index 68% rename from tests/components/backend/mediaaccessmock.hpp rename to tests/components/backend/media-access-mock.hpp index e48733bc1..3c2fdb354 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,16 +21,17 @@ */ -#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" +#include "lib/test/mock-injector.hpp" -namespace backend_interface { +namespace backend { namespace test { /** @@ -39,11 +40,13 @@ namespace test { */ class MediaAccessMock : public MediaAccessFacade { + MediaDesc& queryFile (string const& name) const; + ChanDesc queryChannel (MediaDesc&, uint chanNo) const; + public: - FileHandle queryFile (const char* name) throw(lumiera::error::Invalid); - ChanDesc queryChannel (FileHandle, uint chanNo) throw(); + typedef MediaAccessFacade ServiceInterface; }; -}} // namespace backend_interface::test +}} // namespace backend::test #endif diff --git a/tests/components/backend/mediaaccessmock.cpp b/tests/components/backend/mediaaccessmock.cpp deleted file mode 100644 index e15a2213c..000000000 --- a/tests/components/backend/mediaaccessmock.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - MediaAccessMock.hpp - a test (stub) target object for testing the factories - - 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 mediaacessmock.cpp - ** Mock implementation of the Interface normally used to query media file - ** informations from the data backend. The Mock implementation instead holds - ** a map of fixed response which will be delivered when querying some magic - ** filenames. - ** - ** @see mediaaccessmocktest.cpp validating the Mock - ** @see MediaAccessFactory the real thing - ** - */ - - -#include "backend/mediaaccessmock.hpp" - -#include "lib/util.hpp" -#include "lib/util-foreach.hpp" - -#include <iostream> -#include <vector> -#include <map> - -using lumiera::error::Invalid; -using util::for_each; -using util::isnil; -using std::cout; -using std::string; -using std::vector; -using std::map; - - -namespace backend_interface { -namespace test { - - typedef MediaAccessFacade::FileHandle FileHandle; - typedef MediaAccessFacade::ChanHandle ChanHandle; - - - namespace { // implementation details - - typedef vector<ChanDesc> Response; - const ChanDesc NULLResponse; - - struct TestCases : map<string,Response> - { - TestCases () - { - // ------------------------------------------------------------------TESTCASES - (*this)["test-1"].push_back (ChanDesc ("video","ID", genH())); - - (*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 - } - - bool known (string key) - { - const_iterator i = find (key); - return (i != end()); - } - private: - int _i_; - ChanHandle genH() - { - return reinterpret_cast<ChanHandle> (++_i_); - } - }; - - // instantiate TestCasses table - TestCases testCases; - - } // (end) implementation namespace - - - FileHandle - MediaAccessMock::queryFile (const char* name) throw(Invalid) - { - if (isnil (name)) - throw Invalid ("empty filename passed to MediaAccessFacade."); - - if (!testCases.known(name)) - return 0; - else - return reinterpret_cast<void*> (&testCases[name]); - } - - ChanDesc - MediaAccessMock::queryChannel (FileHandle h, uint chanNo) throw() - { - const Response* res (reinterpret_cast<Response*> (h)); - - if (!res || res->size() <= chanNo) - return NULLResponse; - else - return (*res)[chanNo]; - } - - -}} // namespace backend_interface::test diff --git a/tests/components/backend/mediaaccessmocktest.cpp b/tests/components/backend/mediaaccessmocktest.cpp deleted file mode 100644 index 60d770b89..000000000 --- a/tests/components/backend/mediaaccessmocktest.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - MediaAccessMock(Test) - checking our Test Mock replacing the MediaAccessFacade - - 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 "backend/mediaaccessfacade.hpp" -#include "backend/mediaaccessmock.hpp" - -#include "lib/test/run.hpp" -//#include "lib/util.hpp" - -//#include <boost/format.hpp> -#include <iostream> - -//using boost::format; -//using util::isnil; -using std::string; -using std::cout; - - -namespace backend_interface - { - 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 - { - 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"; - } } } - - - }; - - - - /** Register this test class... */ - LAUNCHER (MediaAccessMock_test, "unit operate"); - - - - } // namespace test - -} // namespace backend_interface 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 76788d3f5..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 @@ -87,5 +88,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..316c24fde 100644 --- a/tests/components/proc/asset/basicpipetest.cpp +++ b/tests/components/proc/asset/basicpipetest.cpp @@ -42,175 +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 lumiera::StreamType; - - - - /*********************************************************************** - * @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+")"); + 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); + } + + + + + 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 716155f7d..07b68d428 100644 --- a/tests/components/proc/asset/create-asset-test.cpp +++ b/tests/components/proc/asset/create-asset-test.cpp @@ -30,12 +30,15 @@ #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; -namespace asset { +namespace proc { +namespace asset{ namespace test { @@ -49,6 +52,8 @@ namespace test { { virtual void run(Arg arg) { + Use4Test<backend::test::MediaAccessMock> within_this_scope; + createMedia(); factoryVariants(); @@ -70,10 +75,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 +129,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 +147,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 +164,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) @@ -201,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 65783b20c..d50ab6d98 100644 --- a/tests/components/proc/asset/dependent-assets-test.cpp +++ b/tests/components/proc/asset/dependent-assets-test.cpp @@ -24,188 +24,192 @@ #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; +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 hopfully don't fail to cover any - * important special cases...) - */ - void checkRealAssetDependencyRegistration () - { - // -----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..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; @@ -46,9 +47,12 @@ using std::endl; +namespace proc { namespace asset{ namespace test { + using lumiera::error::LUMIERA_ERROR_WRONG_TYPE; + namespace { // Test definitions... struct Dummy { }; @@ -254,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; } }; @@ -280,4 +290,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 6f13cb7fc..27e70c69c 100644 --- a/tests/components/proc/asset/identityofassetstest.cpp +++ b/tests/components/proc/asset/identityofassetstest.cpp @@ -30,12 +30,15 @@ #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; -namespace asset { +namespace proc { +namespace asset{ namespace test { @@ -68,13 +71,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 +89,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"); @@ -101,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 4042053a7..609df4c9e 100644 --- a/tests/components/proc/asset/makecliptest.cpp +++ b/tests/components/proc/asset/makecliptest.cpp @@ -25,63 +25,67 @@ #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" #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; 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) - { - - PM mm = asset::Media::create("test-1", VIDEO); - PC cc = mm->createClip(); - PM cm = cc->getMedia(); - - CHECK (cm); - CHECK (Time(0) < 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 ae6f70d53..e90eb794a 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 { @@ -85,7 +86,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; @@ -102,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)); @@ -118,8 +119,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))); } }; @@ -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 cd4009e7f..62cdede05 100644 --- a/tests/components/proc/asset/orderingofassetstest.cpp +++ b/tests/components/proc/asset/orderingofassetstest.cpp @@ -29,100 +29,104 @@ #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; -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 - * boost::shared_ptr, which is not desirable. - * @see Asset::Ident#compare - */ - class OrderingOfAssets_test : public Test - { - virtual void run(Arg) - { - Asset::Ident key1("Au-1", Category(AUDIO), "ichthyo", 5); - PAsset mm1 = asset::Media::create(key1, "Name-1"); - - Asset::Ident key2("Au-1", Category(AUDIO), "ichthyo", 7); - PAsset mm2 = asset::Media::create(key2, "Name-2"); - - Asset::Ident key3("Au-2", Category(AUDIO), "ichthyo", 5); - PAsset mm3 = asset::Media::create(key3, "Name-3"); - - Asset::Ident key4("Au-2", Category(AUDIO), "stega", 5); - PAsset mm4 = asset::Media::create(key4, "Name-4"); - - Asset::Ident key5("Au-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 7dbcbe636..7de1006ab 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> @@ -38,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 asset +}}} // namespace proc::asset::test #endif diff --git a/tests/components/proc/asset/testclipasset.hpp b/tests/components/proc/asset/testclipasset.hpp index eb5167abc..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 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..23fb0af84 100644 --- a/tests/components/proc/asset/typed-id-test.cpp +++ b/tests/components/proc/asset/typed-id-test.cpp @@ -47,22 +47,23 @@ 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; +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 c264ca255..75d80c318 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> @@ -35,16 +35,17 @@ using std::cout; using std::endl; +namespace proc { namespace control { 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; + using lib::meta::FunctionSignature; + using lib::meta::Tuple; @@ -103,7 +104,7 @@ namespace test { run (Arg) { TestClass<void()> testVoid; - TestClass<int(Time,int)> testTime; + TestClass<int(TimeVar,int)> testTime; testVoid.bind(); testTime.bind(randTime(),23); @@ -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 8f5feb85f..60315ad83 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; @@ -49,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; - - - - - + using namespace lib::meta; + + + + + namespace { // test helpers - + ostringstream protocol; ///< used to verify the test function calls - - + + /** * watching the instance creation * of some parameter values @@ -74,108 +77,115 @@ 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<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; protocol << fmt % *time % *str % rand; } - + 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; } - - + + /// 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 } - - + + + int + twoRandomDigits() + { + return 10 + rand() % 90; + } + + } // test-helper implementation - - - - - - + + + + + + typedef lib::ScopedPtrVect<CmdClosure> ArgTuples; - + /*************************************************************************** * @test Check storage handling of the command parameters and state memento. * @@ -184,82 +194,82 @@ namespace test { */ class CommandArgument_test : public Test { - + virtual void run (Arg) { ArgTuples testTuples; - Tracker<Time>::instanceCnt = 0; + Tracker<TimeVar>::instanceCnt = 0; Tracker<string>::instanceCnt = 0; - + createTuples (testTuples); checkArgumentComparison (); serialiseArgTuples (testTuples); testTuples.clear(); - + 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; - - - - + + + + /** @test create various argument tuples and re-access their contents */ 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; - - + + 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, randTime())); - arg4->storeTuple (tuple::make (rand() % 10, randTime())); - - arg5->storeTuple (tuple::make (TTime (randTime()), Tstr("glorious"), rand() % 25)); - + 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 */ @@ -268,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); @@ -312,83 +322,83 @@ 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. */ 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; - + 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"), rand() % 100)); + 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(Time(123456)), Tstr("unbelievable"), rand() %100)); + 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 76ef584d2..1e6a3d20a 100644 --- a/tests/components/proc/control/command-basic-test.cpp +++ b/tests/components/proc/control/command-basic-test.cpp @@ -23,19 +23,22 @@ #include "lib/test/run.hpp" #include "proc/control/command-def.hpp" -#include "lib/lumitime.hpp" +#include "lib/time/timevalue.hpp" #include "lib/p.hpp" #include <cstdlib> -using lumiera::Time; using std::rand; +namespace proc { namespace control { namespace test { - using lumiera::P; + using lib::P; + using lib::time::Time; + using lib::time::TimeVar; + using lib::time::TimeValue; @@ -44,19 +47,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; } @@ -87,8 +90,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) @@ -101,13 +106,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); } }; @@ -116,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..18458e830 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; + using namespace lib::meta; - 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 715a3b83f..cf60a738e 100644 --- a/tests/components/proc/control/command-equality-test.cpp +++ b/tests/components/proc/control/command-equality-test.cpp @@ -36,12 +36,13 @@ #include <iostream> #include <string> +namespace proc { namespace control { namespace test { - using namespace lumiera::typelist; + using namespace lib::meta; using lib::Symbol; using util::isnil; using util::isSameObject; @@ -98,10 +99,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 +114,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 { @@ -315,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..6d82a684e 100644 --- a/tests/components/proc/control/command-mutation-test.cpp +++ b/tests/components/proc/control/command-mutation-test.cpp @@ -41,10 +41,11 @@ using std::cout; using std::endl; +namespace proc { namespace control { namespace test { - using namespace lumiera::typelist; + using namespace lib::meta; using control::CmdClosure; @@ -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..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" @@ -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 29d5c6c90..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 { @@ -46,7 +47,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; @@ -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 5d3d29799..a5ca90d9b 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" @@ -37,13 +36,13 @@ //#include <string> +namespace proc { namespace control { namespace test { // using boost::format; // using boost::str; - //using lumiera::Time; //using util::contains; // using std::tr1::function; // using std::tr1::bind; @@ -104,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 c73a11246..356d63be1 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" @@ -50,13 +49,13 @@ //#include <string> +namespace proc { namespace control { namespace test { // using boost::format; // using boost::str; - //using lumiera::Time; //using util::contains; using std::tr1::function; // using std::tr1::bind; @@ -73,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... @@ -121,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 1a8bd5eb3..518c9ff3a 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" @@ -47,6 +46,7 @@ #include <string> +namespace proc { namespace control { namespace test { @@ -151,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 267978d49..42576dddf 100644 --- a/tests/components/proc/engine/buff-table-test.cpp +++ b/tests/components/proc/engine/buff-table-test.cpp @@ -25,18 +25,18 @@ #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> #include <iostream> -#include <ctime> using test::Test; using std::cout; using std::rand; +namespace proc { namespace engine{ namespace test { @@ -81,6 +81,7 @@ namespace test { +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #852 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 #852 } // (End) internal defs @@ -137,12 +139,13 @@ namespace test { virtual void run(Arg) { counter = 0; - std::srand (time (NULL)); // allocate storage block to be used chunk wise pStorage.reset (new BuffTableStorage (TABLE_SIZ)); +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #852 invocation (0, detect_start_level(*pStorage)); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #852 pStorage.reset(0); // dtor throws assertion error if corrupted @@ -161,12 +164,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 } }; @@ -176,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 new file mode 100644 index 000000000..768016628 --- /dev/null +++ b/tests/components/proc/engine/buffer-metadata-key-test.cpp @@ -0,0 +1,404 @@ +/* + 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 "proc/engine/buffer-metadata.hpp" + +#include <boost/scoped_ptr.hpp> +#include <cstdlib> +#include <cstring> +#include <limits> + +using boost::scoped_ptr; +using util::isnil; +using util::isSameObject; + + +namespace proc { +namespace engine{ +namespace metadata{ +namespace test { + + + 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; + + + /** + * 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; + } + }; + + + /** + * 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 + + + + + + /*********************************************************************** + * @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 + * - also optionally, implementation might attach an private-ID + */ + class BufferMetadataKey_test : public Test + { + + virtual void + run (Arg) + { + CHECK (ensure_proper_fixture()); + buildSimpleKeys(); + verifyChainedHashes(); + verifyTypeHandler<500>(); + 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))); + } + + + 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); // "root" key + + 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); // 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); + 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)); + + 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 (RAW_BUFFER == verifyHandler(kb )); + CHECK (RAW_BUFFER == verifyHandler(k_siz )); + CHECK (noHandler == verifyHandler(k_han0)); + CHECK (placeMarker == verifyHandler(k_han1)); + CHECK (RAW_BUFFER == verifyHandler(k_loc1)); + CHECK (RAW_BUFFER == 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)); + + + // Verify 2nd level specialisation (some examples) + 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); + + 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 (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 ); + 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 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)); + + // 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)); + } + }; + + + /** Register this test class... */ + LAUNCHER (BufferMetadataKey_test, "unit player"); + + + +}}}} // 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 new file mode 100644 index 000000000..5ced14c03 --- /dev/null +++ b/tests/components/proc/engine/buffer-metadata-test.cpp @@ -0,0 +1,404 @@ +/* + 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.hpp" +#include "proc/engine/buffer-metadata.hpp" +#include "proc/engine/testframe.hpp" + +#include <boost/scoped_ptr.hpp> +#include <cstdlib> +#include <cstring> + +using std::strncpy; +using boost::scoped_ptr; +using lib::test::randStr; +using util::isSameObject; +using util::isnil; + + +namespace proc { +namespace engine{ +namespace test { + + using lumiera::error::LUMIERA_ERROR_FATAL; + 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; + + HashVal JUST_SOMETHING = 123; + void* const SOME_POINTER = &JUST_SOMETHING; + + + template<typename TY> + TY& + accessAs (metadata::Entry& entry) + { + TY* ptr = reinterpret_cast<TY*> (entry.access()); + ASSERT (ptr); + return *ptr; + } + }//(End) Test fixture and helpers + + + + + + + /******************************************************************* + * @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 + { + /** common Metadata table to be tested */ + scoped_ptr<BufferMetadata> meta_; + + virtual void + run (Arg) + { + CHECK (ensure_proper_fixture()); + verifyBasicProperties(); + verifyStandardCase(); + verifyStateMachine(); + } + + + bool + ensure_proper_fixture() + { + if (!meta_) + meta_.reset(new BufferMetadata("BufferMetadata_test")); + + return (SIZE_A != SIZE_B) + && (JUST_SOMETHING != meta_->key(SIZE_A)) + && (JUST_SOMETHING != meta_->key(SIZE_B)) + ; + } + + + void + verifyBasicProperties() + { + // retrieve some type keys + metadata::Key key = meta_->key(SIZE_A); + CHECK (key); + + metadata::Key key1 = meta_->key(SIZE_A); + metadata::Key key2 = meta_->key(SIZE_B); + CHECK (key1); + CHECK (key2); + CHECK (key == key1); + CHECK (key != key2); + + // access metadata entries + 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 (meta_->get(key), meta_->get(key))); + CHECK ( isSameObject (meta_->get(key), meta_->get(key1))); + CHECK (!isSameObject (meta_->get(key), meta_->get(key2))); + + // entries retrieved thus far were inactive (type only) entries + 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(FREE) ); + + // 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()); // buffer pointer associated + + // entries are unique and identifiable + 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()); + + // 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 (FATAL, m2.mark(LOCKED)); // buffer missing + CHECK ( isSameObject (m2, meta_->get(keyX))); // still accessible + + // release buffer... + meta_->release(keyX); + CHECK (!meta_->isLocked(keyX)); + CHECK (!meta_->isKnown(keyX)); + CHECK ( meta_->isKnown(key1)); + VERIFY_ERROR (INVALID, meta_->get(keyX)); // now unaccessible + } + + + /** @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 on a higher level in the mentioned 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(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); + CHECK (f2.access() == frames+2); + 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); + HashVal handle_f2(f2); + HashVal handle_r0(r0); + HashVal handle_r1(r1); + + // client uses the buffers---------------------(Start) + accessAs<TestFrame> (f0) = testData(1); + 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())); + + 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: implicitly invoking the embedded dtor + f1.mark(FREE); + f2.mark(FREE); + r0.mark(FREE); + r1.mark(FREE); + + + meta_->release(handle_f0); + meta_->release(handle_f1); + meta_->release(handle_f2); + meta_->release(handle_r0); + meta_->release(handle_r1); + + CHECK (TestFrame::isDead (&frames[0])); // was destroyed implicitly + CHECK (TestFrame::isDead (&frames[1])); + CHECK (TestFrame::isDead (&frames[2])); + + // manual cleanup of test allocations + delete[] frames; + delete[] rawbuf; + + 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)); + } + + + 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)); + + 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)); + } + }; + + + /** Register this test class... */ + LAUNCHER (BufferMetadata_test, "unit player"); + + + +}}} // 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 new file mode 100644 index 000000000..91344b12b --- /dev/null +++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp @@ -0,0 +1,236 @@ +/* + 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/test/testdummy.hpp" +#include "lib/util-foreach.hpp" +#include "proc/engine/testframe.hpp" +#include "proc/engine/diagnostic-buffer-provider.hpp" +#include "proc/engine/buffhandle-attach.hpp" +#include "proc/engine/bufftable.hpp" + +using util::isSameObject; +using util::for_each; + + +namespace proc { +namespace engine{ +namespace test { + + using lib::test::Dummy; + + using proc::engine::BuffHandle; + using error::LUMIERA_ERROR_LOGIC; + using error::LUMIERA_ERROR_LIFECYCLE; + + + namespace { // Test fixture + + 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"); + } + + } + + + /****************************************************************************** + * @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 + { + virtual void + run (Arg) + { + verifySimpleUsage(); + verifyStandardCase(); + verifyObjectAttachment(); + verifyObjectAttachmentFailure(); + } + + + void + verifySimpleUsage() + { + // 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.accessAs<TestFrame>() = testData(0); + + TestFrame& content = buff.accessAs<TestFrame>(); + CHECK (testData(0) == content); + + buff.emit(); + buff.release(); + CHECK (!buff.isValid()); + VERIFY_ERROR (LIFECYCLE, buff.accessAs<TestFrame>() ); + + DiagnosticBufferProvider& checker = DiagnosticBufferProvider::access(provider); + CHECK (checker.buffer_was_used (0)); + CHECK (checker.buffer_was_closed (0)); + + CHECK (testData(0) == checker.accessMemory (0)); + } + + + 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 + BufferProvider& provider = DiagnosticBufferProvider::build(); + + BufferDescriptor desc1 = provider.getDescriptor<TestFrame>(); // note: implies also sizeof(TestFrame) + BufferDescriptor desc2 = provider.getDescriptorFor(TEST_SIZE); + CHECK (desc1.verifyValidity()); + CHECK (desc2.verifyValidity()); + + uint num1 = provider.announce(TEST_ELMS, desc1); + uint 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_SIZE, storage) + .announce(num1, desc1) + .announce(num2, desc2) + .build(); + + tab.lockBuffers(); + for_each (tab.buffers(), do_some_calculations); + tab.releaseBuffers(); + + DiagnosticBufferProvider& checker = DiagnosticBufferProvider::access(provider); + 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>() ); + } + }; + + + /** Register this test class... */ + LAUNCHER (BufferProviderProtocol_test, "unit player"); + + + +}}} // namespace proc::engine::test 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/dispatcher-interface-test.cpp b/tests/components/proc/engine/dispatcher-interface-test.cpp new file mode 100644 index 000000000..b7dfc1476 --- /dev/null +++ b/tests/components/proc/engine/dispatcher-interface-test.cpp @@ -0,0 +1,292 @@ +/* + DispatcherInterface(Test) - document and verify dispatcher for frame job creation + + 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/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" +#include "lib/time/timequant.hpp" +#include "lib/singleton.hpp" +#include "lib/itertools.hpp" +#include "lib/util.hpp" + +//#include <boost/scoped_ptr.hpp> +//#include <iostream> +#include <tr1/functional> +#include <vector> + +using test::Test; +using util::isnil; +using std::vector; +using std::tr1::function; +//using std::cout; +//using std::rand; + + +namespace proc { +namespace engine{ +namespace test { + + using lib::time::QuTime; + using lib::time::FrameRate; + using lib::time::Duration; + using lib::time::Offset; + using lib::time::TimeVar; + using lib::time::Time; + using mobject::ModelPort; + using play::Timings; + + namespace { // used internally + + using play::PlayTestFrames_Strategy; + using play::ModelPorts; + + typedef play::DummyPlayConnection<play::PlayTestFrames_Strategy> DummyPlaybackSetup; + + + class MockDispatcherTable + : public Dispatcher + { + + DummyPlaybackSetup dummySetup_; + + + /* == mock Dispatcher implementation == */ + + FrameCoord + locateFrameNext (uint frameCountOffset, TimeAnchor refPoint) + { + 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: + + ModelPort + provideMockModelPort() + { + ModelPorts mockModelPorts = dummySetup_.provide_testModelPorts(); + return *mockModelPorts; // using just the first dummy port + } + }; + + lib::Singleton<MockDispatcherTable> mockDispatcher; + +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880 +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #890 + ModelPort + getTestPort() + { + return mockDispatcher().provideMockModelPort(); + } + + + } // (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(); + check_ContinuationBuilder(); + } + + + /** @test perform the basic dispatch step + * and verify the generated frame coordinates + */ + void + verify_basicDispatch() + { + Dispatcher& dispatcher = mockDispatcher(); + Timings timings (FrameRate::PAL); + ModelPort modelPort (getTestPort()); + uint startFrame(10); + uint channel(0); + + TimeAnchor refPoint = TimeAnchor::build (timings, startFrame); + CHECK (refPoint == Time::ZERO + Duration(10, FrameRate::PAL)); + + FrameCoord coordinates = dispatcher.onCalcStream (modelPort,channel) + .relativeFrameLocation (refPoint, 15); + CHECK (coordinates.absoluteNominalTime == Time(0,1)); + CHECK (coordinates.absoluteFrameNumber == 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); + + JobTicket& executionPlan = dispatcher.getJobTicketFor (coordinates); + CHECK (executionPlan.isValid()); + + 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 +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880 + } + + + /** @test the standard invocation sequence + * used within the engine for planning new jobs. + * The actual implementation is mocked. + */ + 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); + + JobPlanningSequence jobs = dispatcher.onCalcStream(modelPort,channel) + .establishNextJobs(refPoint); + + // Verify the planned Jobs + + CHECK (!isnil (jobs)); + vector<Job> plannedChunk; + lib::append_all (jobs, plannedChunk); + +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880 + uint chunksize = plannedChunk.size(); + CHECK (chunksize == timings.getPlanningChunkSize()); + + TimeVar nextFrameStart (refPoint); + InvocationInstanceID prevInvocationID(0); + Offset expectedTimeIncrement (1, FrameRate::PAL); + for (uint i=0; i < chunksize; ++i ) + { + Job& thisJob = plannedChunk[i]; + CHECK (nextFrameStart == thisJob.getNominalTime()); + CHECK (prevInvocationID < thisJob.getInvocationInstanceID()); + + 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); + + // prepare the rest of this test to be invoked as "continuation" + function<void(TimeAnchor)> testFunc = verify_invocation_of_Continuation; + + TimeAnchor refPoint = TimeAnchor::build (timings, startFrame); + 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()); + + uint nrJobs = timings.getPlanningChunkSize(); + Duration frameDuration (1, FrameRate::PAL); + + // 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. + // 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); + } + }; + + + /** Register this test class... */ + LAUNCHER (DispatcherInterface_test, "unit 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..7f7b6ea59 --- /dev/null +++ b/tests/components/proc/engine/engine-interface-test.cpp @@ -0,0 +1,117 @@ +/* + 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/output-slot.hpp" +#include "proc/play/diagnostic-output-slot.hpp" +#include "proc/mobject/model-port.hpp" +#include "proc/asset/pipe.hpp" +#include "lib/time/timevalue.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; + using proc::play::OutputSlot; + using proc::play::DiagnosticOutputSlot; + using lib::time::FrameRate; + + typedef asset::ID<Pipe> PID; + typedef OutputSlot::Allocation Allocation; + + + 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 (FrameRate::PAL); /////////TODO + + // Invoke test subject... + CalcStreams 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/tests/components/proc/engine/node-basic-test.cpp b/tests/components/proc/engine/node-basic-test.cpp index 8e973cc1a..1c69d3443 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" @@ -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 new file mode 100644 index 000000000..5b6f62990 --- /dev/null +++ b/tests/components/proc/engine/testframe-test.cpp @@ -0,0 +1,209 @@ +/* + 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 "lib/util.hpp" + +#include <cstdlib> +#include <limits.h> +#include <boost/scoped_ptr.hpp> + +using test::Test; +using std::rand; +using util::isSameObject; +using boost::scoped_ptr; + + +namespace proc { +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(); + useFrameTable(); + } + + + 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) + { + thisFrames[i].swap (prevFrames[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 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]); // 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()); + } + }; + + + /** Register this test class... */ + LAUNCHER (TestFrame_test, "unit engine"); + + + +}}} // namespace proc::engine::test diff --git a/tests/components/proc/engine/testframe.cpp b/tests/components/proc/engine/testframe.cpp new file mode 100644 index 000000000..7f46ffe44 --- /dev/null +++ b/tests/components/proc/engine/testframe.cpp @@ -0,0 +1,293 @@ +/* + 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 <boost/random/linear_congruential.hpp> +#include <boost/scoped_ptr.hpp> + +#include <limits.h> +#include <cstring> +#include <vector> + + + +namespace proc { +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. + * 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. + * @param seq the sequence number of the frame within the channel + * @param family the channel this frame belongs to + */ + uint64_t + generateDistinction(uint seq, uint family) + { + // random offset, but fixed per executable run + static uint base(10 + rand() % 990); + + // 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 (i,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& + testData (uint seqNr) + { + return accessTestFrame (seqNr, 0); + } + + TestFrame& + testData (uint chanNr, uint seqNr) + { + return accessTestFrame (seqNr,chanNr); + } + + void + resetTestFrames() + { + testFrames.reset(0); + } + + + + + /* ===== TestFrame class ===== */ + + TestFrame::~TestFrame() + { + stage_ = DISCARDED; + } + + + TestFrame::TestFrame(uint seq, uint family) + : distinction_(generateDistinction (seq,family)) + , stage_(CREATED) + { + ASSERT (0 < distinction_); + buildData(); + } + + + TestFrame::TestFrame (TestFrame const& o) + : distinction_(o.distinction_) + , 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) + { + distinction_ = o.distinction_; + stage_ = CREATED; + memcpy (data_, o.data_, BUFFSIZ); + } + return *this; + } + + + + /** @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) + { + TestFrame& candidate (accessAsTestFrame (memLocation)); + return candidate.isSane() + && candidate.isAlive(); + } + + bool + TestFrame::isDead (void* memLocation) + { + TestFrame& candidate (accessAsTestFrame (memLocation)); + return candidate.isSane() + && candidate.isDead(); + } + + bool + TestFrame::operator== (void* memLocation) const + { + TestFrame& candidate (accessAsTestFrame (memLocation)); + return candidate.isSane() + && candidate == *this; + } + + bool + TestFrame::contentEquals (TestFrame const& o) const + { + for (uint i=0; i<BUFFSIZ; ++i) + if (data_[i] != o.data_[i]) + return false; + return true; + } + + bool + TestFrame::verifyData() const + { + PseudoRandom gen(distinction_); + for (uint i=0; i<BUFFSIZ; ++i) + if (data_[i] != (gen() % CHAR_MAX)) + return false; + return true; + } + + void + TestFrame::buildData() + { + PseudoRandom gen(distinction_); + for (uint i=0; i<BUFFSIZ; ++i) + data_[i] = (gen() % CHAR_MAX); + } + + + bool + TestFrame::isAlive() const + { + return (CREATED == stage_) + || (EMITTED == stage_); + } + + bool + TestFrame::isDead() const + { + return (DISCARDED == stage_); + } + + bool + TestFrame::isSane() const + { + return ( (CREATED == stage_) + ||(EMITTED == stage_) + ||(DISCARDED == stage_)) + && verifyData(); + } + + + +}}} // namespace proc::engine::test diff --git a/tests/components/proc/engine/testframe.hpp b/tests/components/proc/engine/testframe.hpp new file mode 100644 index 000000000..05cfecff8 --- /dev/null +++ b/tests/components/proc/engine/testframe.hpp @@ -0,0 +1,121 @@ +/* + 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 <cstdlib> +#include <stdint.h> + + +namespace proc { +namespace engine { +namespace test { + + + /** + * 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 + * + * 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 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. + * + * @see TestFrame_test + * @see OutputSlotProtocol_test + * + */ + class TestFrame + { + enum StageOfLife { + CREATED, EMITTED, DISCARDED + }; + + static const size_t BUFFSIZ = 1024; + + uint64_t distinction_; + StageOfLife stage_; + + char data_[BUFFSIZ]; + + public: + ~TestFrame(); + TestFrame (uint seq=0, uint family=0); + TestFrame (TestFrame const&); + TestFrame& operator= (TestFrame const&); + + /** 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. + */ + static bool isAlive (void* memLocation); + + /** Helper to verify a given memory location holds + * an already destroyed TestFrame instance */ + static bool isDead (void* memLocation); + + bool isAlive() const; + bool isDead() const; + bool isSane() const; + + bool operator== (void* memLocation) const; + + 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; + bool verifyData() const; + void buildData (); + }; + + + + /** 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); + + + /** discards all the TestFrame instances and + * initialises an empty table of test frames */ + void resetTestFrames(); + + + +}}} // 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 new file mode 100644 index 000000000..311a647d3 --- /dev/null +++ b/tests/components/proc/engine/tracking-heap-block-provider-test.cpp @@ -0,0 +1,223 @@ +/*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 "proc/engine/tracking-heap-block-provider.hpp" +#include "proc/engine/buffhandle-attach.hpp" +#include "proc/engine/testframe.hpp" + +#include <cstdlib> +#include <vector> + +using std::rand; + + +namespace proc { +namespace engine{ +namespace test { + + + + 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_used() + && 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) + { + 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.emitBuffer (testBuff); + provider.releaseBuffer(testBuff); + + diagn::Block& block0 = provider.access_emitted(0); + 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.lockBuffer(buffType); + buff.accessAs<uint>() = testNumbers[i] = rand() % 100000; + provider.emitBuffer (buff); + provider.releaseBuffer(buff); + } + + for (uint nr=0; nr<numElms; ++nr) + { + CHECK (verifyUsedBlock (nr, provider.access_emitted(nr))); + } + } + + + void + verifyTestProtocol() + { + TrackingHeapBlockProvider provider; + + BufferDescriptor buffType = provider.getDescriptorFor(TEST_ELM_SIZE); + + 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); + + // 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 (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()); + } + }; + + + /** Register this test class... */ + LAUNCHER (TrackingHeapBlockProvider_test, "unit player"); + + + +}}} // namespace proc::engine::test diff --git a/tests/components/proc/mobject/builder/buildertooltest.cpp b/tests/components/proc/mobject/builder/buildertooltest.cpp index 52a85f0af..988b76bc4 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> @@ -39,12 +40,14 @@ using std::cout; +namespace proc { namespace mobject { namespace builder { namespace test { using session::Clip; using session::AbstractMO; + using lib::test::Use4Test; using namespace mobject::test; @@ -106,9 +109,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; @@ -140,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..d37ae5653 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 { @@ -257,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)); @@ -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 b1c5a1bae..3f0d5a8fe 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" @@ -36,12 +35,16 @@ //#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 <iostream> + +namespace proc { namespace mobject { namespace test { @@ -51,9 +54,12 @@ namespace test { using std::endl; using lib::Symbol; + using lib::test::Use4Test; + using lib::time::Duration; + using lib::time::FSecs; + using lib::time::Time; - using lumiera::Time; // using session::Clip; // using session::PMedia; @@ -78,17 +84,20 @@ 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(); // 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); @@ -99,9 +108,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; @@ -117,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 c4b9bbd50..1b9ae39e8 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" @@ -33,16 +32,25 @@ #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; +using lib::time::Time; +using util::isnil; using std::string; using std::cout; using std::endl; +namespace proc { namespace mobject { namespace test { @@ -67,7 +75,6 @@ namespace test { } - using lumiera::Time; using session::Clip; using session::PMedia; @@ -97,14 +104,16 @@ 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(); 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()); @@ -190,13 +199,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>()); @@ -371,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 150907b62..1eccec30a 100644 --- a/tests/components/proc/mobject/placement-basic-test.cpp +++ b/tests/components/proc/mobject/placement-basic-test.cpp @@ -27,17 +27,19 @@ #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; +namespace proc { namespace mobject { namespace session { namespace test { @@ -72,16 +74,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()); } }; @@ -92,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 12196b603..6dbcd39a4 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; @@ -41,6 +43,7 @@ using std::cout; using std::endl; +namespace proc { namespace mobject { namespace session { namespace test { @@ -65,6 +68,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; @@ -168,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 6d3e1044c..e31662d08 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" @@ -32,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" @@ -42,10 +42,10 @@ //using std::endl; +namespace proc { namespace mobject { namespace test { - using lumiera::Time; using session::Clip; using session::SessionServiceMockIndex; @@ -81,8 +81,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()); @@ -156,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 dce4db94d..f57d509e6 100644 --- a/tests/components/proc/mobject/placement-ref-test.cpp +++ b/tests/components/proc/mobject/placement-ref-test.cpp @@ -29,18 +29,18 @@ #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/time/timevalue.hpp" #include "lib/util.hpp" #include <iostream> using util::isSameObject; -using lumiera::Time; using std::string; using std::cout; using std::endl; +namespace proc { namespace mobject { namespace session { namespace test { @@ -49,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 @@ -71,7 +71,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(); @@ -115,7 +117,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 @@ -153,9 +155,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()); @@ -187,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 34e83d2b5..4e57950a3 100644 --- a/tests/components/proc/mobject/session/addcliptest.cpp +++ b/tests/components/proc/mobject/session/addcliptest.cpp @@ -31,50 +31,43 @@ #include <iostream> //using boost::format; -using lumiera::Time; using util::contains; 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 f3cc8d5e0..9df802a31 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" @@ -32,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> @@ -41,6 +39,7 @@ using util::isnil; using std::string; +namespace proc { namespace mobject { namespace session { namespace test { @@ -56,7 +55,6 @@ namespace test { using lumiera::ConfigRules; using lumiera::query::QueryHandler; - using lumiera::StreamType; @@ -157,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" @@ -173,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 3f9ae390e..720c12b6c 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" @@ -33,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> @@ -43,6 +41,7 @@ using std::string; +namespace proc { namespace mobject { namespace session { namespace test { @@ -57,7 +56,6 @@ namespace test { using asset::Struct; using lumiera::Query; using lumiera::query::normaliseID; - using lumiera::StreamType; /** shortcut: run just a query @@ -201,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 8025bfbf0..6ec3ed9b7 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; @@ -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); @@ -171,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; @@ -229,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.... @@ -242,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)); @@ -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..e54fed27a 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 { @@ -108,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) ); @@ -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 f7cb3d974..42ea5329e 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" @@ -35,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; @@ -48,6 +46,7 @@ using std::cout; using std::set; +namespace proc { namespace mobject { namespace session { namespace test { @@ -59,7 +58,6 @@ namespace test { using asset::PSequence; using asset::Sequence; -// using lumiera::Time; using lumiera::Query; typedef MORef<session::Clip> RClip; @@ -321,7 +319,7 @@ namespace test { { TODO ("verify the commands issued by this test"); ////////////////////////TICKET #567 } - + }; @@ -330,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..a96436e8b 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,9 +45,9 @@ 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; @@ -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 6ae27c3ee..448209662 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> @@ -37,6 +36,7 @@ using std::string; using std::cout; +namespace proc { namespace mobject { namespace session { namespace test { @@ -47,7 +47,6 @@ namespace test { using asset::PTimeline; using asset::PSequence; - using lumiera::Time; /******************************************************************************* @@ -140,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 c8c916512..1f9141f3a 100644 --- a/tests/components/proc/mobject/session/testclip.cpp +++ b/tests/components/proc/mobject/session/testclip.cpp @@ -22,20 +22,23 @@ #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" +#include "lib/time/timevalue.hpp" +namespace proc { namespace mobject { namespace session { namespace test { - using lumiera::Time; + 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; @@ -85,7 +88,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 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 5907f6750..7bf9b28d4 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" @@ -34,6 +34,7 @@ using std::tr1::shared_ptr; using std::string; +namespace proc { namespace mobject { namespace session { namespace test { @@ -78,8 +79,8 @@ namespace test { /* == some test data to check == */ - extern const lumiera::Time LENGTH_TestClip; + 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 a1eb84a34..72ba5b60f 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" @@ -42,6 +41,7 @@ using std::string; using std::cout; +namespace proc { namespace mobject { namespace session { namespace test { @@ -56,7 +56,6 @@ namespace test { using asset::Pipe; using lumiera::Query; - using lumiera::Time; /******************************************************************************** @@ -262,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 @@ -284,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()); @@ -304,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 @@ -317,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 new file mode 100644 index 000000000..e78062589 --- /dev/null +++ b/tests/components/proc/play/diagnostic-output-slot.hpp @@ -0,0 +1,437 @@ +/* + 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 "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/advice.hpp" +#include "lib/symbol.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> + + +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; + using proc::asset::meta::TimeGrid; + 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; +//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 + * the processing protocol for output buffers. + */ + class TrackingInMemoryBlockSequence + : public OutputSlot::Connection + , boost::noncopyable + { + + typedef std::tr1::unordered_set<FrameID> FrameTrackingInfo; + + + TrackingHeapBlockProvider buffProvider_; + BufferDescriptor bufferType_; + + FrameTrackingInfo frameTrackingIndex_; + PGrid frameGrid_; + + bool closed_; + + + 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); + + frameTrackingIndex_.insert (frameNr); + return newBuffer; + } + + TimeValue + deadlineFor (FrameID frameNr) + { + // a real world implementation + // would offset by a latency here + return frameGrid_->timeOf (frameNr); + } + + + + + /* === Connection API === */ + + BuffHandle + claimBufferFor(FrameID frameNr) + { + TRACE (test, "Con=%p : lock buffer for frame-#%lu", this, frameNr); + REQUIRE (!closed_); + + return trackFrame (frameNr, + buffProvider_.lockBuffer (bufferType_)); + } + + + bool + isTimely (FrameID frameNr, TimeValue currentTime) + { + TRACE (test, "Con=%p : timely? frame-#%lu" + , this, frameNr); + + if (Time::ANYTIME == currentTime) + return true; + else + return currentTime < deadlineFor (frameNr); + } + + void + transfer (BuffHandle const& filledBuffer) + { + TRACE (test, "Con=%p : transfer buffer %zu" + , this, filledBuffer.entryID()); + REQUIRE (!closed_); + + pushout (filledBuffer); + } + + void + pushout (BuffHandle const& data4output) + { + REQUIRE (!closed_); + buffProvider_.emitBuffer (data4output); + buffProvider_.releaseBuffer(data4output); + } + + void + discard (BuffHandle const& superseededData) + { + REQUIRE (!closed_); + buffProvider_.releaseBuffer (superseededData); + } + + void + shutDown () + { + closed_ = true; + } + + public: + TrackingInMemoryBlockSequence() + : buffProvider_() + , bufferType_(buffProvider_.getDescriptor<TestFrame>()) + , frameTrackingIndex_() + , 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 (at %p)", this); + } + + virtual + ~TrackingInMemoryBlockSequence() + { + INFO (engine_dbg, "releasing diagnostic output sequence (at %p)", this); + } + + + /* === Diagnostic API === */ + + TestFrame const * + accessEmittedFrame (uint frameNr) const + { + if (frameNr < buffProvider_.emittedCnt()) + return & accessFrame(frameNr); + else + return 0; ////////////////////////////////TICKET #856 + } + + diagn::Block const * + accessEmittedBuffer (uint bufferNr) const + { + if (bufferNr < buffProvider_.emittedCnt()) + return & accessBlock(bufferNr); + else + return 0; + } + + bool + wasAllocated (uint frameNr) const + { + TRACE (test, "query wasAllocated. Con=%p", this); + + 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); + } + }; + + + /** + * 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> + { + typedef ConnectionStateManager<TrackingInMemoryBlockSequence> _Base; + + void + buildConnection(ConnectionStorage storage) + { + storage.create<TrackingInMemoryBlockSequence>(); + } + + public: + SimulatedOutputSequences (uint numChannels) + : _Base(numChannels) + { + init(); + } + }; + + + + + + /******************************************************************** + * Helper for unit tests: Mock output sink. + * 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( + getOutputChannelCount()); + } + + /** @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 <= getOutputChannelCount()); + return static_cast<TrackingInMemoryBlockSequence&> (state_->access (channel)); + } + + + public: + /** build a new Diagnostic Output Slot instance, + * discard the existing one. Use the static query API + * for investigating collected data. */ + static OutputSlot& + build() + { + static lib::ScopedPtrVect<OutputSlot> diagnosticSlots; + return diagnosticSlots.manage(new DiagnosticOutputSlot); + } + + static DiagnosticOutputSlot& + access (OutputSlot& to_investigate) + { + return dynamic_cast<DiagnosticOutputSlot&> (to_investigate); + } + + + + /* === diagnostics API === */ + + /** + * diagnostic facility to verify test data frames + * written to this Test/Dummy "output". It exposes + * the emitted Data as a sequence of TestFrame objects. + */ + class OutputFramesLog + : public lib::IterSource<const TestFrame> + , boost::noncopyable + { + 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 OutputFramesLog::iterator OutFrames; + + + OutFrames + getChannel (uint channel) + { + REQUIRE (channel < MAX_CHANNELS); + return OutputFramesLog::build( + new OutputFramesLog ( + accessSequence(channel))); + } + + + bool + frame_was_allocated (uint channel, FrameID nominalFrame) + { + return accessSequence(channel) + .wasAllocated(nominalFrame); + } + + + bool + output_was_emitted (uint channel, FrameID outputFrame) + { + diagn::Block const *block = accessSequence(channel) + .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(); + } + }; + + + +}} // namespace proc::play +#endif diff --git a/tests/components/proc/play/dummy-play-connection-test.cpp b/tests/components/proc/play/dummy-play-connection-test.cpp new file mode 100644 index 000000000..8f2350c11 --- /dev/null +++ b/tests/components/proc/play/dummy-play-connection-test.cpp @@ -0,0 +1,106 @@ +/* + DummyPlayConnection(Test) - create and verify a simplified test render engine setup + + 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 "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 time::Control<time::Duration> DurationControl; + + typedef DummyPlayConnection<PlayTestFrames_Strategy> DummyPlayer; + + + /******************************************************************* + * @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() + { + DummyPlayer dummy; + CHECK (!dummy.isWired()); + + Play::Controller player + = Play::facade().connect ( dummy.provide_testModelPorts() + , dummy.provide_testOutputSlot()); + CHECK ( dummy.isWired()); + + DurationControl playDuration; + player.controlDuration (playDuration); + + // configure the controller to playback only for a fixed time duration + playDuration (dummy.getPlannedTestDuration()); + + CHECK (!player.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 (!player.is_playing()); // should have returned to pause, since we've set a fixed playback duration + CHECK (dummy.isWired()); + + player.close(); + CHECK (!dummy.isWired()); + CHECK (dummy.gotCorrectOutput()); + } + }; + + + /** Register this test class... */ + LAUNCHER (DummyPlayConnection_test, "unit player"); + + + +}}} // namespace proc::play::test 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..d63428175 --- /dev/null +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -0,0 +1,135 @@ +/* + 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 "proc/play/diagnostic-output-slot.hpp" +#include "proc/engine/buffhandle.hpp" +#include "proc/engine/testframe.hpp" + + + +namespace proc { +namespace play { +namespace test { + + using proc::engine::BuffHandle; + using proc::engine::test::testData; + using proc::engine::test::TestFrame; + + + + + /******************************************************************* + * @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 + { + virtual void + run (Arg) + { + 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. + OutputSlot::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 a data exchange cycle + int64_t frameNr = 123; + BuffHandle buff00 = sink1.lockBufferFor (frameNr); + BuffHandle buff10 = sink2.lockBufferFor (frameNr); + + // rendering process calculates content.... + buff00.accessAs<TestFrame>() = testData(0,0); + + // while further frames might be processed in parallel + BuffHandle buff11 = sink2.lockBufferFor (++frameNr); + buff11.accessAs<TestFrame>() = testData(1,1); + buff10.accessAs<TestFrame>() = testData(1,0); + + // Now it's time to emit the output + sink2.emit (frameNr-1, buff10); + sink2.emit (frameNr , buff11); + sink1.emit (frameNr-1, buff00); + // that's all for the client + + // Verify sane operation.... + DiagnosticOutputSlot& checker = DiagnosticOutputSlot::access(oSlot); + 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.output_was_closed (0,0)); + CHECK (checker.output_was_closed (1,0)); + CHECK (checker.output_was_closed (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); + + CHECK ( stream0); + CHECK (*stream0 == testData(0,0)); ++stream0; + CHECK (!stream0); + + CHECK ( stream1); + CHECK (*stream1 == testData(1,0)); ++stream1; + CHECK (*stream1 == testData(1,1)); ++stream1; + CHECK (!stream1); + } + }; + + + /** Register this test class... */ + LAUNCHER (OutputSlotProtocol_test, "unit player"); + + + +}}} // namespace proc::play::test 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..f63b0114d --- /dev/null +++ b/tests/components/proc/play/timing-constraints-test.cpp @@ -0,0 +1,89 @@ +/* + TimingConstraints(Test) - verify timing constraint specifications + + 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 "proc/play/timings.hpp" +#include "lib/time/timevalue.hpp" +//#include "proc/engine/buffhandle.hpp" +//#include "proc/engine/testframe.hpp" +//#include "lib/time/control.hpp" + + + +namespace proc { +namespace play { +namespace test { + + using lib::time::Time; + using lib::time::Duration; + using lib::time::FrameRate; + +//using proc::engine::BuffHandle; +//using proc::engine::test::testData; +//using proc::engine::test::TestFrame; + +//using lumiera::Play; + +//typedef time::Control<time::Duration> DurationControl; +// +//typedef DummyPlayConnection<PlayTestFrames_Strategy> 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() + { + Timings timings (FrameRate::PAL); + + 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 + } + }; + + + /** Register this test class... */ + LAUNCHER (TimingConstraints_test, "unit player"); + + + +}}} // namespace proc::play::test 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/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/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 \ 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/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/allocation-cluster-test.cpp b/tests/lib/allocation-cluster-test.cpp index cf8897bf5..4e765b41c 100644 --- a/tests/lib/allocation-cluster-test.cpp +++ b/tests/lib/allocation-cluster-test.cpp @@ -187,6 +187,11 @@ namespace test { // point at the objects we created. Just use them // and let them go. When clu goes out of scope, // all created object's dtors will be invoked. + + CHECK (4 == clu.size()); + CHECK (1 == clu.count<Dummy<44> >()); + CHECK (2 == clu.count<Dummy<37> >()); + CHECK (1 == clu.count<Dummy<1234> >()); } 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/diagnostic-context-test.cpp b/tests/lib/diagnostic-context-test.cpp new file mode 100644 index 000000000..ba1183510 --- /dev/null +++ b/tests/lib/diagnostic-context-test.cpp @@ -0,0 +1,217 @@ +/* + DiagnosticContext(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... + + /* WARNING: memory hungry */ + const uint NUM_THREADS = 40; + const uint MAX_RAND = 100*1000; + + inline bool + isOdd (uint val) + { + return bool (val % 2); + } + + } // (End) test setup.... + + 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; + + typedef std::vector<uint> VecI; + + + + + + /********************************************************************************** + * @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 DiagnosticContext_test : public Test + { + + virtual void + run (Arg) + { + verify_simpleAccess(); + 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()); + + VecI loggedValues; + + 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); + + loggedValues = Marker::extractStack(); + } + CHECK (1 == Marker::access()); + } + CHECK (0 == Marker::access()); + + CHECK (3 == loggedValues.size()); + CHECK (2 == loggedValues[0]); + CHECK (1 == loggedValues[1]); + CHECK (0 == loggedValues[2]); + } + + + + + /** @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. + * @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() + { + 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) + { } + }; + + + /** the actual test operation running in a separate thread */ + static void + verifyDiagnosticStack() + { + uint seed (1 + rand() % MAX_RAND); + + VecI sequence = descend (seed); + + uint prev = 0; + for (uint i=0; i < sequence.size(); ++i) + { + uint val = sequence[i]; + if (! (isOdd(val) && seed >= val && val > prev )) + throw error::Fatal ("thread-local diagnostic stack"); + prev = val; + } + } + + static VecI + descend (uint current) + { + if (current < 2) + return Marker::extractStack(); + + usleep (500); + if (isOdd(current)) + { + Marker remember(current); + return descend (current+1); + } + else + return descend (current/2); + } + }; + + + + /** Register this test class... */ + LAUNCHER (DiagnosticContext_test, "function common"); + + + +}} // namespace backend::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 diff --git a/tests/lib/format-helper-test.cpp b/tests/lib/format-helper-test.cpp index 4c246ff3d..f223510af 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> @@ -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 () { diff --git a/tests/lib/format-string-test.cpp b/tests/lib/format-string-test.cpp new file mode 100644 index 000000000..34bf3a2c2 --- /dev/null +++ b/tests/lib/format-string-test.cpp @@ -0,0 +1,326 @@ +/* + 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/test/test-helper.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, 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. + * - exceptions 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=%-9s--") + % 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; + void *pt = &val; + + formatter % pt; + 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 == "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 == "000012" ); + + 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("%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.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 == "@" ); + 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" ); + + 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("%c") % c == "\xFF"); + + string str("Lumiera"); + CHECK (_Fmt("%s") % str == "Lumiera" ); + CHECK (_Fmt("%10s") % str == " Lumiera" ); + CHECK (_Fmt("%7.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 == "!!<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 == "!!<string conversion failed: LUMIERA_ERROR_STATE:unforeseen 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; + + const char* evil = NULL; + cout << _Fmt("__%d__") % evil << endl; + cout << _Fmt("__%d__") % "dirt" << endl; + cout << _Fmt("__%d__") % "1234" << endl; + cout << _Fmt("__%d__") % "0xff" << endl; + + VERIFY_ERROR(FORMAT_SYNTAX, _Fmt("%broken")); + } + + + 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; + 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); + 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") % 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__"); + 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__") % vv == "__0__"); + } + }; + + LAUNCHER (FormatString_test, "unit meta"); + + +}} // namespace util::test + diff --git a/tests/lib/functor-util-test.cpp b/tests/lib/functor-util-test.cpp index 0692158fe..8ff8327fb 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,84 @@ 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; + h1 = calculateHash (f1); + 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 + } }; 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/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/lib/iter-adapter-test.cpp b/tests/lib/iter-adapter-test.cpp index 6376c49bc..bc72817de 100644 --- a/tests/lib/iter-adapter-test.cpp +++ b/tests/lib/iter-adapter-test.cpp @@ -132,9 +132,10 @@ namespace test{ /** Implementation of Iteration-logic: pull next element. */ template<class ITER> friend void - iterNext (const TestContainer*, ITER& pos) + iterNext (const TestContainer* src, ITER& pos) { ++pos; + checkPoint (src,pos); } /** Implementation of Iteration-logic: detect iteration end. @@ -147,7 +148,7 @@ namespace test{ */ template<class ITER> friend bool - hasNext (const TestContainer* src, ITER& pos) + checkPoint (const TestContainer* src, ITER& pos) { REQUIRE (src); if ((pos != ITER(0)) && (pos != src->numberz_.end())) @@ -215,7 +216,10 @@ namespace test{ // now for example the client could.... while ( range ) - cout << "::" << *range++; + { + cout << "::" << *range; + ++range; + } cout << endl; CHECK (isnil (range)); @@ -317,7 +321,7 @@ namespace test{ TestContainer::ref_iterator rI (elms.begin_ref()); CHECK (0 == *rI ); - CHECK (0 == *rI++); + ++rI; CHECK (1 == *rI ); CHECK (2 == *++rI); diff --git a/tests/lib/iter-explorer-test.cpp b/tests/lib/iter-explorer-test.cpp new file mode 100644 index 000000000..a98ae02e2 --- /dev/null +++ b/tests/lib/iter-explorer-test.cpp @@ -0,0 +1,548 @@ +/* + IterExplorer(Test) - verify evaluation patterns built using iterators + + 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/iter-adapter-stl.hpp" +#include "lib/util.hpp" + +#include "lib/iter-explorer.hpp" + +#include <boost/lexical_cast.hpp> +#include <iostream> +#include <sstream> +#include <vector> +#include <string> + +#include "lib/meta/trait.hpp" + + +namespace lib { +namespace test{ + + using ::Test; + using util::isnil; + using util::isSameObject; + using std::cout; + using std::endl; + using std::string; + using lib::iter_stl::eachElm; + using lib::iter_explorer::ChainedIters; + using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST; + + + namespace { // test substrate: simple number sequence iterator + + /** + * This iteration "state core" type describes + * a sequence of numbers yet to be delivered. + */ + class State + { + uint p,e; + + public: + State(uint start, uint end) + : p(start) + , e(end) + { } + + friend bool + checkPoint (State const& st) + { + return st.p < st.e; + } + + friend uint& + yield (State const& st) + { + return util::unConst(checkPoint(st)? st.p : st.e); + } + + friend void + iterNext (State & st) + { + if (!checkPoint(st)) return; + ++st.p; + } + }; + + + + /** + * A straight ascending number sequence as basic test iterator. + * The tests will dress up this source sequence in various ways. + */ + class NumberSequence + : public IterStateWrapper<uint, State> + { + + public: + explicit + NumberSequence(uint end = 0) + : IterStateWrapper<uint,State> (State(0,end)) + { } + NumberSequence(uint start, uint end) + : IterStateWrapper<uint,State> (State(start,end)) + { } + + /** allow using NumberSequence in LinkedElements + * (intrusive single linked list) */ + NumberSequence* next; + }; + + inline NumberSequence + seq (uint end) + { + return NumberSequence(end); + } + + inline NumberSequence + seq (uint start, uint end) + { + return NumberSequence(start, end); + } + + NumberSequence NIL_Sequence; + + + /** + * 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<int> NumberSeries; + + + /** "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) + { + 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 + */ + template<class II> + inline string + materialise (II ii) + { + std::ostringstream buff; + while (ii) + { + buff << *ii; + if (++ii) buff << "-"; + } + return buff.str(); + } + + template<class II> + inline void + pullOut (II & ii) + { + while (ii) + { + cout << *ii; + if (++ii) cout << "-"; + } + cout << endl; + } + + } // (END) test helpers + + + + + + + + /********************************************************************* + * @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 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. + * 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) + { + verifyStateAdapter(); + + verifyMonadOperator (); + verifyChainedIterators(); + verifyRawChainedIterators(); + + verifyDepthFirstExploration(); + verifyBreadthFirstExploration(); + verifyRecursiveSelfIntegration(); + } + + + + /** @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(9); + CHECK (!isnil (ii)); + CHECK (0 == *ii); + ++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"); + + ii = NIL_Sequence; + CHECK ( isnil (ii)); + CHECK (!ii); + } + + + + /** @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 + * 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 () + { + typedef ChainedIters<NumberSequence> Chain; + + Chain ci = iterChain (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 (!iterChain (NIL_Sequence)); + + // Iterator chaining "flattens" one level of packaging + NumberSequence s9 = seq(9); + ci = iterChain (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 = iterChain (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 std::vector<NumberSequence> IterContainer; + typedef RangeIter<IterContainer::iterator> IterIter; + + typedef ChainedIters<IterIter> 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_back (s5); + srcIters.push_back (s7); + srcIters.push_back (s9); + + IterIter iti = eachElm(srcIters); + CHECK (!isnil (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 (1 == *s5); // unaffected of course... + CHECK (5 == *s7); + CHECK (8 == *s9); + + ++++chain; + CHECK (4 == *chain); + ++chain; + CHECK (5 == *chain); // switch over to contents of 2nd iterator + ++++++++chain; + CHECK (9 == *chain); + + ++chain; + CHECK (isnil(chain)); + VERIFY_ERROR (ITER_EXHAUST, *chain ); + VERIFY_ERROR (ITER_EXHAUST, ++chain ); + } + + + + /** @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 build a functional datastructure here, on the fly, while exploring it. + * The \c exploreChildren(m) function generates this tree like datastructure: + * 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 e.g. with a root node 30, this scheme yields: + * \code + * ( 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. + * + * \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 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 + * 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) + * + * @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 () + { + 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"); + } + + + + + /** @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 () + { + 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"); + } + + + + /** @test a variation of recursive exploration, this time directly + * relying on the result set iterator type to provide the re-integration + * 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 IterExplorer<iter_explorer::WrappedSequence<NumberSeries> + ,iter_explorer::RecursiveSelfIntegration> SelfIntegratingExploration; + + NumberSeries root = elements(10,20,30); + SelfIntegratingExploration exploration(root); + string explorationResult = materialise (exploration >>= exploreChildren); + 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"); + } + + + + /** @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 + * 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 () + { + // IterExplorer as such is an iterator wrapping the source sequence + string result = materialise (exploreIter(seq(5))); + CHECK (result == "0-1-2-3-4"); + + // now, if the source sequence yields exactly one element 5... + result = materialise (exploreIter(seq(5,6))); + CHECK (result == "5"); + + // 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"); + + // 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"); + + // 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 + explode (uint top) + { + return seq(0,top); + } + }; + + + + LAUNCHER (IterExplorer_test, "unit common"); + + +}} // namespace lib::test + 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 <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/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<int> 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-source-test.cpp b/tests/lib/iter-source-test.cpp index 694556be8..046333fbf 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; @@ -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); - } }; diff --git a/tests/lib/iter-stack-test.cpp b/tests/lib/iter-stack-test.cpp new file mode 100644 index 000000000..9802b84f8 --- /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 <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/iter-stack.hpp" + + + +namespace lib { +namespace test{ + + using ::Test; + using util::isnil; + using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST; + + + + + typedef IterStack<int> IStack; + + + + + /********************************************************************* + * @test cover behaviour of a convenience class built by using a + * \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 + * - 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 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"); diff --git a/tests/lib/linked-elements-test.cpp b/tests/lib/linked-elements-test.cpp new file mode 100644 index 000000000..63655aa62 --- /dev/null +++ b/tests/lib/linked-elements-test.cpp @@ -0,0 +1,449 @@ +/* + LinkedElements(Test) - verify the intrusive single linked list template + + 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/allocation-cluster.hpp" +#include "lib/linked-elements.hpp" +#include "lib/test/testdummy.hpp" +#include "lib/iter-source.hpp" + +#include <tr1/memory> + + + +namespace lib { +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; + + inline void __triggerErrorAt(int i) { exception_trigger = i; } + inline void __triggerError_reset() { exception_trigger =-1; } + + + /** + * Test-Element, supporting intrusive linked list storage. + * Also tracks ctor/dtor calls by virtue of the Dummy baseclass. + */ + struct Nummy + : Dummy + { + Nummy* next; + + Nummy() + : Dummy() + , next(0) + { } + + explicit + 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<uint I> + 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<Nummy> + { + 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) + { + return n*(n+1) / 2; + } + + }//(End) subversive test data + + + + + + /// default case: ownership for heap allocated nodes + typedef LinkedElements<Nummy> List; + + /// managing existing node elements without taking ownership + typedef LinkedElements<Nummy, linked_elements::NoOwnership> ListNotOwner; + + /// creating nodes in-place, using a custom allocator for creation and disposal + typedef LinkedElements<Nummy, linked_elements::UseAllocationCluster> ListCustomAllocated; + + + + /******************************************************************** + * @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 + { + + virtual void + run (Arg) + { + simpleUsage(); + iterating(); + + verify_nonOwnership(); + verify_ExceptionSafety(); + populate_by_iterator(); + verify_RAII_safety(); + verify_customAllocator(); + } + + + void + simpleUsage() + { + CHECK (0 == Dummy::checksum()); + { + List elements; + CHECK (isnil (elements)); + CHECK (0 == elements.size()); + CHECK (0 == Dummy::checksum()); + + elements.pushNew<Nummy>(1); + elements.pushNew<Nummy>(2); + elements.pushNew<Nummy>(3); + elements.pushNew<Nummy>(4); + elements.pushNew<Nummy>(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<Nummy>(); + elements.pushNew<Nummy>(); + elements.pushNew<Nummy>(); + + 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<Nummy>(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 (ii == List::iterator()); + CHECK (cii == elements.end()); + CHECK (cii == List::const_iterator()); + VERIFY_ERROR (ITER_EXHAUST, ++ii ); + VERIFY_ERROR (ITER_EXHAUST, ++cii ); + + } + CHECK (0 == Dummy::checksum()); + } + + + /** @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() + { + 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<Nummy>(1); + elements.pushNew<Nummy>(2); + CHECK (1+2 == Dummy::checksum()); + + VERIFY_ERROR (PROVOKED_FAILURE, elements.pushNew<Nummy>(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()); + } + + + /** @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() + { + CHECK (0 == Dummy::checksum()); + + __triggerErrorAt(3); + Populator yieldSomeElements(NUM_ELEMENTS); + VERIFY_ERROR (PROVOKED_FAILURE, List elements(yieldSomeElements) ); + + CHECK (0 == Dummy::checksum()); + __triggerError_reset(); + } + + + void + verify_customAllocator() + { + CHECK (0 == Dummy::checksum()); + { + AllocationCluster allocator; + + ListCustomAllocated elements(allocator); + + elements.pushNew<Num<1> > (2); + elements.pushNew<Num<3> > (4,5); + elements.pushNew<Num<6> > (7,8,9); + + CHECK (sum(9) == Dummy::checksum()); + CHECK (3 == allocator.size()); + CHECK (1 == allocator.count<Num<1> >()); + CHECK (1 == allocator.count<Num<3> >()); + CHECK (1 == allocator.count<Num<6> >()); + + 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 (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()); + } + }; + + + + LAUNCHER (LinkedElements_test, "unit common"); + + +}} // namespace lib::test 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 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 diff --git a/tests/lib/meta/config-flags-test.cpp b/tests/lib/meta/config-flags-test.cpp index 572bc3adf..9319a4e39 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" @@ -52,285 +52,287 @@ using ::test::Test; using std::string; using std::cout; +using std::endl; -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { 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<uint 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 (); } - - - }; + 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 + }; + + + /** Register this test class... */ + LAUNCHER (ConfigFlags_test, "unit common"); + + + +}}} // namespace lib::meta::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/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 e024ff808..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. @@ -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" @@ -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..45a50a047 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; @@ -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 () { @@ -387,4 +384,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 new file mode 100644 index 000000000..6a5bbba88 --- /dev/null +++ b/tests/lib/meta/generator-combinations-test.cpp @@ -0,0 +1,140 @@ +/* + 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 lib { +namespace meta { +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 formatted ("-<%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 + : BASE + { + static string + visitAll() + { + T1 param1; + T2 param2; + return str(formatted % uint(param1) + % uint(param2) + % BASE::visitAll()); + } + }; + + template<> + 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 lib::meta::test diff --git a/tests/lib/meta/generator-test.cpp b/tests/lib/meta/generator-test.cpp index 31ca453ab..9e1583ed2 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 */ @@ -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)); } }; @@ -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 8c5b8b770..1dd45a6b2 100644 --- a/tests/lib/meta/iterable-classification-test.cpp +++ b/tests/lib/meta/iterable-classification-test.cpp @@ -26,9 +26,9 @@ #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" -#include "lib/lumitime.hpp" /////////////////////////////////////////////////////////////TODO draft #include <boost/type_traits/is_convertible.hpp> @@ -41,13 +41,13 @@ #include <set> -namespace lib { -namespace meta{ -namespace test{ +namespace lib { +namespace meta { +namespace test { - using lumiera::Time; - 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; using std::endl; diff --git a/tests/lib/meta/meta-utils-test.cpp b/tests/lib/meta/meta-utils-test.cpp new file mode 100644 index 000000000..50aa87a4c --- /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 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..0bfa8e3a9 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 { @@ -451,8 +451,8 @@ namespace test { /** Register this test class... */ - LAUNCHER (TypeTuple_test, "unit common"); + LAUNCHER (TypeTuple_test, "unit meta"); -}}} // 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 bd924b1d8..d55a84fe2 100644 --- a/tests/lib/meta/typelist-diagnostics.hpp +++ b/tests/lib/meta/typelist-diagnostics.hpp @@ -22,16 +22,19 @@ /** @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 - ** 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 @@ -50,15 +53,15 @@ using boost::format; using boost::enable_if; -namespace lumiera { -namespace typelist{ +namespace lib { +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_; } }; @@ -70,18 +73,18 @@ namespace typelist{ { 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; @@ -95,7 +98,7 @@ namespace typelist{ - namespace test { //< unit tests covering typelist manipulating templates + namespace test { // unit tests covering typelist manipulating templates namespace { // hidden internals for diagnostics.... using boost::format; @@ -127,7 +130,7 @@ namespace typelist{ 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 { @@ -166,7 +169,7 @@ namespace typelist{ } }; - 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 { @@ -198,14 +201,14 @@ 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; -}}} // 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 ce51270a5..46e3f784b 100644 --- a/tests/lib/meta/typelist-manip-test.cpp +++ b/tests/lib/meta/typelist-manip-test.cpp @@ -40,19 +40,18 @@ #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 <boost/format.hpp> #include <iostream> using ::test::Test; -using std::string; using std::cout; +using std::endl; -namespace lumiera { -namespace typelist{ +namespace lib { +namespace meta { namespace test { @@ -90,12 +89,13 @@ 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) { 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; + + 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)); + } + + void check_append () { @@ -352,8 +373,8 @@ namespace test { /** Register this test class... */ - LAUNCHER (TypeListManipl_test, "unit common"); + LAUNCHER (TypeListManip_test, "unit common"); -}}} // 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 new file mode 100644 index 000000000..576167a40 --- /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 lib { +namespace meta { +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 meta"); + + + +}}} // namespace lib::meta::test diff --git a/tests/lib/meta/typeseq-manip-test.cpp b/tests/lib/meta/typeseq-manip-test.cpp index 1221c610b..0874eae9f 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> @@ -48,11 +48,12 @@ using ::test::Test; using std::string; using std::cout; +using std::endl; -namespace lumiera { -namespace typelist{ -namespace test { +namespace lib { +namespace meta { +namespace test { namespace { // test data @@ -182,4 +183,4 @@ namespace test { -}}} // namespace lumiera::typelist::test +}}} // namespace lib::meta::test diff --git a/tests/lib/polymorphic-value-test.cpp b/tests/lib/polymorphic-value-test.cpp new file mode 100644 index 000000000..eacf30933 --- /dev/null +++ b/tests/lib/polymorphic-value-test.cpp @@ -0,0 +1,311 @@ +/* + 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-foreach.hpp" + +#include "lib/polymorphic-value.hpp" + +#include <vector> + + +namespace lib { +namespace test{ + + using ::Test; + using util::for_each; + using util::unConst; + using util::isSameObject; + using lumiera::error::LUMIERA_ERROR_ASSERTION; + + + 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) const + { + return unConst(this)->localSum() + == unConst(o).localSum(); + } + }; + + + const uint MAX_RAND = 1000; + 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 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]; + + ~Imp() + { + mark (-1 * localSum()); + CHECK (0 == localSum()); + } + + Imp() + { + REQUIRE (0 < ii); + localSum() = 0; + mark(ii); + ++ _created; + } + + Imp (Imp const& o) + { + ++ _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; + } + + private: + virtual long + apiFunc() + { + long rr = ii * (1 + rand() % MAX_RAND); + mark (rr); + _callSum += rr; + return rr; + } + + long& + localSum() + { + return localData_[ii-1]; + } + + void + mark (long markerValue) + { + localSum() += markerValue; + _checkSum += markerValue; + } + + void + copyData (Imp const& o) + { + for (uint i=0; i<ii; ++i) + localData_[i] = o.localData_[i]; + } + }; + + + /** 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 std::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; + + verifyBasics(); + + { + TestList objs = createOpaqueValues (); + for_each (objs, operate); + } + CHECK (0 == _checkSum); // all dead + + verifyOverrunProtection(); + verifyCopySupportDetectionMetafunctions(); + } + + + 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 + polyvalue::Trait<Interface>::ADMIN_OVERHEAD + _ALIGN_); + } + + + 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; + + 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(Holder) >= sizeof(ImpType)); + Api& embedded = val; + CHECK (isSameObject(embedded,val)); + 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<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 ); + } + }; + + + LAUNCHER (PolymorphicValue_test, "unit common"); + + +}} // namespace lib::test 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/scoped-collection-test.cpp b/tests/lib/scoped-collection-test.cpp new file mode 100644 index 000000000..c2408d7dc --- /dev/null +++ b/tests/lib/scoped-collection-test.cpp @@ -0,0 +1,476 @@ +/* + 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); + } + + SubDummy() + : Dummy() + , trigger_(-1) + { } + }; + + + inline uint + sum (uint n) + { + return n*(n+1) / 2; + } + + }//(End) subversive test data + + + + + using util::isnil; + using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST; + + typedef ScopedCollection<Dummy, sizeof(SubDummy)> 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(); + verify_defaultPopulator(); + verify_iteratorPopulator(); + verify_embeddedCollection(); + } + + + void + simpleUsage() + { + CHECK (0 == Dummy::checksum()); + { + 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()); + + container.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()); + } + CHECK (0 == Dummy::checksum()); + } + + + void + iterating() + { + CHECK (0 == Dummy::checksum()); + { + CollD coll(50); + for (uint 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 + CollD const& const_coll (coll); + check = 0; + CollD::const_iterator cii = const_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 ); + + } + 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() + { + CHECK (0 == Dummy::checksum()); + { + + int rr = rand() % 100; + + 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) ); + + CHECK ( 2 == coll.size()); // the other objects survived + CHECK (sum == Dummy::checksum()); + + Dummy& d2 = coll.appendNew<SubDummy> (rr, rr+1); + CHECK (3 == coll.size()); + + CHECK (sum + rr == Dummy::checksum()); + + VERIFY_ERROR (CAPACITY, coll.appendNewElement()); + VERIFY_ERROR (CAPACITY, coll.appendNewElement()); + VERIFY_ERROR (CAPACITY, coll.appendNewElement()); + + CHECK (3 == coll.size()); + CHECK (sum + rr == Dummy::checksum()); + + + CHECK (d0.acc(11) == coll[0].getVal() + 11 ); + 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 ( 1 == 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) == rr + 44); + + } + 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()))); + } + + + /** @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; + } + }; + }; + + + + 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 b424ec685..3815d86a3 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,33 +85,33 @@ 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->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() ); - 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==checksum); + CHECK (0 == Dummy::checksum()); } @@ -119,27 +119,27 @@ namespace test{ void checkErrorHandling() { - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); { HO holder; - throw_in_ctor = true; + Dummy::activateCtorFailure(); try { create_contained_object (holder); - NOTREACHED (); + NOTREACHED ("expect failure in ctor"); } 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()); @@ -221,17 +221,17 @@ 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!=checksum); + CHECK (0 != Dummy::checksum()); - long value55 = maph[55]->add(0); - long currSum = checksum; + long value55 = maph[55]->acc(0); + 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..add399caa 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]); @@ -143,9 +143,9 @@ 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==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 48461e56b..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 { @@ -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(); } @@ -61,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); @@ -78,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); @@ -91,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) @@ -140,11 +142,49 @@ namespace test{ VERIFY_ERROR (ITER_EXHAUST, ++cii ); } - CHECK (0==checksum); + CHECK (0 == Dummy::checksum()); } + + void + detaching() + { + int id2, id3; + Dummy* extracted(0); + CHECK (0 == Dummy::checksum()); + { + VectD holder; + CHECK (0 == Dummy::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 < Dummy::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 < Dummy::checksum()); // not all dummies are dead + CHECK (id2 == Dummy::checksum()); // #2 is alive! + + extracted->setVal(id2+id3); + CHECK (id2+id3 == Dummy::checksum()); + + delete extracted; + CHECK (0 == Dummy::checksum()); + } }; + LAUNCHER (ScopedPtrVect_test, "unit common"); diff --git a/tests/lib/simple-allocator-test.cpp b/tests/lib/simple-allocator-test.cpp new file mode 100644 index 000000000..ea239d26d --- /dev/null +++ b/tests/lib/simple-allocator-test.cpp @@ -0,0 +1,168 @@ +/* + 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 <cstdlib> +#include <string> + + +namespace lib { +namespace test{ + + + using util::isSameObject; + 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 (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) + checksum_ -= crap_[i]; + } + }; + + typedef Types<DummyObj<1>,DummyObj<23>,string> SupportedTypes; + typedef SimpleAllocator<SupportedTypes, UseInstantiationCounting> 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 == allocator.numSlots<DummyObj<1> >()); + CHECK (0 == allocator.numSlots<DummyObj<23> >()); + CHECK (0 == allocator.numSlots<string>()); + CHECK (0 == checksum_); + } + }; + + + /** Register this test class... */ + LAUNCHER (SimpleAllocator_test, "unit common"); + + +}} // namespace lib::test 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<TestSingletonO> instance; + Singleton<TestSingletonO> 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); } }; 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/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()); 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/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/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/teststreamtypes.hpp b/tests/lib/teststreamtypes.hpp index fda471b74..db22fe9cb 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" { @@ -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/thread-local-test.cpp b/tests/lib/thread-local-test.cpp new file mode 100644 index 000000000..114ba4c05 --- /dev/null +++ b/tests/lib/thread-local-test.cpp @@ -0,0 +1,113 @@ +/* + 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 setup... + + const uint NUM_THREADS = 50; + const uint MAX_RAND = 5*1000*1000; + + + /** Subject of the test */ + 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 setup.... + + + + + + + + + + + /************************************************************************** + * @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/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/time/quantiser-basics-test.cpp b/tests/lib/time/quantiser-basics-test.cpp index e790d5f8b..a2656fa79 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)); + } } @@ -82,9 +88,9 @@ namespace test{ FixedFrameQuantiser fixQ(25); uint frames = (rand() % MAX_FRAMES); - FSecs dirt = (F25 / (rand() % DIRT_GRAIN)); + FSecs dirt = (F25 / (2 + 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-control-test.cpp b/tests/lib/time/time-control-test.cpp new file mode 100644 index 000000000..f7db3039f --- /dev/null +++ b/tests/lib/time/time-control-test.cpp @@ -0,0 +1,507 @@ +/* + 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/control.hpp" + +#include "lib/meta/generator-combinations.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> +#include <iostream> +#include <string> +#include <limits> + +using lib::test::showType; +using boost::lexical_cast; +using util::isnil; +using std::cout; +using std::endl; +using std::string; + + +namespace lib { +namespace time{ +namespace test{ + + namespace error = lumiera::error; + + using lib::ScopedHolder; + using proc::asset::meta::TimeGrid; + using lib::meta::Types; + using lib::meta::InstantiateChainedCombinations; + using error::LUMIERA_ERROR_UNCONNECTED; + + + + namespace { // Test setup and helpers.... + + inline string + pop (Arg arg) + { + if (isnil (arg)) return ""; + string entry = arg[0]; + arg.erase (arg.begin()); + return entry; + } + + + /** + * 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 + { + mutable + ScopedHolder<TI> received_; + + public: + TestListener() + { + received_.create (Time::ZERO); + } + + TestListener(TI const& initialValue) + { + received_.create (initialValue); + } + + void + operator() (TI const& changeValue) const + { + received_.clear(); + received_.create (changeValue); + } + + TI const& + receivedValue() const + { + 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 + * should be invoked. + * + * 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 + { + 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); + } + + + virtual void + run (Arg arg) + { + TimeValue o (random_or_get (pop(arg))); + TimeValue c (random_or_get (pop(arg))); + CHECK (c!=Time::ZERO && o != c, "unsuitable testdata"); + + // 25fps-grid, but with an time origin offset by 1/50sec + 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); + + verifyBasics(); + 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,2))); + CHECK (Time(500,10) == target); + + CHECK (follower.receivedValue() == Time::ZERO); + controller.connectChangeNotification (follower); + CHECK (follower.receivedValue() == Time(500,10)); + + controller (Offset(-Time(500,1))); + CHECK (Time(0,9) == target); + CHECK (Time(0,9) == follower.receivedValue()); + } + + + /** @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); + }; + + + namespace { // Implementation: Matrix of individual test combinations + + using lib::meta::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& someTime) + { + return someTime; + } + inline TimeValue + materialise (QuTime const& alignedTime) + { + PQuant grid(alignedTime); + return grid->materialise (alignedTime); + } + + + 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_PAL"); + } + }; + + + 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) + { + if (isDuration<TAR>()) + { + 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); + CHECK (target == materialise(change)); + } + else + { + CHECK (target != org); + CHECK (target == change); + } + } + + 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_as_change) + { + CHECK (target != org); + 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); + } + + + + template<class TAR> + void + ____verify_wasOffset (TAR const& target, TAR const& refState, Offset const& offset) + { + CHECK (target != refState); + CHECK (target == Time(refState)+offset); + } + + template<class TAR> + void + ____verify_wasOffsetBack (TAR const& target, TAR const& refState) + { + CHECK (target == refState); + } + + + template<class TAR> + void + ____verify_nudged (TAR const& target, TAR const& refState, int64_t offsetSteps) + { + 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 || !offsetSteps); + CHECK (target == Time (materialise(refState)) + + Offset(offsetSteps, FrameRate::PAL)); + } + + + template<class TAR, class SRC> + void + ____verify_notification (TAR const& target, TestListener<SRC> const& follower) + { + if (isDuration<SRC>()) + { + CHECK (materialise(target) == follower.receivedValue() + || 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) + { + 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()); + CHECK (targetDuration == follower.receivedValue().duration()); + } + + + + + 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 + performTestSequence(TimeValue const& org, TimeValue const& c) + { + cout << "Test-Case. Target=" << showType<TAR>() + << "\t <--feed--- " << showType<SRC>() + << endl; + + // test subject + 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); + + controller (change); + ____verify_wasChanged (target, org, change); + ____verify_notification(target,follower); + + TAR refState(target); + + Offset offset(c); + controller (offset); + ____verify_wasOffset (target, refState, offset); + controller (-offset); + ____verify_wasOffsetBack (target, refState); + ____verify_notification(target,follower); + + controller (0); + ____verify_nudged (target, refState, 0); + ____verify_notification(target,follower); + + controller (+1); + ____verify_nudged (target, refState, +1); + ____verify_notification(target,follower); + + controller (-2); + ____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, refState, -1LL + maxInt); + ____verify_notification(target,follower); + + controller (minInt); + ____verify_nudged (target, refState, -1LL + maxInt+minInt); + ____verify_notification(target,follower); + + + // tail recursion: further test combinations.... + BASE::performTestSequence(org,c); + } + }; + + struct IterationEnd + { + void performTestSequence(TimeValue const&, TimeValue const&) { } + }; + + }//(End)Implementation Test-case matrix + + + void + TimeControl_test::verifyMatrix_of_MutationCases (TimeValue const& origVal, TimeValue const& change) + { + 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 // template to be instantiated for each type + , IterationEnd > TestMatrix; + + TestMatrix().performTestSequence(origVal, change); + } + + + + + + /** Register this test class... */ + LAUNCHER (TimeControl_test, "unit common"); + + + +}}} // namespace lib::time::test diff --git a/tests/lib/time/time-formats-test.cpp b/tests/lib/time/time-formats-test.cpp index daf7f6ca1..0222a1eeb 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; @@ -44,7 +47,25 @@ namespace lib { namespace time{ namespace test{ - using asset::meta::TimeGrid; + using proc::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,10 +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"); + + // 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>()); } @@ -94,7 +159,7 @@ namespace test{ void checkSeconds () { - UNIMPLEMENTED ("verify seconds as timecode format"); + UNIMPLEMENTED ("verify fractional seconds as timecode format"); } @@ -122,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 @@ -148,7 +214,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 +227,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-mutation-test.cpp b/tests/lib/time/time-mutation-test.cpp new file mode 100644 index 000000000..2a4837fcb --- /dev/null +++ b/tests/lib/time/time-mutation-test.cpp @@ -0,0 +1,272 @@ +/* + 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/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 proc::asset::meta::TimeGrid; + + 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 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); + + mutate_by_Value (o, Time(c)); + mutate_by_Offset (o, Offset(c)); + mutate_quantised (o, qChange); + mutate_by_Increment(o, count); + } + + + void + mutate_by_Value(TimeValue original, Time newStart) + { + 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)))); + } + + + void + mutate_by_Offset (TimeValue original, Offset change) + { + 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); + } + + + void + mutate_quantised (TimeValue original, QuTime change) + { + 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>(); + PQuant quantiser(change); + Time materialised (quantiser->materialise(change)); + 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, 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 + + Time doublyQuantised (PQuant(funny)->materialise(funny)); + CHECK (doublyQuantised != materialised); + } + + + void + mutate_by_Increment (TimeValue original, int change) + { + 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 + PQuant testGrid = Quantiser::retrieve("test_grid"); + Offset distance (testGrid->timeOf(0), testGrid->timeOf(change)); + CHECK (distance == change * FrameRate::PAL.duration()); + CHECK (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. the quantised time's own grid is used + } + }; + + + /** Register this test class... */ + LAUNCHER (TimeMutation_test, "unit common"); + + + +}}} // namespace lib::time::test diff --git a/tests/lib/time/time-parsing-test.cpp b/tests/lib/time/time-parsing-test.cpp new file mode 100644 index 000000000..24ada115c --- /dev/null +++ b/tests/lib/time/time-parsing-test.cpp @@ -0,0 +1,215 @@ +/* + 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/timecode.hpp" +#include "lib/symbol.hpp" +#include "lib/util.hpp" + +using lib::Symbol; +using util::cStr; + + +namespace lib { +namespace time{ +namespace test{ + + using proc::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) + { + 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 () + { + 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); // 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_fail(); // rejected because of leading dot (ambiguity) + } + + + void + parseFractionalSeconds () + { + 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 + } + + + 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 diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp index 237ce7f8e..9cda288d3 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; /******************************************************** @@ -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); @@ -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/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index 9a1383e8b..59d71b7c2 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; @@ -71,11 +69,14 @@ namespace test{ checkBasicTimeValues (ref); checkMutableTime (ref); - checkTimeConveniance (ref); + checkTimeConvenience (ref); verify_invalidFramerateProtection(); createOffsets (ref); buildDuration (ref); buildTimeSpan (ref); + compareTimeSpan (Time(ref)); + relateTimeIntervals (ref); + verify_fractionalOffset(); } @@ -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); @@ -167,8 +168,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 +191,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)); @@ -233,7 +234,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 @@ -252,14 +260,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))); @@ -279,6 +287,28 @@ namespace test{ } + void + verify_fractionalOffset () + { + typedef boost::rational<int64_t> 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) { @@ -296,15 +326,117 @@ namespace test{ CHECK (theLength == Offset(org,five).abs()); Time endpoint = interval.end(); - TimeSpan successor (endpoint, Duration(Time(2))); + 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 (Time const& org) + { + 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); + 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 (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); + 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()); // 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 (span3y.end() < span3z.end()); + CHECK (Time(span3) < Time(span3z)); + } + + + void + relateTimeIntervals (TimeValue org) + { + Time oneSec(FSecs(1)); + + TimeSpan span1 (org, FSecs(2)); + TimeSpan span2 (oneSec + org, 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)); } }; 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; diff --git a/tests/lib/util-floordiv-test.cpp b/tests/lib/util-floordiv-test.cpp index 015faaf64..256498cef 100644 --- a/tests/lib/util-floordiv-test.cpp +++ b/tests/lib/util-floordiv-test.cpp @@ -22,9 +22,11 @@ #include "lib/test/run.hpp" +#include "lib/util-quant.hpp" #include "lib/util.hpp" #include <cmath> +#include <time.h> #include <vector> #include <iostream> #include <boost/format.hpp> @@ -90,6 +92,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 +114,12 @@ namespace test { { verifyBehaviour (); + verifyIntegerTypes<int>(); + verifyIntegerTypes<long>(); + verifyIntegerTypes<short>(); + verifyIntegerTypes<int64_t>(); + verifyIntegerTypes<long long int>(); + if (!isnil (arg)) runPerformanceTest(); } @@ -147,6 +156,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/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 <cmath> #include <iostream> @@ -36,6 +36,7 @@ using std::endl; using boost::format; using boost::lexical_cast; using lib::test::showType; +using util::floorwrap; namespace util { 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()); } }; 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 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 3c1957645..97d667435 100644 --- a/tests/library/test-time.c +++ b/tests/library/test-time.c @@ -21,11 +21,18 @@ typedef unsigned int uint; -#include "tests/test.h" +#include "lib/test/test.h" #include "lib/time.h" #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); 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) ) ); 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" 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/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/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/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/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 new file mode 100644 index 000000000..6aa5780ec --- /dev/null +++ b/uml/lumiera/133637 @@ -0,0 +1,760 @@ +format 74 +"Play" // ProcessingLayer::Play + revision 5 + modified_by 5 "hiv" + // class settings + //class diagram settings + 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_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 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 + 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 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 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_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 + //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 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 + + 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 "" + + 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" + 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-L" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 146309 "video-R" + 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-Y" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 148357 "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 + + 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_class_context_mode default show_msg_context_mode 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 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 + + 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/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/143877.diagram b/uml/lumiera/143877.diagram new file mode 100644 index 000000000..e39b7082b --- /dev/null +++ b/uml/lumiera/143877.diagram @@ -0,0 +1,88 @@ +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 356.7 107.5 2000 +end +classcanvas 128133 class_ref 176261 // Controller + 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 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 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 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 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 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 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 16.5 2000 324 218 +end +fragment 130181 "Model / Fixture" + xyzwh 299 239.2 1995 323 91 +end +fragment 130309 "Player" + 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 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.4 345.7 1995 606 208 +end +classcanvas 130693 class_ref 177285 // Feed + 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 + 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 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 183 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 105 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..6e70081c9 --- /dev/null +++ b/uml/lumiera/144005.diagram @@ -0,0 +1,94 @@ +format 74 + +classinstancecanvas 128005 classinstance_ref 145669 // + xyz 46.7 86.6 2000 show_context_mode no +end +classinstancecanvas 128133 classinstance_ref 145797 // + xyz 112.7 143.3 2000 show_context_mode no +end +classinstancecanvas 128261 classinstance_ref 145925 // + xyz 171.6 174.4 2000 show_context_mode no +end +classinstancecanvas 128389 classinstance_ref 146053 // + xyz 171.6 241.1 2000 show_context_mode no +end +classinstancecanvas 128517 classinstance_ref 146181 // video-L + xyz 242.4 195.5 2000 color lightgreen show_context_mode no +end +classinstancecanvas 128645 classinstance_ref 146309 // video-R + xyz 243.5 217.7 2000 color lightgreen show_context_mode no +end +classinstancecanvas 128773 classinstance_ref 146437 // sound-W + xyz 243.8 270 2000 color lightgreen show_context_mode no +end +classinstancecanvas 128901 classinstance_ref 146565 // sound-X + xyz 242.6 292.2 2000 color lightgreen show_context_mode no +end +classinstancecanvas 129029 classinstance_ref 146693 // sound-Y + xyz 242.6 314.4 2000 color lightgreen show_context_mode no +end +classinstancecanvas 132101 classinstance_ref 146821 // + xyz 425.6 270 2000 color lightblue show_context_mode no +end +classinstancecanvas 132229 classinstance_ref 146949 // + xyz 425.6 195.5 2000 color lightblue show_context_mode no +end +note 132357 "stateless (functional)" + xyzwh 347.7 140.1 2000 124 35 +note 132485 "stateful" + color verylightorange xyzwh 130.8 84.6 2000 72 36 +classinstancecanvas 132613 classinstance_ref 147077 // + xyz 237 24.4 2000 show_context_mode no +end +classinstancecanvas 132741 classinstance_ref 147205 // + xyz 237 47.7 2000 show_context_mode no +end +classinstancecanvas 132869 classinstance_ref 148357 // sound-Z + 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 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 144 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 131845 norel + geometry VH + 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 + 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 diff --git a/uml/lumiera/145157.diagram b/uml/lumiera/145157.diagram new file mode 100644 index 000000000..b051824fd --- /dev/null +++ b/uml/lumiera/145157.diagram @@ -0,0 +1,130 @@ +format 74 + +classinstance 128005 class_ref 178437 // Client + name "" xyz 53.1 3.7 2000 life_line_z 2000 +classinstance 128133 class_ref 176133 // OutputSlot + name "" xyz 285.4 5 2000 life_line_z 2000 +classinstance 128261 class_ref 178565 // DataSink + name "" xyz 143.6 27.9 2000 life_line_z 2000 +classinstance 128389 class_ref 178693 // BufferProvider + name "" xyz 471.5 5 2000 life_line_z 2000 +fragment 131845 "client side" + xyzwh 41.4 28.1 1995 156 279 +end +fragment 131973 "output implementation" + xyzwh 281.3 29.1 1995 262 277 +end +durationcanvas 128517 classinstance_ref 128005 // :Client + xyzwh 72 81.7 2010 11 52 +end +durationcanvas 129541 classinstance_ref 128005 // :Client + xyzwh 72 211.1 2010 11 41 +end +durationcanvas 129669 classinstance_ref 128261 // :DataSink + xyzwh 166 238 2010 11 25 +end +durationcanvas 129925 classinstance_ref 128133 // :OutputSlot + xyzwh 313 242.3 2010 11 63 + overlappingdurationcanvas 133637 + xyzwh 319 271.5 2020 11 28 + end +end +durationcanvas 130309 classinstance_ref 128389 // :BufferProvider + xyzwh 507 250.2 2010 11 26 +end +durationcanvas 131333 classinstance_ref 128133 // :OutputSlot + xyzwh 313 85.1 2010 11 34 +end +durationcanvas 132101 classinstance_ref 128261 // :DataSink + 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 166 169.1 2010 11 25 +end +durationcanvas 132869 classinstance_ref 128133 // :OutputSlot + xyzwh 313 173.5 2010 11 25 +end +durationcanvas 133125 classinstance_ref 128005 // :Client + xyzwh 72 211.5 2010 11 29 +end +durationcanvas 133381 classinstance_ref 128389 // :BufferProvider + xyzwh 507 180.2 2010 11 25 +end +durationcanvas 133893 classinstance_ref 128389 // :BufferProvider + 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 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 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 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 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 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 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 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 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 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 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 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 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..19335767f --- /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 155.04 38.2 2000 +end +fragment 128261 "output management" + 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 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 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 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.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 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 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 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 484.36 459.64 2000 +end +fragment 132997 "concrete output driver" + xyzwh 126.68 309.48 1995 464 204 +end +note 133125 "Client +front-end" + xyzwh 15.36 270.88 2000 75 43 +note 133381 "Knowledge specific to this external output and driver" + 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 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 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 +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 188 210 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 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 524.68 414 + 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 09a1aa0c4..0d5cb5160 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -1,11 +1,17 @@ -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 objectdiagram_ref 138885 // ModelAssetRelations 730 488 100 4 0 0 - active classdiagram_ref 142725 // Time flavours - 595 646 100 4 0 0 + classdiagram_ref 143877 // Player Entities + 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 + 643 590 92 4 0 0 end show_stereotypes selected @@ -13,31 +19,19 @@ selected open package_ref 128005 // design - class_ref 160389 // VirtualMedia - class_ref 136837 // Proc - class_ref 160901 // Timeline - 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> + 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 - expansionregion_ref 128133 // establish partitioning usecaseview_ref 128261 // config examples - classview_ref 128133 // Engine Workings - classdiagram_ref 142725 // Time flavours - class_ref 134917 // Time - class_ref 169221 // TimeGrid - classview_ref 128645 // Service Components - classview_ref 128266 // SmartPointers + + package_ref 128389 // RenderEngine + + package_ref 129157 // BackendLayer end end 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 f1dc5de9f..341a05212 100644 --- a/uml/lumiera/lumiera.prj +++ b/uml/lumiera/lumiera.prj @@ -1,6 +1,6 @@ -format 58 +format 74 "lumiera" - revision 68 + revision 74 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/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 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/renderengine.html b/wiki/renderengine.html
index 1985b9c83..399e8e00d 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
 
-
+
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
@@ -1077,6 +1077,103 @@ Please note the shortcomings and logical contradictions in the solution currentl
 * The current design rather places the implementation according to the roles of the involved entities, which causes some ping-pong on the implementation level. Especially the ScopeLocator singleton can be accessed multiple times. This is the usual clarity vs. performance tradeoff. Scope resolution is assumed rather to be //not performance critical.//
 
+
+
All rendering, transformations and output of media data requires using ''data buffers'' -- but the actual layout and handling of these buffers is closely related to the actual implementation of these operations. As we're relying heavily on external libraries and plug-ins for performing these 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
+* 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|BufferMetadata]]
+
+
+!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.
+
+
+
+
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.
+
+
+
+
+
the generic BufferProvider implementation exposes a service to attach and maintain additional metadata with individual buffers. Using this service is not mandatory -- a concrete buffer provider implementation may chose to maintain more specific metadata right on the implementation level, especially if more elaborate management is necessary within the implementation anyway (e.g. the frame index). We can expect most buffer provider implementations to utilise at least the generic buffer type id service though.
+
+!buffer types and descriptors
+Client code accesses buffer through [[smart buffer handles|BuffHandle]], including some kind of buffer type information, encoded into a type ID within the ''buffer descriptor''. These descriptors are used like prototypes, relating the type-~IDs hierarchically. Obviously, the most fundamental distinction is the BufferProvider in charge for that specific buffer. Below that, the next mandatory level of distinction is the ''buffer size''. In some cases, additional distinctions can be necessary. Each BufferProvider exposes a service to yield unique type ~IDs governed by such a hierarchical scheme.
+
+!state and metadata for individual buffers
+Beyond that, it can be necessary to associate at least a state flag with //individual buffers.// Doing so requires the buffer to be in //locked state,// otherwise it wouldn't be distinguishable as an separate entity (a client is able to access the buffer memory address only after "locking" this buffer). Especially when using a buffer provider in conjunction with an OutputSlot, these states and transitions are crucial for performing an orderly handover of generated data from the producer (render engine) to the consumer (external output sink).
+
+__Note__: while the API to access this service is uniform, conceptually there is a difference between just using the (shared) type information and associating individual metadata, like the buffer state. Type-~IDs, once allocated, will never be discarded (within the lifetime of an Lumiera application instance -- buffer associations aren't persistent). To the contrary, individual metadata //will be discarded,// when releasing the corresponding buffer. According to the ''prototype pattern'', individual metadata is treated as a one-way-off specialisation.
+
+
+
+
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding 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.
+
+!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. 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
+: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
+&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 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
+* there should be convenience functions to release all pending buffers, forwarding the release operation to the individual handles
+
+
//Building the fixture is actually at the core of the [[builder's operation|Builder]]//
 {{red{WIP as of 11/10}}} &rarr; see also the [[planning page|PlanningBuildFixture]]
@@ -1279,6 +1376,27 @@ 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
+*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 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
+
+
Background: #fefefd
 Foreground: #000
@@ -1308,8 +1426,8 @@ Commands are //defined// using a [[fluent API|http://en.wikipedia.org/wiki/Fluen
 &rarr; see CommandUsage
 
-
-
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
+
+
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.
@@ -1375,11 +1493,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.
 
-
+
[<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.
 
@@ -1546,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]]
@@ -1576,20 +1701,20 @@ 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]]
-
+
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]]
@@ -1597,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
 
@@ -1632,15 +1757,50 @@ 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.
-
-
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.
+
+
//Currently (5/2011) this page is used to collect and build up a coherent design for the player subsystem of Lumiera..//
+
+!Starting point
+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 [[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 [[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
+
+!!!!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 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.
+
 
-
+
+
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.
 
@@ -1655,7 +1815,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.
-
+
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
@@ -1679,7 +1839,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...}}}
 
@@ -1693,6 +1853,19 @@ When building the low-level model, the actual processing code is resolved and a Initially, only the parameter (descriptors) are present on the effect ~MObject, while the actual [[parameter providers|ParamProvider]] are created or wired (by the ConManager) on demand.
+
+
The primary interface used by the upper application layers to interact with the render engine, to create and manage ongoing [[calculation streams|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
+
+!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.
+
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.
                                                                                       
@@ -1735,8 +1908,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 PipeID as used at the corresponding ModelPort and the corresponding [[global pipe|GlobalPipe]] ("5.1 Audio master bus")
 
@@ -1801,15 +1976,25 @@ Some further details * a special case of this factory use is the [[Singleton]] factory, which is used a lot within the Proy-Layer code
-
-
a specially configured view -- joining together high-level and low-level model
+
+
A 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.//
 * 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]])
 * 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
 
 !{{red{WIP}}} Structure of the fixture
 [<img[Structure of the Fixture|draw/Fixture1.png]]
@@ -1823,12 +2008,97 @@ 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.
+: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.
+
+
+
+
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.
+
+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
+** 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
+* (re)building
+** 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
+** 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 down to implementation level, both with the clean-up and the render processes themselves
+
+
+
+
The Fixture &rarr; [[data structure|FixtureDatastructure]] acts as umbrella to hook up the elements of the render engine's processing nodes network (LowLevelModel).
+Each segment within the [[Segmentation]] of any timeline serves as ''extent'' or unit of memory management: it is built up completely during the corresponding build process and becomes immutable thereafter, finally to be discarded as a whole when superseded by a modified version of that segment (new build process) -- but only after all related render processes (&rarr; CalcStream) are known to be terminated.
+
+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 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 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 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 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?
+# 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 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. 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 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 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.
 
@@ -1868,6 +2138,35 @@ 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 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
+* 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 (''frame coordinates''), comprised of
+** 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
+
+!!!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 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.//
+
+!!!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.
+
+
/***
 |Name|FullScreenPlugin|
@@ -1953,9 +2252,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
@@ -2092,7 +2391,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]]
@@ -2125,22 +2424,24 @@ 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]]
 
-
+
!Observations, Ideas, Proposals
 ''this page is a scrapbook for collecting ideas'' &mdash; please don't take anything noted here too literal. While writing code, I observe that I (ichthyo) follow certain informal guidelines, some of which I'd like to note down because they could evolve into general style guidelines for the Proc-Layer code.
 * ''Inversion of Control'' is the leading design principle.
 * but deliberately we stay just below the level of using Dependency Injection. Singletons and call-by-name are good enough. We're going to build //one// application, not any conceivable application.
-* write error handling code only if the error situation can be actually //handled// at this place. Otherwise, be prepared for exceptions just passing by and thus handle any resources by "resource acquisition is initialisation" (RAII). Remember: error handling defeats decoupling and encapsulation
+* write error handling code only if the error situation can be actually //handled// at this place. Otherwise, be prepared for exceptions just passing by and thus handle any resources by "resource acquisition is initialisation" (RAII). Remember: error handling defeats decoupling and encapsulation.
 * (almost) never {{{delete}}} an object directly, use {{{new}}} only when some smart pointer is at hand.
-* when user/client code is intended to create objects, make the ctor protected and provide a factory member called {{{create}}} instead, returning a smart pointer
+* clearly distinguish ''value objects'' from objects with ''reference semantics'', i.e. objects having a distict //object identity.//
+* when user/client code is intended to create reference-semantics objects, make the ctor protected and provide a factory member called {{{create}}} instead, returning a smart pointer
 * similarly, when we need just one instance of a given service, make the ctor protected and provide a factory member called {{{instance}}}, to be implemented by the lumiera::[[Singleton]] factory.
 * whenever possible, prefer this (lazy initialised [[Singleton]]) approach and avoid static initialisation magic
 * avoid doing anything non-local during the startup phase or shutdown phase of the application, especially avoid doing substantial work in any dtor.
 * avoid asuming anything that can't be enforced by types, interfaces or signatures; this means: be prepared for open possibilities
 * prefer {{{const}}} and initialisation code over assignment and active changes (inspired by functional programming)
+* code is written for ''being read by humans''; code shall convey its meaning //even to the casual reader.//
 
@@ -2496,6 +2797,20 @@ 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]]. 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|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
+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.
 &rarr; [[overfiew and technical details|LayerSeparationInterfaces]]
@@ -2558,8 +2873,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.
 
-
-
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:
+
+
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:
@@ -2859,23 +3174,23 @@ These are used as token for dealing with other objects and have no identity of t
 
 
-
+
Any point where output possibly might be produced. Model port entities are located within the [[Fixture]] &mdash; model port as a concept spans the high-level and low-level view. A model port can be associated both to a pipe in the HighLevelModel but at the same time denotes a set of corresponding [[exit nodes|ExitNode]] within the [[segments|Segmentation]] of the render nodes network.
 
-A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output destination and some other entity actually uses this designation as a target, either directly or indirectly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
+A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output destination, while some other entity at the same time actually //uses this designation as a target,// either directly or indirectly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
 
-Model ports are the effective, resulting outputs of each timeline; additional ports result from [[connecting a viewer|ViewerPlayConnection]]. Any render or display process happens at a model port.
+Model ports are the effective, resulting outputs of each timeline; additional ports result from [[connecting a viewer|ViewConnection]]. Any render or display process happens at a model port.
 
 !formal specification
 Model port is a //conceptual entity,// denoting the possibility to pull generated data of a distinct (stream)type from a specific bus within the model -- any possible output produced or provided by Lumiera is bound to appear at a model port. The namespace of model ports is global, each being associated with a ~Pipe-ID.
 
-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.
 
 
-
+
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
@@ -2905,7 +3220,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")
@@ -2916,28 +3231,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 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.
 
@@ -2960,21 +3274,33 @@ While the general approach and reasoning remains valid, a lot of the details loo In the most general case the render network may be just a DAG (not just a tree). Especially, multiple exit points may lead down to the same node, and following each of this possible paths the node may be at a different depth on each. This rules out a simple counter starting from the exit level, leaving us with the possibility of either employing a rather convoluted addressing scheme or using arbitrary ID numbers.{{red{...which is what we do for now}}}
-
-
The [[nodes|ProcNode]] are wired to form a "Directed Acyclic Graph"; each node knows its predecessor(s), but not its successor(s).  The RenderProcess is organized according to the ''pull principle'', thus we find an operation {{{pull()}}} at the core of this process. There is no such thing as an "engine object" calling nodes iteratively or table driven, rather, the nodes themselves issue recursive calls to their predecessor(s). For this to work, we need the nodes to adhere to a specific protocol:
-# Node is pulled, with a StateProxy object as parameter (encapsulating the access to the frames or buffers)
-# Node may now access current parameter values, using the state accessible via the StateProxy
-# using it's //input-output and wiring descriptor,// the Node creates a StateAdapter wrapping the StateProxy for allocating buffers and accessing the required input
-# StateAdapter might first try to get the output frames from the Cache in the Backend. In case of failure, a {{{process()}}} call is prepared by generating {{{pull()}}} call(s) for the input
-# as late as possible, typically on return, these recursive pull-calls have allocated a buffer containing the input data.
-# when input is ready prior to the {{{process()}}} call, output buffers will be allocated, either from the cache, or (if not caching) from the "parent" StateAdapter up the callstack.
-# after all buffers are available, the StateAdapter issues the {{{process()}}} call back to the originating node, which now may dereference the frame pointers and do its calculations
-# finally, when the {{{pull()}}} call returns, "parent" state originating the pull holds onto the buffers containing the calculated output result.
+
+
The [[nodes|ProcNode]] are wired to form a "Directed Acyclic Graph"; each node knows its predecessor(s), but not its successor(s).  The RenderProcess is organized according to the ''pull principle'', thus we find an operation {{{pull()}}} at the core of this process. Meaning that there isn't 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
+:# 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
 * 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]]
 &rarr; more fine grained [[implementation details|RenderImplDetails]]
@@ -3021,15 +3347,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.
 
-
+
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.
 
-
+
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.
 
@@ -3041,7 +3367,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
@@ -3053,21 +3379,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
 
 
@@ -3075,7 +3401,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.
@@ -3086,17 +3412,18 @@ While actually data frames are //pulled,// on a conceptual level data is assumed
 * first of all, we need to know //what to route// -- kind of the traits of the data. This is given by the //current pipe.//
 * then we'll need to determine an output designation //suitable for this data.// This is given by a "Plug" (WiringRequest) in the placement, and may be derived.
 * finally, this output designation will be //resolved// -- at least partially, resulting in a target pipe to be used for the wiring
-As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type (e.g. and audio output when the pipe currently in question deals with video) is irrelevant.
+As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type is irrelevant. (e.g. and audio output when the pipe currently in question deals with video)
 
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
 * 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
 
@@ -3105,30 +3432,62 @@ 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".
+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 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 timeouts gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
-&rarr; see also the PlayerDummy
+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
+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 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{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.
 
-
+
+
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
+
+!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
+* 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)
+
+
+
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.
 ;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 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
@@ -3143,11 +3502,158 @@ 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, 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.
 
+
+
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)
+
+!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. (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|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
+
+!!!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.
+
+!!!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....}}}
+
+!!!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
+
+
+
+
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.
+
+!!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.
+
+{{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 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....}}}
+
+-----
+!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. 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?
+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 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 --
+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
+# 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.
+
+&rarr; OutputSlotImpl
+
+
+
+
OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs. The actual output slot is a subclass object, created and managed from the "driver code" for a specific output connection. Moreover, each output slot 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.
+;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 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)  | · | ·         |
+|  {{{LOCKED}}}||↯|↷ emitted |✔|↷ emitted   | · |↷ free    |
+| {{{EMITTED}}}||↯|↷ blocked |↯|↷ blocked   | · |↷ free    |
+| {{{BLOCKED}}}||↯| ✗        |↯| ✗          | ∅ |↷ free    |
+where · means no operation, ✔ marks the standard cases (OK response to caller), ↯ will throw and thus kill the calling job, ∅ will treat this frame as //glitch,// ✗ will request playback stop.
+The rationale is for all states out-of-order to transition into the {{{BLOCKED}}}-state eventually, which, when hit by the next operation, will request playback stop.
+
+
The Lumiera Processing Layer is comprised of various subsystems and can be separated into a low-level and a high-level part. At the low-level end is the [[Render Engine|OverviewRenderEngine]] which basically is a network of render nodes cooperating closely with the Backend Layer in order to carry out the actual playback and media transforming calculations. Whereas on the high-level side we find several different [[Media Objects|MObjects]] that can be placed into the session, edited and manipulated. This is complemented by the [[Asset Management|Asset]], which is the "bookkeeping view" of all the different "things" within each [[Session|SessionOverview]].
 
@@ -3158,7 +3664,7 @@ There is rather strong separation between these two levels, and &mdash; <
 [img[Block Diagram|uml/fig128005.png]]
 
-
+
Render Engine, [[Builder]] and [[Controller]] are closely related Subsystems. Actually, the [[Builder]] //creates// a newly configured Render Engine //for every// RenderProcess. Before doing so, it queries from the Session (or, to be more precise, from the [[Fixture]] within the current Session) all necessary Media Object Placement information. The [[Builder]] then derives from this information the actual assembly of [[Processing Nodes|ProcNode]] comprising the Render Engine. Thus:
  * the source of the build process is a sequence of absolute (explicit) [[Placements|Placement]] called the [[Playlist]]
  * the [[build process|BuildProcess]] is driven, configured and controlled by the [[Controller]] subsystem component. It encompasses the actual playback configuration and State of the System.
@@ -3166,6 +3672,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]]
 
@@ -3724,12 +4231,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.
 
@@ -3755,6 +4262,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)
 
@@ -3805,7 +4320,7 @@ Placements have //value semantics,// i.e. we don't stress the identity of a plac
 
 
-
+
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).
 
@@ -3819,13 +4334,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.
 
@@ -4006,37 +4521,71 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T
 <<tasksum end>>
 
-
+
With //play process//&nbsp; we denote an ongoing effort to calculate a stream of frames for playback or rendering.
-The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls dispatched as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
+The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls [[dispatched|FrameDispatcher]] as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
 
-''Note'': the player is in no way engaged in any of the actual calculation and management tasks necessary to make this stream of calculations happen. The play process code contained within the player subsystem is largely comprised of organisational concerns and not especially performance critical.
-* the [[Backend]] is responsible for scheduling and dispatching the calculations
-* 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
+''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 [[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]] +!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|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. +
-
-
Within Lumiera, &raquo;Player&laquo; denotes the [[Subsystem]] responsible for organising and tracking //ongoing playback and render processes.// &rarr; [[PlayProcess]]
+
+
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
+
+!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 [[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.
+:for clients, this process can be accessed and maintained through a PlayController, which acts as (copyable) handle and front-end.
+;engine
+:the actual processing is done by the RenderEngine, which in itself is a compound of several services within [[Backend]] and Proc-Layer
+:any details of this processing remain opaque for the clients; even the player subsystem just accesses the EngineFaçade
+
+
+
+
Within Lumiera, &raquo;Player&laquo; 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 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.
 
-!{{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.
 
-
-
__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]]
@@ -4049,7 +4598,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.
@@ -4122,8 +4671,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>> |
@@ -4164,7 +4713,7 @@ The system is ''open'' inasmuch every part mirrors the structure of correspondin
 &rarr; see RenderProcess
 
-
+
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
@@ -4178,7 +4727,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.
@@ -4193,18 +4742,18 @@ Viewed as a micro program, the processing patterns are ''weak typed'' &mdash
 
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
 
-
+
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.
@@ -4594,10 +5143,10 @@ config.macros.rssFeedUpdate = {
 //}}}
 
-
+
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).
@@ -4614,28 +5163,32 @@ 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 
 
-
+
The [[Render Engine|Rendering]] only carries out the low-level and performance critical tasks. All configuration and decision concerns are to be handled by [[Builder]] and [[Controller]]. While the actual connection of the Render Nodes can be highly complex, basically each Segment of the Timeline with uniform characteristics is handled by one Processor, which is a graph of [[Processing Nodes|ProcNode]] discharging into a ExitNode. The Render Engine Components as such are //stateless// themselves; for the actual calculations they are combined with a StateProxy object generated by and connected internally to the [[Controller]], while at the same time holding the Data Buffers (Frames) for the actual calculations.
 
+{{red{Warning: obsolete as of 9/11}}}
+Currently the Render/Playback is beeing targetted for implementation; almost everything in this diagram will be implemented in a slightly differently way....
 [img[Entities comprising the Render Engine|uml/fig128389.png]]
 
-
+
Below are some notes regarding details of the actual implementation of the render process and processing node operation. In the description of the [[render node operation protocol|NodeOperationProtocol]] and the [[mechanics of the render process|RenderMechanics]], these details were left out deliberately.
+{{red{WIP as of 9/11 -- need to mention the planning phase more explicitly}}}
 
 !Layered structure of State
 State can be seen as structured like an onion. All the [[StateAdapter]]s in one call stack are supposed to be within one layer: they all know of a "current state", which in turn is a StateProxy (and thus may refer yet to another state, maybe accros the network or in the backend or whatever). The actual {{{process()}}} function "within" the individual nodes just sees a single StateAdapter and thus can be thought to be a layer below.
 
 !Buffer identification
-For the purpose of node operation, Buffers are identified by a //Buffer-handle,// which contains both the actual buffer pointer and an internal indes and classification of the source providing the buffer; the latter information is used for deallocation. Especially for calling the {{{process()}}} function (which is supposed to be plain C) the respective StateAdapter provides an array containing just the output and input buffer pointers
+For the purpose of node operation, Buffers are identified by a [[buffer-handle|BuffHandle]], which contains both the actual buffer pointer and an internal indes and classification of the source providing the buffer; the latter information is used for deallocation. Especially for calling the {{{process()}}} function (which is supposed to be plain C) the node invocation needs to prepare and provide an array containing just the output and input buffer pointers. Typically, this //frame pointer array//&nbsp; is allocated on the call stack.
 
 !Problem of multi-channel nodes
-Some data processors simply require to work on multiple channels simultanously, while others work just on a single channel and will be replicated by the builder for each channel invoved. Thus, we are struck with the nasty situation that the node graph may go through some nodes spanning the chain of several channels. Now the decision is //not to care for this complexity within a single chain calculating a single channel.// We rely solely on the cache to avoid duplicated calculations. When a given node happens to produce multiple output buffers, we are bound to allocate them for the purpose of this nodes {{{process()}}} call, but we just "let go" the buffers not needed immediately for the channel acutally to be processed. For this to work, it is supposed that the builder has wired in a caching, and that the cache will hit when we touch the same node again for the other channels.
+Some data processors simply require to work on multiple channels simultanously, while others work just on a single channel and will be replicated by the builder for each channel invoved. Thus, we are struck with the nasty situation that the node graph may go through some nodes spanning the chain of several channels. Now the decision is //not to care for this complexity within a single chain calculating a single channel.// We rely solely on the cache to avoid duplicated calculations. When a given node happens to produce multiple output buffers, we are bound to allocate them for the purpose of this node's {{{process()}}} call, but afterwards we'r just "letting go", releasing the buffers not needed immediately for the channel acutally to be processed. For this to work, it is supposed that the builder has wired in a caching, and that the cache will hit when we touch the same node again for the other channels.
 
 Closely related to this is the problem how to number and identify nodes and thus to be able to find calculated frames in cache (&rarr; [[here|NodeFrameNumbering]])
 
@@ -4648,23 +5201,48 @@ 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" StaeProxy.
-* 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):@@
 
-
+
+
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}}}
 
 !requirements
 * operations should be "in place" as much as possible
@@ -4678,6 +5256,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.
@@ -4715,8 +5295,8 @@ __see also__
 &rarr; the protocol [[how to operate the nodes|NodeOperationProtocol]]
 
-
-
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.
+
+
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.
 
 
@@ -4731,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.
 
@@ -4750,6 +5334,21 @@ Later on we expect a distinct __query subsystem__ to emerge, presumably embeddin
 
 &rarr; QuantiserImpl
+
+
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
+: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
+
+!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.
+
+
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.
 
@@ -4802,24 +5401,24 @@ 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
 ** 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
 
-!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
 :&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 (&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
@@ -4832,12 +5431,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
-# during the build process, there is a collection of placements; this can be discarded afterwards
+# 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 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
 # 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]]
 
@@ -4853,8 +5454,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]]
-
-
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]].
+
+
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
@@ -4869,6 +5470,9 @@ The Session object is a singleton &mdash; actually it is a »~PImpl«-Facade
 &rarr; see [[relation of timeline, sequences and objects|TimelineSequences]]
 
+
+
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.
+
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.
@@ -4983,7 +5587,7 @@ Currently, I'm planning to modify MObjectRef to return only a const ref to the u
 <<<
 
-
+
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
@@ -4996,7 +5600,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.
 
@@ -5028,8 +5632,8 @@ As detailed above, {{{Session::current}}} exposes the management / lifecycle API
 {{red{none of the above is implemented as of 11/09}}}
 
-
-
The Session contains all informations, state and objects to be edited by the User (&rarr;[[def|Session]]).
+
+
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]].
 
@@ -5064,14 +5668,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.
 
-
+
<<<
 {{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).
 
@@ -5213,14 +5817,14 @@ if (oldText.indexOf("SplashScreen")==-1)
 }
 //}}}
-
-
A small (in terms of storage) and specifically configured StateProxy object which is created on the stack for each individual {{{pull()}}} call. It is part of the invocation state of such a call and participates in the buffer management. Thus, in a calldown sequence of {{{pull()}}} calls we get a corresponding sequence of "parent" states. At each level, the &rarr; WiringDescriptor of the respective node defines a Strategy how the call is passed on.
+
+
A small (in terms of storage) and specifically configured StateProxy object which is created on the stack {{red{Really on the stack? 9/11}}} for each individual {{{pull()}}} call. It is part of the invocation state of such a call and participates in the buffer management. Thus, in a calldown sequence of {{{pull()}}} calls we get a corresponding sequence of "parent" states. At each level, the &rarr; WiringDescriptor of the respective node defines a Strategy how the call is passed on.
-
+
An Object representing a //Render Process// and containing associated state information.
-* 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
 
@@ -5231,7 +5835,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,...}}}).
@@ -5244,7 +5848,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, data reduced ~MP3 versus full quality linear PCM...
 
@@ -5423,13 +6027,14 @@ Instead, we should try to just connect the various subsystems via Interfaces and * to shield the rendering code of all complexities of thread communication and synchronization, we use the StateProxy
-
+
Structural Assets are intended mainly for internal use, but the user should be able to see and query them. They are not "loaded" or "created" directly, rather they //leap into existence // by creating or extending some other structures in the session, hence the name. Some of the structural Asset parametrisation can be modified to exert control on some aspects of the Proc Layer's (default) behaviour.
 * [[Processing Patterns|ProcPatt]] encode information how to set up some parts of the render network to be created automatically: for example, when building a clip, we use the processing pattern how to decode and pre-process the actual media data.
 * [[Tracks|Track]] are one of the dimensions used for organizing the session data. They serve as an Anchor to attach parametrisation of output pipe, overlay mode etc. By [[placing|Placement]] to a track, a media object inherits placement properties from this track.
 * [[Pipes|Pipe]] form &mdash; at least as visible to the user &mdash; the basic building block of the render network, because the latter appears to be a collection of interconnected processing pipelines. (this is the //outward view; // in fact the render network consists of [[nodes|ProcNode]] and is [[built|Builder]] from the Pipes, clips, effects...)[>img[Asset Classess|uml/fig131205.png]]
 * [[Sequence]] assets act as a façade to the fundamental compound building blocks within the model, a sequence being a collection of clips placed onto a tree of tracks. Sequences, as well as the top-level tracks enclosed will be created automatically on demand. Of course you may create them deliberately. Without binding it to a timeline or meta-clip, a sequence remains invisible.
-* [[Timeline]] assets are the top level structures to access the model; similar to the sequences, they act as façade to relevant parts of the model (BindingMO) and will be created on demand, alongside with a new session if necessary, bound to the new timeline. Likewise, they can be referred by their name-ID 
+* [[Timeline]] assets are the top level structures to access the model; similar to the sequences, they act as façade to relevant parts of the model (BindingMO) and will be created on demand, alongside with a new session if necessary, bound to the new timeline. Likewise, they can be referred by their name-ID
+* [[Viewer|ViewerAsset]] assets correspond to the available viewer elements in the GUI. By [[connecting to a viewer|ViewConnection]], session elements derive a concrete (physical) output and gain the ability to be [[played|PlayService]].
 
 
 
@@ -5615,6 +6220,14 @@ h1,h2,h3,h4,h5,h6 {
 /*}}}*/
 
+
+
Viewers for displaying output are connected to the timelines or other elements to be displayed through a ViewConnection. This creates an additional mapping step (&rarr; BindingMO), similar to the attachment of a sequence or virtual clip to the [[global busses|GlobalBus]]. Yet the viewers don't provide such an elaborate mixing desk like view as the timelines -- rather there is only a simplified version of the global busses, exposed to the user as ''switch board''.
+
+The corresponding GUI control will allow to choose among possible data sources (esp. ProbePoint) for any given StreamType (more precisely, for every //prototype,// e.g. image, sound). Moreover, there are some (limited) overlay capabilities (split image, mix sound). By default, this additional control will likely be hidden, as there is a default 1:1 association between the master busses of the timeline and the usable output destinations.
+
+!Model and Interfaces
+Regarding the internal wiring of components, the Viewer with a switch board behaves similar as a timeline: At the output side, there are a small number of standard OutputDesignation elements for the selection of primary kinds of media handled within this session (typically Video and Audio). But contrary to a timeline, a Viewer element also exposes an OutputManager interface, which is backed by a specific OutputMapping element, which internally connects to the main OutputManager for the whole Application. This way, a Viewer has the capability to get a PlayController
+
<<timeline better:true maxDays:55 maxEntries:45>>
@@ -6618,8 +7231,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]]
-
-
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.
+
+
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 +7244,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.
@@ -6661,61 +7274,82 @@ 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 |
-| Offset|Time ||offset the value |
-|~|Duration ||adjust by offset |
-|~|~TimeSpan ||adjust start/length |
+| Duration|~TimeSpan ||set length |
+|~|Duration  ||set length |
+|~|quantised || ↯ |
+| Offset|Time || -- |
+|~|Duration ||adjust length |
+|~|~TimeSpan ||adjust start |
 |~|quantised ||adjust raw value |
-| ~QuTime|any raw ||treat as raw |
-|~|~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. 
 * 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.//
+* 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
+
+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
+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 {{{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.
 :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.
+: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 informations get discarded.
-:Note that quantisation introduces an ''time origin'' and a ''reference scale''
+: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.
@@ -6723,10 +7357,10 @@ Question is: how fine grained and configurable needs this to be?
 :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
@@ -6743,15 +7377,15 @@ __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]]
 
-
+
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
@@ -6771,6 +7405,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.
@@ -6788,12 +7424,17 @@ 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.
+
+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
+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]]
 * how to integrate [[modifications of quantised values|TimeMutation]].
@@ -6801,7 +7442,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]]
 
-
+
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
@@ -6818,7 +7459,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.
@@ -6847,15 +7488,16 @@ 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??}}}
 
-
+
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]]
 
@@ -6885,6 +7527,22 @@ 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}}}
+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?
+
+!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.
 
@@ -6977,7 +7635,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]])
 
-
+
//drafted service as of 4/10 &mdash; &rarr;[[implementation plans|TypedLookup]]//
 A registration service to associate object identities, symbolic identifiers and types.
 
@@ -7003,7 +7661,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.
 
@@ -7024,7 +7682,7 @@ Just an ''registration scheme'' should be implemented right now, working complet
 see [[implementation planning|TypedLookup]]
 
-
+
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
@@ -7035,7 +7693,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.
@@ -7076,14 +7734,29 @@ For now, as of 6/10, we use specialised QueryResolver instances explicitly and d
 &rarr; QueryRegistration
 
-
+
+
For any kind of playback to happen, timeline elements (or similar model objects) need to be attached to a Viewer element through a special kind of [[binding|BindingMO]], called a ''view connection''. In the most general case, this creates an additional OutputMapping (and in the typical standard case, this boils down to a 1:1 association, sending the master bus of each media kind to the standard OutputDesignation for that kind).
+
+Establishing a ~ViewConnection is prerequisite for creating or attaching an PlayController through the PlayService. Multiple "play control" GUI elements can be associated with such a play controller, causing them to work as being linked together: if you e.g. push "play" on one of them, the button states of all linked GUI controls will reflect the state change of the underlying play controller.
+
+View connections are part of the model and thus persistent. They can be created explicitly, or just derived by //allocating a viewer.// And a new view connection can push aside (and thus "break") an existing one from another timeline or model element. When a view connection is //broken,// any associated PlayProcess needs to be terminated (this is a blocking operation). Thus, at any time, there can be only one active view connection to a given viewer; here "active" means, that a PlayController has been hooked up, and the connection is ready for playback or rendering. But on the other hand, nothing prevents a timeline (or similar model object) to maintain multiple view connections -- consequently the actual playback position behaves as if associated with the view connection.
+
+
+
+
A [[structural Asset|StructAsset]] corresponding to a Viewer element in the GUI.
+
+These Viewer (or Monitor) elements play an enabling role for any output generation. In order to [[initiate playback|PlayService]], we need a 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.
+
+
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 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. 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.
 
@@ -7179,8 +7852,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
@@ -7188,24 +7861,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 can be categorised as yielding two kinds of connections:
-* 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 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 "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]])
 
-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 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
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.
-
-
- - - - - - - - - -