Merged changes in the trunk up to revision 35828.
authorTamito Kajiyama <rd6t-kjym@asahi-net.or.jp>
Sun, 27 Mar 2011 23:11:22 +0000 (23:11 +0000)
committerTamito Kajiyama <rd6t-kjym@asahi-net.or.jp>
Sun, 27 Mar 2011 23:11:22 +0000 (23:11 +0000)
Conflicts resolved:
source/blender/makesrna/RNA_enum_types.h

According to the changes in revision 35667, the Freestyle-related code
in release/scripts/ui/ was moved to release/scripts/startup/bl_ui/.

295 files changed:
1  2 
CMakeLists.txt
GNUmakefile
SConstruct
build_files/buildbot/master.cfg
build_files/buildbot/master_unpack.py
build_files/buildbot/slave_compile.py
build_files/buildbot/slave_pack.py
build_files/buildbot/slave_test.py
build_files/cmake/cmake_qtcreator_project.py
build_files/cmake/macros.cmake
build_files/cmake/packaging.cmake
build_files/package_spec/build_archive.py
build_files/scons/tools/btools.py
doc/python_api/examples/bpy.data.py
doc/python_api/sphinx_doc_gen.py
extern/bullet2/src/BulletSoftBody/btSoftBody.h
intern/ghost/CMakeLists.txt
intern/ghost/GHOST_C-api.h
intern/ghost/GHOST_ISystem.h
intern/ghost/SConscript
intern/ghost/intern/GHOST_C-api.cpp
intern/ghost/intern/GHOST_System.cpp
intern/ghost/intern/GHOST_System.h
intern/ghost/intern/GHOST_SystemPathsX11.cpp
intern/ghost/intern/GHOST_SystemX11.cpp
intern/ghost/intern/GHOST_WindowX11.cpp
intern/itasc/Scene.cpp
release/scripts/io/netrender/__init__.py
release/scripts/io/netrender/balancing.py
release/scripts/io/netrender/client.py
release/scripts/io/netrender/master.py
release/scripts/io/netrender/master_html.py
release/scripts/io/netrender/model.py
release/scripts/io/netrender/netrender.css
release/scripts/io/netrender/netrender.js
release/scripts/io/netrender/operators.py
release/scripts/io/netrender/repath.py
release/scripts/io/netrender/slave.py
release/scripts/io/netrender/thumbnail.py
release/scripts/io/netrender/ui.py
release/scripts/io/netrender/utils.py
release/scripts/io/netrender/versioning.py
release/scripts/modules/addon_utils.py
release/scripts/modules/animsys_refactor.py
release/scripts/modules/bpy/utils.py
release/scripts/modules/bpyml.py
release/scripts/modules/rna_info.py
release/scripts/modules/sys_info.py
release/scripts/presets/interaction/blender.py
release/scripts/presets/interaction/maya.py
release/scripts/presets/keyconfig/maya.py
release/scripts/startup/bl_operators/__init__.py
release/scripts/startup/bl_operators/animsys_update.py
release/scripts/startup/bl_operators/object.py
release/scripts/startup/bl_operators/uvcalc_lightmap.py
release/scripts/startup/bl_operators/vertexpaint_dirt.py
release/scripts/startup/bl_operators/wm.py
release/scripts/startup/bl_ui/properties_data_armature.py
release/scripts/startup/bl_ui/properties_data_curve.py
release/scripts/startup/bl_ui/properties_data_mesh.py
release/scripts/startup/bl_ui/properties_physics_cloth.py
release/scripts/startup/bl_ui/properties_physics_fluid.py
release/scripts/startup/bl_ui/properties_render.py
release/scripts/startup/bl_ui/properties_texture.py
release/scripts/startup/bl_ui/space_info.py
release/scripts/startup/bl_ui/space_nla.py
release/scripts/startup/bl_ui/space_userpref.py
release/scripts/startup/bl_ui/space_userpref_keymap.py
release/scripts/startup/bl_ui/space_view3d.py
release/scripts/templates/addon_add_object.py
release/scripts/templates/background_job.py
source/blender/blenkernel/BKE_animsys.h
source/blender/blenkernel/BKE_blender.h
source/blender/blenkernel/BKE_depsgraph.h
source/blender/blenkernel/BKE_mesh.h
source/blender/blenkernel/BKE_object.h
source/blender/blenkernel/BKE_screen.h
source/blender/blenkernel/intern/CCGSubSurf.c
source/blender/blenkernel/intern/DerivedMesh.c
source/blender/blenkernel/intern/anim.c
source/blender/blenkernel/intern/anim_sys.c
source/blender/blenkernel/intern/blender.c
source/blender/blenkernel/intern/cdderivedmesh.c
source/blender/blenkernel/intern/constraint.c
source/blender/blenkernel/intern/depsgraph.c
source/blender/blenkernel/intern/effect.c
source/blender/blenkernel/intern/ipo.c
source/blender/blenkernel/intern/lattice.c
source/blender/blenkernel/intern/mesh.c
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/particle_system.c
source/blender/blenkernel/intern/screen.c
source/blender/blenkernel/intern/texture.c
source/blender/blenlib/BLI_editVert.h
source/blender/blenlib/BLI_math_rotation.h
source/blender/blenlib/BLI_string.h
source/blender/blenlib/BLI_utildefines.h
source/blender/blenlib/intern/math_base_inline.c
source/blender/blenlib/intern/math_color.c
source/blender/blenlib/intern/math_geom.c
source/blender/blenlib/intern/math_matrix.c
source/blender/blenlib/intern/math_rotation.c
source/blender/blenlib/intern/math_vector.c
source/blender/blenlib/intern/math_vector_inline.c
source/blender/blenlib/intern/string.c
source/blender/blenlib/intern/uvproject.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/collada/AnimationImporter.cpp
source/blender/collada/CMakeLists.txt
source/blender/collada/DocumentImporter.cpp
source/blender/collada/DocumentImporter.h
source/blender/collada/EffectExporter.cpp
source/blender/collada/EffectExporter.h
source/blender/collada/ExtraHandler.cpp
source/blender/collada/ExtraHandler.h
source/blender/collada/ExtraTags.cpp
source/blender/collada/ExtraTags.h
source/blender/collada/GeometryExporter.cpp
source/blender/collada/InstanceWriter.cpp
source/blender/collada/LightExporter.cpp
source/blender/collada/LightExporter.h
source/blender/collada/SConscript
source/blender/editors/animation/anim_channels_edit.c
source/blender/editors/animation/anim_draw.c
source/blender/editors/animation/fmodifier_ui.c
source/blender/editors/animation/keyframes_draw.c
source/blender/editors/animation/keyframes_edit.c
source/blender/editors/animation/keyframing.c
source/blender/editors/animation/keyingsets.c
source/blender/editors/armature/armature_intern.h
source/blender/editors/armature/armature_ops.c
source/blender/editors/armature/editarmature.c
source/blender/editors/armature/poseSlide.c
source/blender/editors/armature/poselib.c
source/blender/editors/armature/poseobject.c
source/blender/editors/curve/curve_ops.c
source/blender/editors/curve/editcurve.c
source/blender/editors/curve/editfont.c
source/blender/editors/gpencil/drawgpencil.c
source/blender/editors/gpencil/gpencil_edit.c
source/blender/editors/include/ED_keyframes_draw.h
source/blender/editors/include/ED_sequencer.h
source/blender/editors/include/UI_interface.h
source/blender/editors/interface/interface.c
source/blender/editors/interface/interface_draw.c
source/blender/editors/interface/interface_handlers.c
source/blender/editors/interface/interface_intern.h
source/blender/editors/interface/interface_layout.c
source/blender/editors/interface/interface_panel.c
source/blender/editors/interface/interface_regions.c
source/blender/editors/interface/interface_templates.c
source/blender/editors/interface/interface_widgets.c
source/blender/editors/interface/resources.c
source/blender/editors/interface/view2d.c
source/blender/editors/interface/view2d_ops.c
source/blender/editors/mesh/editmesh_lib.c
source/blender/editors/mesh/editmesh_tools.c
source/blender/editors/mesh/mesh_ops.c
source/blender/editors/metaball/mball_edit.c
source/blender/editors/metaball/mball_ops.c
source/blender/editors/object/object_add.c
source/blender/editors/object/object_edit.c
source/blender/editors/object/object_relations.c
source/blender/editors/object/object_transform.c
source/blender/editors/object/object_vgroup.c
source/blender/editors/physics/particle_edit.c
source/blender/editors/physics/physics_fluid.c
source/blender/editors/physics/physics_ops.c
source/blender/editors/render/render_internal.c
source/blender/editors/render/render_shading.c
source/blender/editors/screen/area.c
source/blender/editors/screen/glutil.c
source/blender/editors/screen/screen_edit.c
source/blender/editors/screen/screen_ops.c
source/blender/editors/sculpt_paint/paint_image.c
source/blender/editors/sculpt_paint/paint_ops.c
source/blender/editors/sculpt_paint/paint_stroke.c
source/blender/editors/sculpt_paint/paint_utils.c
source/blender/editors/sculpt_paint/paint_vertex.c
source/blender/editors/sculpt_paint/sculpt.c
source/blender/editors/space_action/action_ops.c
source/blender/editors/space_file/file_draw.c
source/blender/editors/space_file/filelist.c
source/blender/editors/space_file/filesel.c
source/blender/editors/space_graph/graph_edit.c
source/blender/editors/space_graph/graph_intern.h
source/blender/editors/space_graph/graph_ops.c
source/blender/editors/space_image/image_buttons.c
source/blender/editors/space_image/image_draw.c
source/blender/editors/space_image/image_ops.c
source/blender/editors/space_image/space_image.c
source/blender/editors/space_info/info_ops.c
source/blender/editors/space_logic/logic_window.c
source/blender/editors/space_nla/nla_draw.c
source/blender/editors/space_nla/nla_edit.c
source/blender/editors/space_node/node_draw.c
source/blender/editors/space_node/space_node.c
source/blender/editors/space_sequencer/sequencer_edit.c
source/blender/editors/space_sequencer/sequencer_ops.c
source/blender/editors/space_text/text_draw.c
source/blender/editors/space_text/text_ops.c
source/blender/editors/space_time/space_time.c
source/blender/editors/space_view3d/drawarmature.c
source/blender/editors/space_view3d/drawmesh.c
source/blender/editors/space_view3d/drawobject.c
source/blender/editors/space_view3d/space_view3d.c
source/blender/editors/space_view3d/view3d_buttons.c
source/blender/editors/space_view3d/view3d_draw.c
source/blender/editors/space_view3d/view3d_edit.c
source/blender/editors/space_view3d/view3d_fly.c
source/blender/editors/space_view3d/view3d_header.c
source/blender/editors/space_view3d/view3d_select.c
source/blender/editors/space_view3d/view3d_snap.c
source/blender/editors/space_view3d/view3d_view.c
source/blender/editors/transform/transform.c
source/blender/editors/transform/transform_generics.c
source/blender/editors/transform/transform_input.c
source/blender/editors/transform/transform_manipulator.c
source/blender/editors/transform/transform_ops.c
source/blender/editors/util/ed_util.c
source/blender/editors/uvedit/uvedit_draw.c
source/blender/ikplugin/intern/itasc_plugin.cpp
source/blender/imbuf/intern/divers.c
source/blender/imbuf/intern/imageprocess.c
source/blender/imbuf/intern/rectop.c
source/blender/imbuf/intern/scaling.c
source/blender/imbuf/intern/tiff.c
source/blender/makesdna/DNA_anim_types.h
source/blender/makesdna/DNA_scene_types.h
source/blender/makesdna/DNA_windowmanager_types.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/RNA_enum_types.h
source/blender/makesrna/SConscript
source/blender/makesrna/intern/CMakeLists.txt
source/blender/makesrna/intern/SConscript
source/blender/makesrna/intern/rna_access.c
source/blender/makesrna/intern/rna_actuator.c
source/blender/makesrna/intern/rna_define.c
source/blender/makesrna/intern/rna_internal_types.h
source/blender/makesrna/intern/rna_key.c
source/blender/makesrna/intern/rna_particle.c
source/blender/makesrna/intern/rna_rna.c
source/blender/makesrna/intern/rna_scene.c
source/blender/makesrna/intern/rna_scene_api.c
source/blender/makesrna/intern/rna_space.c
source/blender/makesrna/intern/rna_wm.c
source/blender/modifiers/intern/MOD_build.c
source/blender/modifiers/intern/MOD_cast.c
source/blender/modifiers/intern/MOD_edgesplit.c
source/blender/modifiers/intern/MOD_hook.c
source/blender/modifiers/intern/MOD_meshdeform.c
source/blender/modifiers/intern/MOD_particleinstance.c
source/blender/modifiers/intern/MOD_particlesystem.c
source/blender/modifiers/intern/MOD_screw.c
source/blender/modifiers/intern/MOD_simpledeform.c
source/blender/modifiers/intern/MOD_smooth.c
source/blender/modifiers/intern/MOD_uvproject.c
source/blender/modifiers/intern/MOD_wave.c
source/blender/nodes/intern/CMP_nodes/CMP_image.c
source/blender/python/generic/mathutils.c
source/blender/python/generic/mathutils_Matrix.c
source/blender/python/generic/mathutils_Quaternion.c
source/blender/python/generic/mathutils_Vector.c
source/blender/python/generic/mathutils_geometry.c
source/blender/python/generic/py_capi_utils.c
source/blender/python/generic/py_capi_utils.h
source/blender/python/intern/bpy.c
source/blender/python/intern/bpy_interface.c
source/blender/python/intern/bpy_operator.c
source/blender/python/intern/bpy_rna.c
source/blender/render/extern/include/RE_pipeline.h
source/blender/render/intern/include/render_types.h
source/blender/render/intern/source/envmap.c
source/blender/render/intern/source/occlusion.c
source/blender/render/intern/source/pipeline.c
source/blender/render/intern/source/render_texture.c
source/blender/windowmanager/intern/wm_event_system.c
source/blender/windowmanager/intern/wm_files.c
source/blender/windowmanager/intern/wm_operators.c
source/blender/windowmanager/intern/wm_subwindow.c
source/blender/windowmanager/intern/wm_window.c
source/blenderplayer/bad_level_call_stubs/stubs.c
source/creator/CMakeLists.txt
source/creator/creator.c
source/gameengine/Converter/KX_BlenderSceneConverter.cpp
source/gameengine/Converter/KX_BlenderSceneConverter.h
source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
source/gameengine/Ketsji/KX_PyConstraintBinding.cpp
source/gameengine/Ketsji/KX_PythonInit.cpp
source/gameengine/Ketsji/KX_PythonInit.h
source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp
source/tests/CMakeLists.txt
source/tests/bl_run_operators.py
source/tests/pep8.py

diff --cc CMakeLists.txt
index b2d02ce5c3aeeffdbdf1b4432b922ea57322e531,d860d5252897e0c309ab7a9c9fa074fa1fb3edd8..81263095bf8acf16da0f39a50f4d77361e2f0bc7
@@@ -84,6 -84,6 +84,10 @@@ else(
        option(WITH_OPENMP        "Enable OpenMP (has to be supported by the compiler)" OFF)
  endif()
  
++if(UNIX AND NOT APPLE)
++      option(WITH_X11_XINPUT "Enable X11 Xinput (tablet support)"     ON)
++endif()
++
  # Modifiers
  option(WITH_MOD_FLUID         "Enable Elbeem Modifier (Fluid Simulation)" ON)
  option(WITH_MOD_DECIMATE              "Enable Decimate Modifier" ON)
@@@ -107,11 -107,11 +111,7 @@@ endif(
  
  # 3D format support
  # disable opencollada on non-apple unix because opencollada has no package for debian
--if(UNIX AND NOT APPLE)
--      option(WITH_OPENCOLLADA         "Enable OpenCollada Support (http://www.opencollada.org)"       OFF)
--else()
--      option(WITH_OPENCOLLADA         "Enable OpenCollada Support (http://www.opencollada.org)"       OFF)
--endif()
++option(WITH_OPENCOLLADA               "Enable OpenCollada Support (http://www.opencollada.org)"       OFF)
  
  # Sound output
  option(WITH_SDL           "Enable SDL for sound and joystick support" ON)
@@@ -384,7 -384,7 +384,11 @@@ if(UNIX AND NOT APPLE
        mark_as_advanced(X11_XF86keysym_INCLUDE_PATH)
  
        # OpenSuse needs lutil, ArchLinux not, for now keep, can avoid by using --as-needed
--      set(LLIBS "-lutil -lc -lm -lpthread -lstdc++ ${X11_X11_LIB} ${X11_Xinput_LIB}")
++      set(LLIBS "-lutil -lc -lm -lpthread -lstdc++ ${X11_X11_LIB}")
++
++      if(WITH_X11_XINPUT)
++              list(APPEND LLIBS ${X11_Xinput_LIB})
++      endif()
  
        if(CMAKE_SYSTEM_NAME MATCHES "Linux")
                if(NOT WITH_PYTHON_MODULE)
@@@ -1016,7 -1016,7 +1020,14 @@@ if(WITH_OPENMP
        mark_as_advanced(OpenMP_CXX_FLAGS)
  endif() 
  
+ #-----------------------------------------------------------------------------
++# Configure Python.
++
++if(WITH_PYTHON_MODULE)
++      add_definitions(-DPy_ENABLE_SHARED)
++endif() 
++
 +#-----------------------------------------------------------------------------
  # Extra compile flags
  
  if((NOT WIN32) AND (NOT MSVC))
diff --cc GNUmakefile
index 772cec3c47106e975b15fc96578aac79dd442d6a,772cec3c47106e975b15fc96578aac79dd442d6a..42d7c33e46b73c1025cd5e23ec2c0360b8c19143
@@@ -90,6 -90,6 +90,10 @@@ package_debian
  package_pacman:
        cd build_files/package_spec/pacman ; MAKEFLAGS="-j$(NPROCS)" makepkg --asroot
  
++package_archive:
++      cd $(BUILD_DIR) ; make -s package_archive
++      @echo archive in "$(BUILD_DIR)/release"
++
  # forward build targets
  test:
        cd $(BUILD_DIR) ; ctest . --output-on-failure
diff --cc SConstruct
index 49c5ae98249124474ab74177c9ac985d62ca4d48,49c5ae98249124474ab74177c9ac985d62ca4d48..583cc0b144be6d9a6ffafd6c5bd892a846732756
@@@ -669,7 -669,7 +669,14 @@@ if 'blenderlite' in B.targets
  
  Depends(nsiscmd, allinstall)
  
++buildslave_action = env.Action(btools.buildslave, btools.buildslave_print)
++buildslave_cmd = env.Command('buildslave_exec', None, buildslave_action)
++buildslave_alias = env.Alias('buildslave', buildslave_cmd)
++
++Depends(buildslave_cmd, allinstall)
++
  Default(B.program_list)
  
  if not env['WITHOUT_BF_INSTALL']:
          Default(installtarget)
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6913ed2dab007038773499a9a468e1db9329603f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,176 @@@
++# -*- python -*-
++# ex: set syntax=python:
++
++# <pep8 compliant>
++
++# Dictionary that the buildmaster pays attention to.
++c = BuildmasterConfig = {}
++
++# BUILD SLAVES
++#
++# We load the slaves and their passwords from a separator file, so we can have
++# this one in SVN.
++
++from buildbot.buildslave import BuildSlave
++import master_private
++
++c['slaves'] = []
++
++for slave in master_private.slaves:
++    c['slaves'].append(BuildSlave(slave['name'], slave['password']))
++
++# TCP port through which slaves connect
++
++c['slavePortnum'] = 9989
++
++# CHANGE SOURCES
++
++from buildbot.changes.svnpoller import SVNPoller
++
++c['change_source'] = SVNPoller(
++       'https://svn.blender.org/svnroot/bf-blender/trunk/',
++       pollinterval=1200)
++
++# BUILDERS
++#
++# The 'builders' list defines the Builders, which tell Buildbot how to perform a build:
++# what steps, and which slaves can execute them.  Note that any particular build will
++# only take place on one slave.
++
++from buildbot.process.factory import BuildFactory
++from buildbot.steps.source import SVN
++from buildbot.steps.shell import ShellCommand
++from buildbot.steps.shell import Compile
++from buildbot.steps.shell import Test
++from buildbot.steps.transfer import FileUpload
++# from buildbot.steps.transfer import FileDownload
++from buildbot.steps.master import MasterShellCommand
++from buildbot.config import BuilderConfig
++
++# add builder utility
++
++c['builders'] = []
++buildernames = []
++
++
++def add_builder(c, name, libdir, factory):
++    slavenames = []
++
++    for slave in master_private.slaves:
++        if name in slave['builders']:
++            slavenames.append(slave['name'])
++
++    if len(slavenames) > 0:
++        f = factory(name, libdir)
++        c['builders'].append(BuilderConfig(name=name, slavenames=slavenames, factory=f, category='blender'))
++        buildernames.append(name)
++
++# common steps
++
++
++def svn_step():
++    return SVN(baseURL='https://svn.blender.org/svnroot/bf-blender/%%BRANCH%%/blender', mode='update', defaultBranch='trunk', workdir='blender')
++
++
++def lib_svn_step(dir):
++    return SVN(name='lib svn', baseURL='https://svn.blender.org/svnroot/bf-blender/%%BRANCH%%/lib/' + dir, mode='update', defaultBranch='trunk', workdir='lib/' + dir)
++
++# generic builder
++
++
++def generic_builder(id, libdir=""):
++    filename = 'buildbot_upload_' + id + '.zip'
++    compile_script = '../blender/build_files/buildbot/slave_compile.py'
++    test_script = '../blender/build_files/buildbot/slave_test.py'
++    pack_script = '../blender/build_files/buildbot/slave_pack.py'
++    unpack_script = 'master_unpack.py'
++
++    f = BuildFactory()
++    f.addStep(svn_step())
++    if libdir != '':
++        f.addStep(lib_svn_step(libdir))
++
++    f.addStep(Compile(command=['python', compile_script, id]))
++    f.addStep(Test(command=['python', test_script, id]))
++    f.addStep(ShellCommand(name='package', command=['python', pack_script, id], description='packaging', descriptionDone='packaged'))
++    if id.find('cmake') != -1:
++        f.addStep(FileUpload(name='upload', slavesrc='buildbot_upload.zip', masterdest=filename, maxsize=100 * 1024 * 1024))
++    else:
++        f.addStep(FileUpload(name='upload', slavesrc='buildbot_upload.zip', masterdest=filename, maxsize=100 * 1024 * 1024, workdir='install'))
++    f.addStep(MasterShellCommand(name='unpack', command=['python', unpack_script, filename], description='unpacking', descriptionDone='unpacked'))
++    return f
++
++# builders
++
++add_builder(c, 'mac_x86_64_cmake', 'darwin-9.x.universal', generic_builder)
++add_builder(c, 'mac_i386_cmake', 'darwin-9.x.universal', generic_builder)
++add_builder(c, 'mac_ppc_cmake', 'darwin-9.x.universal', generic_builder)
++add_builder(c, 'linux_x86_64_cmake', '', generic_builder)
++add_builder(c, 'linux_x86_64_scons', '', generic_builder)
++add_builder(c, 'win32_scons', 'windows', generic_builder)
++
++# SCHEDULERS
++#
++# Decide how to react to incoming changes.
++
++# from buildbot.scheduler import Scheduler
++from buildbot.schedulers import timed
++
++c['schedulers'] = []
++#c['schedulers'].append(Scheduler(name="all", branch=None,
++#                                 treeStableTimer=None,
++#                                 builderNames=[]))
++#c['schedulers'].append(timed.Periodic(name="nightly",
++#                        builderNames=buildernames,
++#                        periodicBuildTimer=24*60*60))
++
++c['schedulers'].append(timed.Nightly(name='nightly',
++    builderNames=buildernames,
++    hour=3,
++    minute=0))
++
++# STATUS TARGETS
++#
++# 'status' is a list of Status Targets. The results of each build will be
++# pushed to these targets. buildbot/status/*.py has a variety to choose from,
++# including web pages, email senders, and IRC bots.
++
++c['status'] = []
++
++from buildbot.status import html
++from buildbot.status.web import authz
++
++authz_cfg = authz.Authz(
++    # change any of these to True to enable; see the manual for more
++    # options
++    gracefulShutdown=False,
++    forceBuild=True,  # use this to test your slave once it is set up
++    forceAllBuilds=False,
++    pingBuilder=False,
++    stopBuild=False,
++    stopAllBuilds=False,
++    cancelPendingBuild=False,
++)
++
++c['status'].append(html.WebStatus(http_port=8010, authz=authz_cfg))
++
++# PROJECT IDENTITY
++
++c['projectName'] = "Blender"
++c['projectURL'] = "http://www.blender.org"
++
++# the 'buildbotURL' string should point to the location where the buildbot's
++# internal web server (usually the html.WebStatus page) is visible. This
++# typically uses the port number set in the Waterfall 'status' entry, but
++# with an externally-visible host name which the buildbot cannot figure out
++# without some help.
++
++c['buildbotURL'] = "http://builder.blender.org/"
++
++# DB URL
++#
++# This specifies what database buildbot uses to store change and scheduler
++# state.  You can leave this at its default for all but the largest
++# installations.
++
++c['db_url'] = "sqlite:///state.sqlite"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f97e53384b0e613fcb9030dfaed2b37c26d55aeb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,121 @@@
++# ##### BEGIN GPL LICENSE BLOCK #####
++#
++#  This program is free software; you can redistribute it and/or
++#  modify it under the terms of the GNU General Public License
++#  as published by the Free Software Foundation; either version 2
++#  of the License, or (at your option) any later version.
++#
++#  This program is distributed in the hope that it will be useful,
++#  but WITHOUT ANY WARRANTY; without even the implied warranty of
++#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++#  GNU General Public License for more details.
++#
++#  You should have received a copy of the GNU General Public License
++#  along with this program; if not, write to the Free Software Foundation,
++#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++#
++# ##### END GPL LICENSE BLOCK #####
++
++# Runs on Buildbot master, to unpack incoming unload.zip into latest
++# builds directory and remove older builds.
++
++# <pep8 compliant>
++
++import os
++import shutil
++import sys
++import zipfile
++
++
++# extension stripping
++def strip_extension(filename):
++    extensions = '.zip', '.tar', '.bz2', '.gz', '.tgz', '.tbz', '.exe'
++    filename_noext, ext = os.path.splitext(filename)
++    if ext in extensions:
++        return strip_extension(filename_noext)  # may have .tar.bz2
++    else:
++        return filename
++
++
++# extract platform from package name
++def get_platform(filename):
++    # name is blender-version-platform.extension. we want to get the
++    # platform out, but there may be some variations, so we fiddle a
++    # bit to handle current and hopefully future names
++    filename = strip_extension(filename)
++
++    tokens = filename.split("-")
++    platforms = 'osx', 'mac', 'bsd', 'win', 'linux', 'source', 'irix', 'solaris'
++    platform_tokens = []
++    found = False
++
++    for i, token in enumerate(tokens):
++        if not found:
++            for platform in platforms:
++                if platform in token.lower():
++                    found = True
++                    break
++
++        if found:
++            platform_tokens += [token]
++
++    return '-'.join(platform_tokens)
++
++# get filename
++if len(sys.argv) < 2:
++    sys.stderr.write("Not enough arguments, expecting file to unpack\n")
++    sys.exit(1)
++
++filename = sys.argv[1]
++
++# open zip file
++if not os.path.exists(filename):
++    sys.stderr.write("File %r not found.\n" % filename)
++    sys.exit(1)
++
++try:
++    z = zipfile.ZipFile(filename, "r")
++except Exception, ex:
++    sys.stderr.write('Failed to open zip file: %s\n' % str(ex))
++    sys.exit(1)
++
++if len(z.namelist()) != 1:
++    sys.stderr.write("Expected one file in %r." % filename)
++    sys.exit(1)
++
++package = z.namelist()[0]
++packagename = os.path.basename(package)
++
++# detect platform
++platform = get_platform(packagename)
++
++if platform == '':
++    sys.stderr.write('Failed to detect platform from package: %r\n' % packagename)
++    sys.exit(1)
++
++# extract
++dir = 'public_html/download'
++
++try:
++    zf = z.open(package)
++    f = file(os.path.join(dir, packagename), "wb")
++
++    shutil.copyfileobj(zf, f)
++
++    zf.close()
++    z.close()
++
++    os.remove(filename)
++except Exception, ex:
++    sys.stderr.write('Failed to unzip package: %s\n' % str(ex))
++    sys.exit(1)
++
++# remove other files from the same platform
++try:
++    for f in os.listdir(dir):
++        if platform.lower() in f.lower():
++            if f != packagename:
++                os.remove(os.path.join(dir, f))
++except Exception, ex:
++    sys.stderr.write('Failed to remove old packages: %s\n' % str(ex))
++    sys.exit(1)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..54150a93e61dcbfdc344ff5b1fb3685af6cda31b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++# ##### BEGIN GPL LICENSE BLOCK #####
++#
++#  This program is free software; you can redistribute it and/or
++#  modify it under the terms of the GNU General Public License
++#  as published by the Free Software Foundation; either version 2
++#  of the License, or (at your option) any later version.
++#
++#  This program is distributed in the hope that it will be useful,
++#  but WITHOUT ANY WARRANTY; without even the implied warranty of
++#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++#  GNU General Public License for more details.
++#
++#  You should have received a copy of the GNU General Public License
++#  along with this program; if not, write to the Free Software Foundation,
++#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++#
++# ##### END GPL LICENSE BLOCK #####
++
++# <pep8 compliant>
++
++import os
++import subprocess
++import sys
++
++# get builder name
++if len(sys.argv) < 2:
++    sys.stderr.write("Not enough arguments, expecting builder name\n")
++    sys.exit(1)
++
++builder = sys.argv[1]
++
++# we run from build/ directory
++blender_dir = '../blender'
++
++if builder.find('cmake') != -1:
++    # cmake
++
++    # set build options
++    cmake_options = ['-DCMAKE_BUILD_TYPE:STRING=Release']
++
++    if builder == 'mac_x86_64_cmake':
++        cmake_options.append('-DCMAKE_OSX_ARCHITECTURES:STRING=x86_64')
++    elif builder == 'mac_i386_cmake':
++        cmake_options.append('-DCMAKE_OSX_ARCHITECTURES:STRING=i386')
++    elif builder == 'mac_ppc_cmake':
++        cmake_options.append('-DCMAKE_OSX_ARCHITECTURES:STRING=ppc')
++
++    # configure and make
++    retcode = subprocess.call(['cmake', blender_dir] + cmake_options)
++    if retcode != 0:
++        sys.exit(retcode)
++    retcode = subprocess.call(['make', '-s', '-j4', 'install'])
++    sys.exit(retcode)
++else:
++    # scons
++    os.chdir(blender_dir)
++    scons_options = []
++
++    retcode = subprocess.call(['python', 'scons/scons.py'] + scons_options)
++    sys.exit(retcode)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..74d00bf9249235f1bb0df8f55a0b4444dc7d1977
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,87 @@@
++# ##### BEGIN GPL LICENSE BLOCK #####
++#
++#  This program is free software; you can redistribute it and/or
++#  modify it under the terms of the GNU General Public License
++#  as published by the Free Software Foundation; either version 2
++#  of the License, or (at your option) any later version.
++#
++#  This program is distributed in the hope that it will be useful,
++#  but WITHOUT ANY WARRANTY; without even the implied warranty of
++#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++#  GNU General Public License for more details.
++#
++#  You should have received a copy of the GNU General Public License
++#  along with this program; if not, write to the Free Software Foundation,
++#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++#
++# ##### END GPL LICENSE BLOCK #####
++
++# <pep8 compliant>
++
++# Runs on buildbot slave, creating a release package using the build
++# system and zipping it into buildbot_upload.zip. This is then uploaded
++# to the master in the next buildbot step.
++
++import os
++import subprocess
++import sys
++import zipfile
++
++# get builder name
++if len(sys.argv) < 2:
++    sys.stderr.write("Not enough arguments, expecting builder name\n")
++    sys.exit(1)
++
++builder = sys.argv[1]
++
++# scons does own packaging
++if builder.find('scons') != -1:
++    os.chdir('../blender')
++    retcode = subprocess.call(['python', 'scons/scons.py', 'BF_QUICK=slnt', 'buildslave'])
++    sys.exit(retcode)
++
++# clean release directory if it already exists
++dir = 'release'
++
++if os.path.exists(dir):
++    for f in os.listdir(dir):
++        if os.path.isfile(os.path.join(dir, f)):
++            os.remove(os.path.join(dir, f))
++
++# create release package
++try:
++    subprocess.call(['make', 'package_archive'])
++except Exception, ex:
++    sys.stderr.write('Make package release failed' + str(ex) + '\n')
++    sys.exit(1)
++
++# find release directory, must exist this time
++if not os.path.exists(dir):
++    sys.stderr.write("Failed to find release directory.\n")
++    sys.exit(1)
++
++# find release package
++file = None
++filepath = None
++
++for f in os.listdir(dir):
++    rf = os.path.join(dir, f)
++    if os.path.isfile(rf) and f.startswith('blender'):
++        file = f
++        filepath = rf
++
++if not file:
++    sys.stderr.write("Failed to find release package.\n")
++    sys.exit(1)
++
++# create zip file
++try:
++    upload_zip = "buildbot_upload.zip"
++    if os.path.exists(upload_zip):
++        os.remove(upload_zip)
++    z = zipfile.ZipFile(upload_zip, "w", compression=zipfile.ZIP_STORED)
++    z.write(filepath, arcname=file)
++    z.close()
++except Exception, ex:
++    sys.stderr.write('Create buildbot_upload.zip failed' + str(ex) + '\n')
++    sys.exit(1)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f79ef7cf5c6a9d1cc5ca163c79bf216ac7a31673
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++# ##### BEGIN GPL LICENSE BLOCK #####
++#
++#  This program is free software; you can redistribute it and/or
++#  modify it under the terms of the GNU General Public License
++#  as published by the Free Software Foundation; either version 2
++#  of the License, or (at your option) any later version.
++#
++#  This program is distributed in the hope that it will be useful,
++#  but WITHOUT ANY WARRANTY; without even the implied warranty of
++#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++#  GNU General Public License for more details.
++#
++#  You should have received a copy of the GNU General Public License
++#  along with this program; if not, write to the Free Software Foundation,
++#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++#
++# ##### END GPL LICENSE BLOCK #####
++
++# <pep8 compliant>
++
++import subprocess
++import sys
++
++# get builder name
++if len(sys.argv) < 2:
++    sys.stderr.write("Not enough arguments, expecting builder name\n")
++    sys.exit(1)
++
++builder = sys.argv[1]
++
++# we run from build/ directory
++blender_dir = '../blender'
++
++if builder.find('cmake') != -1:
++    # cmake
++    retcode = subprocess.call(['ctest', '.' '--output-on-failure'])
++    sys.exit(retcode)
++else:
++    # scons
++    pass
index a6d6906d26decbb753cc12bbe1a9aee8c2f012d3,a6d6906d26decbb753cc12bbe1a9aee8c2f012d3..1bad1835434bd5ba0916efbc8528095697373a1c
  # along with this program; if not, write to the Free Software Foundation,
  # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  #
--# Contributor(s): Campbell Barton
++# Contributor(s): Campbell Barton, M.G. Kishalmi
  #
  # ***** END GPL LICENSE BLOCK *****
  
  # <pep8 compliant>
  
  """
--Exampel Win32 usage:
++Example Win32 usage:
   c:\Python32\python.exe c:\blender_dev\blender\build_files\cmake\cmake_qtcreator_project.py c:\blender_dev\cmake_build
++
++example linux usage
++ python .~/blenderSVN/blender/build_files/cmake/cmake_qtcreator_project.py ~/blenderSVN/cmake
  """
  
++import sys
  import os
  from os.path import join, dirname, normpath, abspath, splitext, relpath, exists
  
@@@ -37,6 -37,6 +41,20 @@@ base = abspath(base
  
  SIMPLE_PROJECTFILE = False
  
++# get cmake path
++CMAKE_DIR = sys.argv[-1]
++
++if not os.path.exists(os.path.join(CMAKE_DIR, "CMakeCache.txt")):
++    CMAKE_DIR = os.getcwd()
++if not os.path.exists(os.path.join(CMAKE_DIR, "CMakeCache.txt")):
++    print("CMakeCache.txt not found in %r or %r\n    Pass CMake build dir as an argument, or run from that dir, aborting" % (CMAKE_DIR, os.getcwd()))
++    sys.exit(1)
++
++
++# could be either.
++# PROJECT_DIR = base
++PROJECT_DIR = CMAKE_DIR
++
  
  def source_list(path, filename_check=None):
      for dirpath, dirnames, filenames in os.walk(path):
@@@ -62,6 -62,6 +80,16 @@@ def is_c_header(filename)
      return (ext in (".h", ".hpp", ".hxx"))
  
  
++def is_py(filename):
++    ext = splitext(filename)[1]
++    return (ext == ".py")
++
++
++def is_glsl(filename):
++    ext = splitext(filename)[1]
++    return (ext == ".glsl")
++
++
  def is_c(filename):
      ext = splitext(filename)[1]
      return (ext in (".c", ".cpp", ".cxx", ".m", ".mm", ".rc"))
@@@ -78,19 -78,19 +106,19 @@@ def is_svn_file(filename)
  
  
  def is_project_file(filename):
--    return (is_c_any(filename) or is_cmake(filename))  # and is_svn_file(filename)
++    return (is_c_any(filename) or is_cmake(filename) or is_glsl(filename))  # and is_svn_file(filename)
  
  
  def cmake_advanced_info():
      """ Extracr includes and defines from cmake.
      """
  
--    def create_eclipse_project(cmake_dir):
++    def create_eclipse_project(CMAKE_DIR):
          import sys
          if sys.platform == "win32":
--            cmd = 'cmake %r -G"Eclipse CDT4 - MinGW Makefiles"' % cmake_dir
++            cmd = 'cmake %r -G"Eclipse CDT4 - MinGW Makefiles"' % CMAKE_DIR
          else:
--            cmd = 'cmake %r -G"Eclipse CDT4 - Unix Makefiles"' % cmake_dir
++            cmd = 'cmake %r -G"Eclipse CDT4 - Unix Makefiles"' % CMAKE_DIR
  
          os.system(cmd)
  
      defines = []
  
      import os
--    import sys
--
--    cmake_dir = sys.argv[-1]
--
--    if not os.path.exists(os.path.join(cmake_dir, "CMakeCache.txt")):
--        cmake_dir = os.getcwd()
--    if not os.path.exists(os.path.join(cmake_dir, "CMakeCache.txt")):
--        print("CMakeCache.txt not found in %r or %r\n    Pass CMake build dir as an argument, or run from that dir, abording" % (cmake_dir, os.getcwd()))
--        sys.exit(1)
  
--    create_eclipse_project(cmake_dir)
++    create_eclipse_project(CMAKE_DIR)
  
      from xml.dom.minidom import parse
--    tree = parse(os.path.join(cmake_dir, ".cproject"))
++    tree = parse(os.path.join(CMAKE_DIR, ".cproject"))
      '''
      f = open(".cproject_pretty", 'w')
      f.write(tree.toprettyxml(indent="    ", newl=""))
      return includes, defines
  
  
--def main():
++def cmake_cache_var(var):
++    cache_file = open(os.path.join(CMAKE_DIR, "CMakeCache.txt"))
++    lines = [l_strip for l in cache_file for l_strip in (l.strip(),) if l_strip if not l_strip.startswith("//") if not l_strip.startswith("#")]
++    cache_file.close()
++
++    for l in lines:
++        if l.split(":")[0] == var:
++            return l.split("=", 1)[-1]
++    return None
++
++
++def cmake_compiler_defines():
++    compiler = cmake_cache_var("CMAKE_C_COMPILER")  # could do CXX too
++
++    if compiler is None:
++        print("Couldn't find the compiler, os defines will be omitted...")
++        return
++
++    import tempfile
++    temp_c = tempfile.mkstemp(suffix=".c")[1]
++    temp_def = tempfile.mkstemp(suffix=".def")[1]
++
++    os.system("%s -dM -E %s > %s" % (compiler, temp_c, temp_def))
++
++    temp_def_file = open(temp_def)
++    lines = [l.strip() for l in temp_def_file if l.strip()]
++    temp_def_file.close()
++
++    os.remove(temp_c)
++    os.remove(temp_def)
++
++    return lines
++
++
++def create_qtc_project_main():
      files = list(source_list(base, filename_check=is_project_file))
--    files_rel = [relpath(f, start=base) for f in files]
++    files_rel = [relpath(f, start=PROJECT_DIR) for f in files]
      files_rel.sort()
  
--    # --- qtcreator spesific, simple format
++    # --- qtcreator specific, simple format
      if SIMPLE_PROJECTFILE:
--        # --- qtcreator spesific, simple format
++        # --- qtcreator specific, simple format
          PROJECT_NAME = "Blender"
--        f = open(join(base, "%s.files" % PROJECT_NAME), 'w')
++        f = open(join(PROJECT_DIR, "%s.files" % PROJECT_NAME), 'w')
          f.write("\n".join(files_rel))
  
--        f = open(join(base, "%s.includes" % PROJECT_NAME), 'w')
++        f = open(join(PROJECT_DIR, "%s.includes" % PROJECT_NAME), 'w')
          f.write("\n".join(sorted(list(set(dirname(f) for f in files_rel if is_c_header(f))))))
  
--        qtc_prj = join(base, "%s.creator" % PROJECT_NAME)
++        qtc_prj = join(PROJECT_DIR, "%s.creator" % PROJECT_NAME)
          f = open(qtc_prj, 'w')
          f.write("[General]\n")
  
--        qtc_cfg = join(base, "%s.config" % PROJECT_NAME)
++        qtc_cfg = join(PROJECT_DIR, "%s.config" % PROJECT_NAME)
          if not exists(qtc_cfg):
              f = open(qtc_cfg, 'w')
              f.write("// ADD PREDEFINED MACROS HERE!\n")
          includes.sort()
  
          PROJECT_NAME = "Blender"
--        f = open(join(base, "%s.files" % PROJECT_NAME), 'w')
++        FILE_NAME = PROJECT_NAME.lower()
++        f = open(join(PROJECT_DIR, "%s.files" % FILE_NAME), 'w')
          f.write("\n".join(files_rel))
  
--        f = open(join(base, "%s.includes" % PROJECT_NAME), 'w')
++        f = open(join(PROJECT_DIR, "%s.includes" % FILE_NAME), 'w')
          f.write("\n".join(sorted(includes)))
  
--        qtc_prj = join(base, "%s.creator" % PROJECT_NAME)
++        qtc_prj = join(PROJECT_DIR, "%s.creator" % FILE_NAME)
          f = open(qtc_prj, 'w')
          f.write("[General]\n")
  
--        qtc_cfg = join(base, "%s.config" % PROJECT_NAME)
++        qtc_cfg = join(PROJECT_DIR, "%s.config" % FILE_NAME)
          f = open(qtc_cfg, 'w')
          f.write("// ADD PREDEFINED MACROS HERE!\n")
--        f.write("\n".join([("#define %s %s" % item) for item in defines]))
++        defines_final = [("#define %s %s" % item) for item in defines]
++        defines_final += cmake_compiler_defines()  # defines from the compiler
++        f.write("\n".join(defines_final))
  
--    print("Project file written to: %s" % qtc_prj)
++    print("Blender project file written to: %s" % qtc_prj)
      # --- end
  
++
++def create_qtc_project_python():
++    files = list(source_list(base, filename_check=is_py))
++    files_rel = [relpath(f, start=PROJECT_DIR) for f in files]
++    files_rel.sort()
++
++    # --- qtcreator specific, simple format
++    PROJECT_NAME = "Blender_Python"
++    FILE_NAME = PROJECT_NAME.lower()
++    f = open(join(PROJECT_DIR, "%s.files" % FILE_NAME), 'w')
++    f.write("\n".join(files_rel))
++
++    qtc_prj = join(PROJECT_DIR, "%s.creator" % FILE_NAME)
++    f = open(qtc_prj, 'w')
++    f.write("[General]\n")
++
++    qtc_cfg = join(PROJECT_DIR, "%s.config" % FILE_NAME)
++    if not exists(qtc_cfg):
++        f = open(qtc_cfg, 'w')
++        f.write("// ADD PREDEFINED MACROS HERE!\n")
++
++    print("Python project file written to:  %s" % qtc_prj)
++
++
++def main():
++    create_qtc_project_main()
++    create_qtc_project_python()
++
++
  if __name__ == "__main__":
      main()
index 2297cac8bb973b5579551f8afed55c0aaa8c9c6a,2297cac8bb973b5579551f8afed55c0aaa8c9c6a..073147266bb0a1247c554d083c180084a7a057aa
@@@ -66,7 -66,7 +66,7 @@@ macro(SETUP_LIBDIRS
  
        link_directories(${JPEG_LIBPATH} ${PNG_LIBPATH} ${ZLIB_LIBPATH} ${FREETYPE_LIBPATH})
  
--      if(WITH_PYTHON AND NOT WITH_PYTHON_MODULE)
++      if(WITH_PYTHON)  #  AND NOT WITH_PYTHON_MODULE  # WIN32 needs
                link_directories(${PYTHON_LIBPATH})
        endif()
        if(WITH_INTERNATIONAL)
@@@ -127,7 -127,7 +127,7 @@@ macro(setup_liblink
        target_link_libraries(${target} ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY} ${JPEG_LIBRARIES} ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${LLIBS})
  
        # since we are using the local libs for python when compiling msvc projects, we need to add _d when compiling debug versions
--      if(WITH_PYTHON AND NOT WITH_PYTHON_MODULE)
++      if(WITH_PYTHON)  # AND NOT WITH_PYTHON_MODULE  # WIN32 needs
                target_link_libraries(${target} ${PYTHON_LINKFLAGS})
  
                if(WIN32 AND NOT UNIX)
index b21c13f80b481adc119883e2a6b319a03b63d736,b21c13f80b481adc119883e2a6b319a03b63d736..ee07857db260552f56a83f3bb26d7bcf251d6b97
@@@ -51,7 -51,7 +51,35 @@@ if(APPLE
  
        # Libraries are bundled directly
        set(CPACK_COMPONENT_LIBRARIES_HIDDEN TRUE)
--endif(APPLE)
++endif()
  
  set(CPACK_PACKAGE_EXECUTABLES "blender")
  include(CPack)
++
++# Target for build_archive.py script, to automatically pass along
++# version, revision, platform, build directory
++macro(add_package_archive packagename extension)
++      set(build_archive python ${CMAKE_SOURCE_DIR}/build_files/package_spec/build_archive.py)
++      set(package_output ${CMAKE_BINARY_DIR}/release/${packagename}.${extension})
++
++      add_custom_target(package_archive DEPENDS ${package_output})
++
++      add_custom_command(
++              OUTPUT ${package_output}
++              COMMAND ${build_archive} ${packagename} ${extension} bin release
++              WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
++endmacro()
++
++if(APPLE)
++      add_package_archive(
++              "blender-${BLENDER_VERSION}-r${BUILD_REV}-OSX-${CMAKE_OSX_ARCHITECTURES}"
++              "zip")
++elseif(UNIX)
++      # platform name could be tweaked, to include glibc, and ensure processor is correct (i386 vs i686)
++      string(TOLOWER ${CMAKE_SYSTEM_NAME} PACKAGE_SYSTEM_NAME)
++
++      add_package_archive(
++              "blender-${BLENDER_VERSION}-r${BUILD_REV}-${PACKAGE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}"
++              "tar.bz2")
++endif()
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0329d16b1ec40609df251fca1691377209c47c31
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,70 @@@
++#!/usr/bin/python
++
++# <pep8 compliant>
++
++import os
++import shutil
++import subprocess
++import sys
++
++# todo:
++# strip executables
++
++# get parameters
++if len(sys.argv) < 5:
++    sys.stderr.write('Excepted arguments: ./build_archive.py name extension install_dir output_dir')
++    sys.exit(1)
++
++package_name = sys.argv[1]
++extension = sys.argv[2]
++install_dir = sys.argv[3]
++output_dir = sys.argv[4]
++
++package_archive = os.path.join(output_dir, package_name + '.' + extension)
++package_dir = package_name
++
++# remove existing package with the same name
++try:
++    if os.path.exists(package_archive):
++        os.remove(package_archive)
++    if os.path.exists(package_dir):
++        shutil.rmtree(package_dir)
++except Exception, ex:
++    sys.stderr.write('Failed to clean up old package files: ' + str(ex) + '\n')
++    sys.exit(1)
++
++# create temporary package dir
++try:
++    shutil.copytree(install_dir, package_dir)
++
++    for f in os.listdir(package_dir):
++        if f.startswith('makes'):
++            os.remove(os.path.join(package_dir, f))
++except Exception, ex:
++    sys.stderr.write('Failed to copy install directory: ' + str(ex) + '\n')
++    sys.exit(1)
++
++# create archive
++try:
++    if not os.path.exists(output_dir):
++        os.mkdir(output_dir)
++
++    if extension == 'zip':
++        archive_cmd = ['zip', '-9', '-r', package_archive, package_dir]
++    elif extension == 'tar.bz2':
++        archive_cmd = ['tar', 'cjf', package_archive, package_dir]
++    else:
++        sys.stderr.write('Unknown archive extension: ' + extension)
++        sys.exit(-1)
++
++    subprocess.call(archive_cmd)
++except Exception, ex:
++    sys.stderr.write('Failed to create package archive: ' + str(ex) + '\n')
++    sys.exit(1)
++
++# empty temporary package dir
++try:
++    shutil.rmtree(package_dir)
++except Exception, ex:
++    sys.stderr.write('Failed to clean up package directory: ' + str(ex) + '\n')
++    sys.exit(1)
index 4554cad055dd074f9bc84483c0779c5b25455a98,4554cad055dd074f9bc84483c0779c5b25455a98..17ee80e6dbd07f24c71805438f38f17f7a245811
@@@ -56,10 -56,10 +56,16 @@@ def get_version()
  
      raise Exception("%s: missing version string" % fname)
  
++def get_revision():
++    build_rev = os.popen('svnversion').read()[:-1] # remove \n
++    if build_rev == '' or build_rev==None: 
++        build_rev = 'UNKNOWN'
++
++    return 'r' + build_rev
  
  # This is used in creating the local config directories
  VERSION, VERSION_DISPLAY = get_version()
--
++REVISION = get_revision()
  
  def print_arguments(args, bc):
      if len(args):
@@@ -175,7 -175,7 +181,7 @@@ def print_targets(targs, bc)
  def validate_targets(targs, bc):
      valid_list = ['.', 'blender', 'blenderstatic', 'blenderplayer', 'webplugin',
              'blendernogame', 'blenderstaticnogame', 'blenderlite', 'release',
--            'everything', 'clean', 'install-bin', 'install', 'nsis']
++            'everything', 'clean', 'install-bin', 'install', 'nsis','buildslave']
      oklist = []
      for t in targs:
          if t in valid_list:
@@@ -496,6 -496,6 +502,67 @@@ def read_opts(env, cfg, args)
  
      return localopts
  
++def buildbot_zip(src, dest, package_name, extension):
++    import zipfile
++    ln = len(src)+1 # one extra to remove leading os.sep when cleaning root for package_root
++    flist = list()
++
++    # create list of tuples containing file and archive name
++    for root, dirs, files in os.walk(src):
++        package_root = os.path.join(package_name, root[ln:])
++        flist.extend([(os.path.join(root, file), os.path.join(package_root, file)) for file in files])
++
++    if extension == '.zip':
++        package = zipfile.ZipFile(dest, 'w', zipfile.ZIP_DEFLATED)
++        package.comment = package_name + ' is a zip-file containing the Blender software. Visit http://www.blender.org for more information.'
++        for entry in flist:
++            package.write(entry[0], entry[1])
++        package.close()
++    else:
++        import tarfile
++        package = tarfile.open(dest, 'w:bz2')
++        for entry in flist:
++            package.add(entry[0], entry[1], recursive=False)
++        package.close()
++    bb_zip_name = os.path.normpath(src + os.sep + '..' + os.sep + 'buildbot_upload.zip')
++    print("creating %s" % (bb_zip_name))
++    bb_zip = zipfile.ZipFile(bb_zip_name, 'w', zipfile.ZIP_DEFLATED)
++    print("writing %s to %s" % (dest, bb_zip_name))
++    bb_zip.write(dest, os.path.split(dest)[1])
++    bb_zip.close()
++    print("done.")
++
++def buildslave_print(target, source, env):
++    return "Running buildslave target"
++
++def buildslave(target=None, source=None, env=None):
++    """
++    Builder for buildbot integration. Used by buildslaves of http://builder.blender.org only.
++    """
++
++    if env['OURPLATFORM'] in ('win32-vc', 'win64-vc', 'win32-mingw'):
++        extension = '.zip'
++    else:
++        extension = '.tar.bz2'
++
++    outdir = os.path.abspath(env['BF_INSTALLDIR'])
++    package_name = 'blender-' + VERSION+'-'+REVISION + '-' + env['OURPLATFORM'].split('-')[0]
++    package_dir = os.path.normpath(outdir + os.sep + '..' + os.sep + package_name)
++    package_archive = os.path.normpath(outdir + os.sep + '..' + os.sep + package_name + extension)
++
++    try:
++        if os.path.exists(package_archive):
++            os.remove(package_archive)
++        if os.path.exists(package_dir):
++            shutil.rmtree(package_dir)
++    except Exception, ex:
++        sys.stderr.write('Failed to clean up old package files: ' + str(ex) + '\n')
++        return 1
++
++    buildbot_zip(outdir, package_archive, package_name, extension)
++
++    return 0
++
  def NSIS_print(target, source, env):
      return "Creating NSIS installer for Blender"
  
index 555b421ddac436585ad0dd354e46a28a48184050,555b421ddac436585ad0dd354e46a28a48184050..7d6bf94532b4c7907c445d49946a65a7af80f19b
@@@ -14,7 -14,7 +14,7 @@@ print(bpy.data.scenes.keys()
  if "Cube" in bpy.data.meshes:
      mesh = bpy.data.meshes["Cube"]
      print("removing mesh", mesh)
--    bpy.data.meshes.unlink(mesh)
++    bpy.data.meshes.remove(mesh)
  
  
  # write images into a file next to the blend
@@@ -22,6 -22,6 +22,6 @@@ import o
  file = open(os.path.splitext(bpy.data.filepath)[0] + ".txt", 'w')
  
  for image in bpy.data.images:
--    file.write("%s %dx%d\n" % (image.filepath, image.size[0], image.size[1]))
++    file.write("%s %d x %d\n" % (image.filepath, image.size[0], image.size[1]))
  
  file.close()
index 7bb5019ae13c3749e07c8296accf76ce155b5160,e51ea516158988640895023ebb0483a30c578f72..4ed7ae36e8606f0498ca9df144b7d7bbeb8e8212
@@@ -36,7 -36,7 +36,7 @@@ For HTML generatio
  
      sphinx-build doc/python_api/sphinx-in doc/python_api/sphinx-out
  
--  assuming that you have sphinx 0.6.7 installed
++  assuming that you have sphinx 1.0.7 installed
  
  For PDF generation
  ------------------
@@@ -61,7 -61,7 +61,7 @@@ else
          "bpy.app",
          "bpy.path",
          "bpy.data",
--        #"bpy.props",
++        "bpy.props",
          "bpy.utils",
          "bpy.context",
          # "bpy.types",  # supports filtering
@@@ -104,8 -102,8 +104,14 @@@ EXAMPLE_SET = set(
  EXAMPLE_SET_USED = set()
  
  _BPY_STRUCT_FAKE = "bpy_struct"
++_BPY_PROP_COLLECTION_FAKE = "bpy_prop_collection"
  _BPY_FULL_REBUILD = False
  
++if _BPY_PROP_COLLECTION_FAKE:
++    _BPY_PROP_COLLECTION_ID = ":class:`%s`" % _BPY_PROP_COLLECTION_FAKE
++else:
++    _BPY_PROP_COLLECTION_ID = "collection"
++
  
  def undocumented_message(module_name, type_name, identifier):
      if str(type_name).startswith('<module'):
@@@ -157,6 -155,6 +163,10 @@@ def example_extract_docstring(filepath)
      return "\n".join(text), line_no
  
  
++def write_title(fw, text, heading_char):
++    fw("%s\n%s\n\n" % (text, len(text) * heading_char))
++
++
  def write_example_ref(ident, fw, example_id, ext="py"):
      if example_id in EXAMPLE_SET:
  
@@@ -312,8 -310,8 +322,7 @@@ def pymodule2sphinx(BASEPATH, module_na
  
      fw = file.write
  
--    fw(title + "\n")
--    fw(("=" * len(title)) + "\n\n")
++    write_title(fw, title, "=")
  
      fw(".. module:: %s\n\n" % module_name)
  
@@@ -543,14 -535,14 +552,18 @@@ def pyrna2sphinx(BASEPATH)
          if is_return:
              id_name = "return"
              id_type = "rtype"
--            kwargs = {"as_ret": True, "class_fmt": ":class:`%s`"}
++            kwargs = {"as_ret": True}
              identifier = ""
          else:
              id_name = "arg"
              id_type = "type"
--            kwargs = {"as_arg": True, "class_fmt": ":class:`%s`"}
++            kwargs = {"as_arg": True}
              identifier = " %s" % prop.identifier
  
++        kwargs["class_fmt"] = ":class:`%s`"
++
++        kwargs["collection_id"] = _BPY_PROP_COLLECTION_ID
++
          type_descr = prop.get_type_description(**kwargs)
          if prop.name or prop.description:
              fw(ident + ":%s%s: %s\n" % (id_name, identifier, ", ".join(val for val in (prop.name, prop.description) if val)))
          else:
              title = struct.identifier
  
--        fw("%s\n%s\n\n" % (title, "=" * len(title)))
++        write_title(fw, title, "=")
  
          fw(".. module:: bpy.types\n\n")
  
          sorted_struct_properties.sort(key=lambda prop: prop.identifier)
  
          for prop in sorted_struct_properties:
--            type_descr = prop.get_type_description(class_fmt=":class:`%s`")
++            type_descr = prop.get_type_description(class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID)
              # readonly properties use "data" directive, variables properties use "attribute" directive
              if 'readonly' in type_descr:
                  fw("   .. data:: %s\n\n" % prop.identifier)
              elif func.return_values:  # multiple return values
                  fw("      :return (%s):\n" % ", ".join(prop.identifier for prop in func.return_values))
                  for prop in func.return_values:
--                    type_descr = prop.get_type_description(as_ret=True, class_fmt=":class:`%s`")
++                    type_descr = prop.get_type_description(as_ret=True, class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID)
                      descr = prop.description
                      if not descr:
                          descr = prop.name
                  continue
              write_struct(struct)
  
--        # special case, bpy_struct
--        if _BPY_STRUCT_FAKE:
--            filepath = os.path.join(BASEPATH, "bpy.types.%s.rst" % _BPY_STRUCT_FAKE)
++        def fake_bpy_type(class_value, class_name, descr_str, use_subclasses=True):
++            filepath = os.path.join(BASEPATH, "bpy.types.%s.rst" % class_name)
              file = open(filepath, "w")
              fw = file.write
  
--            fw("%s\n" % _BPY_STRUCT_FAKE)
--            fw("=" * len(_BPY_STRUCT_FAKE) + "\n")
--            fw("\n")
++            write_title(fw, class_name, "=")
++
              fw(".. module:: bpy.types\n")
              fw("\n")
  
--            subclass_ids = [s.identifier for s in structs.values() if s.base is None if not rna_info.rna_id_ignore(s.identifier)]
--            if subclass_ids:
--                fw("subclasses --- \n" + ", ".join((":class:`%s`" % s) for s in sorted(subclass_ids)) + "\n\n")
++            if use_subclasses:
++                subclass_ids = [s.identifier for s in structs.values() if s.base is None if not rna_info.rna_id_ignore(s.identifier)]
++                if subclass_ids:
++                    fw("subclasses --- \n" + ", ".join((":class:`%s`" % s) for s in sorted(subclass_ids)) + "\n\n")
  
--            fw(".. class:: %s\n\n" % _BPY_STRUCT_FAKE)
--            fw("   built-in base class for all classes in bpy.types.\n\n")
++            fw(".. class:: %s\n\n" % class_name)
++            fw("   %s\n\n" % descr_str)
              fw("   .. note::\n\n")
--            fw("      Note that bpy.types.%s is not actually available from within blender, it only exists for the purpose of documentation.\n\n" % _BPY_STRUCT_FAKE)
++            fw("      Note that bpy.types.%s is not actually available from within blender, it only exists for the purpose of documentation.\n\n" % class_name)
  
--            descr_items = [(key, descr) for key, descr in sorted(bpy.types.Struct.__bases__[0].__dict__.items()) if not key.startswith("__")]
++            descr_items = [(key, descr) for key, descr in sorted(class_value.__dict__.items()) if not key.startswith("__")]
  
              for key, descr in descr_items:
                  if type(descr) == MethodDescriptorType:  # GetSetDescriptorType, GetSetDescriptorType's are not documented yet
--                    py_descr2sphinx("   ", fw, descr, "bpy.types", _BPY_STRUCT_FAKE, key)
++                    py_descr2sphinx("   ", fw, descr, "bpy.types", class_name, key)
  
              for key, descr in descr_items:
                  if type(descr) == GetSetDescriptorType:
--                    py_descr2sphinx("   ", fw, descr, "bpy.types", _BPY_STRUCT_FAKE, key)
++                    py_descr2sphinx("   ", fw, descr, "bpy.types", class_name, key)
              file.close()
  
++        # write fake classes
++        if _BPY_STRUCT_FAKE:
++            class_value = bpy.types.Struct.__bases__[0]
++            fake_bpy_type(class_value, _BPY_STRUCT_FAKE, "built-in base class for all classes in bpy.types.", use_subclasses=True)
++
++        if _BPY_PROP_COLLECTION_FAKE:
++            class_value = bpy.data.objects.__class__
++            fake_bpy_type(class_value, _BPY_PROP_COLLECTION_FAKE, "built-in class used for all collections.", use_subclasses=False)
++
      # operators
      def write_ops():
          API_BASEURL = "https://svn.blender.org/svnroot/bf-blender/trunk/blender/release/scripts"
              fw = file.write
  
              title = "%s Operators" % op_module_name.replace("_", " ").title()
--            fw("%s\n%s\n\n" % (title, "=" * len(title)))
++
++            write_title(fw, title, "=")
  
              fw(".. module:: bpy.ops.%s\n\n" % op_module_name)
  
@@@ -1084,7 -1074,7 +1106,9 @@@ def rna2sphinx(BASEPATH)
          fw("\n")
  
          title = ":mod:`bpy` --- Blender Python Module"
--        fw("%s\n%s\n\n" % (title, "=" * len(title)))
++
++        write_title(fw, title, "=")
++
          fw(".. module:: bpy.types\n\n")
          file.close()
  
index 87247c3c1c0c8628fc6822b2fda66ff49b1f5060,87247c3c1c0c8628fc6822b2fda66ff49b1f5060..ad8678f28b2fa06cc8f940846c55c26dc2852ae9
@@@ -205,14 -205,14 +205,12 @@@ public
                btScalar                                m_kAST;                 // Area/Angular stiffness coefficient [0,1]
                btScalar                                m_kVST;                 // Volume stiffness coefficient [0,1]
                int                                             m_flags;                // Flags
--              Material() : Element() {}
        };
  
        /* Feature              */ 
        struct  Feature : Element
        {
                Material*                               m_material;             // Material
--              Feature() : Element() {}
        };
        /* Node                 */ 
        struct  Node : Feature
                btScalar                                m_area;                 // Area
                btDbvtNode*                             m_leaf;                 // Leaf data
                int                                             m_battach:1;    // Attached
--              Node() : Feature() {}
        };
        /* Link                 */ 
        struct  Link : Feature
                btScalar                                m_c1;                   // rl^2
                btScalar                                m_c2;                   // |gradient|^2/c0
                btVector3                               m_c3;                   // gradient
--              Link() : Feature() {}
        };
        /* Face                 */ 
        struct  Face : Feature
                btVector3                               m_normal;               // Normal
                btScalar                                m_ra;                   // Rest area
                btDbvtNode*                             m_leaf;                 // Leaf data
--              Face() : Feature() {}
        };
        /* Tetra                */ 
        struct  Tetra : Feature
                btVector3                               m_c0[4];                // gradients
                btScalar                                m_c1;                   // (4*kVST)/(im0+im1+im2+im3)
                btScalar                                m_c2;                   // m_c1/sum(|g0..3|^2)
--              Tetra() : Feature() {}
        };
        /* RContact             */ 
        struct  RContact
                int                                             m_rank;                 // Rank
                Node*                                   m_nodes[4];             // Nodes
                btScalar                                m_coords[4];    // Coordinates
--              Note() : Element() {}
        };      
        /* Pose                 */ 
        struct  Pose
index 33185a63d5bc62a3a8f0721ac2c14f9145fd5670,33185a63d5bc62a3a8f0721ac2c14f9145fd5670..6c86cf82f0e4588fb33375f3bf47d72273d6200d
@@@ -118,6 -118,6 +118,11 @@@ if(APPLE
        endif()
  
  elseif(UNIX)
++
++      if(WITH_X11_XINPUT)
++              add_definitions(-DWITH_X11_XINPUT)
++      endif()
++
        list(APPEND INC ${X11_X11_INCLUDE_PATH})
  
        list(APPEND SRC
                intern/GHOST_WindowX11.h
        )
  
--      add_definitions(-DPREFIX="${CMAKE_INSTALL_PREFIX}")
++      if(NOT WITH_INSTALL_PORTABLE)
++              add_definitions(-DPREFIX="${CMAKE_INSTALL_PREFIX}")
++      endif()
  
        if(X11_XF86keysym_INCLUDE_PATH)
                add_definitions(-DWITH_XF86KEYSYM)
index e1464542ed31910ff9f3613b4e4e1d577c3c15b1,e1464542ed31910ff9f3613b4e4e1d577c3c15b1..be8dc138797d1f9b8a028ccacc47ccb9e74cc140
@@@ -262,7 -262,7 +262,16 @@@ extern int GHOST_DispatchEvents(GHOST_S
   */
  extern GHOST_TSuccess GHOST_AddEventConsumer(GHOST_SystemHandle systemhandle,
                                                                                          GHOST_EventConsumerHandle consumerhandle);
--      
++
++/**
++ * Remove the given event consumer to our list.
++ * @param systemhandle The handle to the system
++ * @param consumerhandle The event consumer to remove.
++ * @return Indication of success.
++ */
++extern GHOST_TSuccess GHOST_RemoveEventConsumer(GHOST_SystemHandle systemhandle,
++                                                                                        GHOST_EventConsumerHandle consumerhandle);
++
  /***************************************************************************************
   ** Progress bar functionality
   ***************************************************************************************/
index 7e1300d780ab5eb1407bfb992c12727c6d7b8ec4,7e1300d780ab5eb1407bfb992c12727c6d7b8ec4..38c732153d79c201aeb97f770023f3ef52bd23a2
@@@ -290,7 -290,7 +290,14 @@@ public
         * @return Indication of success.
         */
        virtual GHOST_TSuccess addEventConsumer(GHOST_IEventConsumer* consumer) = 0;
--      
++
++      /**
++       * Removes the given event consumer to our list.
++       * @param consumer The event consumer to remove.
++       * @return Indication of success.
++       */
++      virtual GHOST_TSuccess removeEventConsumer(GHOST_IEventConsumer* consumer) = 0;
++
         /***************************************************************************************
         ** N-degree of freedom device management functionality
         ***************************************************************************************/
index 7aedc08ce96d08eca20024fe6133e559c0581ba6,7aedc08ce96d08eca20024fe6133e559c0581ba6..b67545f216a4b4b9edc862365427d242be8ade5a
@@@ -21,7 -21,7 +21,8 @@@ if window_system in ('linux2', 'openbsd
              sources.remove('intern' + os.sep + f + 'Carbon.cpp')
          except ValueError:
              pass
--    defs += ['PREFIX=\\"/usr/local/\\"']
++    defs += ['PREFIX=\\"/usr/local/\\"']  # XXX, make an option
++    defs += ['WITH_X11_XINPUT']  # XXX, make an option
  
  elif window_system in ('win32-vc', 'win32-mingw', 'cygwin', 'linuxcross', 'win64-vc'):
      for f in pf:
index 20caa057be01c4df311428b31965de6286fc8546,20caa057be01c4df311428b31965de6286fc8546..28058c604999c9b2d92edbf45fbb96ee5c5ba947
@@@ -253,6 -253,6 +253,13 @@@ GHOST_TSuccess GHOST_AddEventConsumer(G
        return system->addEventConsumer((GHOST_CallbackEventConsumer*)consumerhandle);
  }
  
++GHOST_TSuccess GHOST_RemoveEventConsumer(GHOST_SystemHandle systemhandle, GHOST_EventConsumerHandle consumerhandle)
++{
++      GHOST_ISystem* system = (GHOST_ISystem*) systemhandle;
++
++      return system->removeEventConsumer((GHOST_CallbackEventConsumer*)consumerhandle);
++}
++
  GHOST_TSuccess GHOST_SetProgressBar(GHOST_WindowHandle windowhandle,float progress)
  {
        GHOST_IWindow* window = (GHOST_IWindow*) windowhandle;
index ce492c2903b16eacfc2b3a808a053d53b062a861,ce492c2903b16eacfc2b3a808a053d53b062a861..559fb65277d871030b82b70e0d4c65fca2d15268
@@@ -226,6 -226,6 +226,17 @@@ GHOST_TSuccess GHOST_System::addEventCo
        return success;
  }
  
++GHOST_TSuccess GHOST_System::removeEventConsumer(GHOST_IEventConsumer* consumer)
++{
++      GHOST_TSuccess success;
++      if (m_eventManager) {
++              success = m_eventManager->removeConsumer(consumer);
++      }
++      else {
++              success = GHOST_kFailure;
++      }
++      return success;
++}
  
  GHOST_TSuccess GHOST_System::pushEvent(GHOST_IEvent* event)
  {
index 1f2199855fad909c5de8c40d30bc0a87575e3390,1f2199855fad909c5de8c40d30bc0a87575e3390..b5c64bfceb672a5efd716d4337a9cbac2f22d428
@@@ -183,7 -183,7 +183,12 @@@ public
         */
        virtual GHOST_TSuccess addEventConsumer(GHOST_IEventConsumer* consumer);
  
--
++      /**
++       * Remove the given event consumer to our list.
++       * @param consumer The event consumer to remove.
++       * @return Indication of success.
++       */
++      virtual GHOST_TSuccess removeEventConsumer(GHOST_IEventConsumer* consumer);
  
        /***************************************************************************************
         ** N-degree of freedom devcice management functionality
index 30c45de47bcc6691691c59ecbd99bffd60d565e1,30c45de47bcc6691691c59ecbd99bffd60d565e1..dd8935732c504974a175336b4d0043612d47bad0
  #include <stdio.h> // for fprintf only
  #include <cstdlib> // for exit
  
--#ifndef PREFIX
--#  define PREFIX "/usr/local"
--#endif
--
--
  using namespace std;
  
  GHOST_SystemPathsX11::GHOST_SystemPathsX11()
@@@ -60,7 -60,7 +55,12 @@@ GHOST_SystemPathsX11::~GHOST_SystemPath
  
  const GHOST_TUns8* GHOST_SystemPathsX11::getSystemDir() const
  {
++      /* no prefix assumes a portable build which only uses bundled scripts */
++#ifdef PREFIX
        return (GHOST_TUns8*) PREFIX "/share";
++#else
++      return NULL;
++#endif
  }
  
  const GHOST_TUns8* GHOST_SystemPathsX11::getUserDir() const
index 36748b9f2953e9486a434c794db8feb16f9d3841,36748b9f2953e9486a434c794db8feb16f9d3841..e2ee7044392810ca8d2494b615aa9c595a3a598b
  #include <stdio.h> // for fprintf only
  #include <cstdlib> // for exit
  
--#ifndef PREFIX
--#error "PREFIX not defined"
--#endif
--
  typedef struct NDOFPlatformInfo {
        Display *display;
        Window window;
index 890c40f6f11e2f87ca273575676b6d855e2742be,890c40f6f11e2f87ca273575676b6d855e2742be..aea5b5156d9d9a72a5d76694259d545f2055a9e5
@@@ -439,7 -439,7 +439,9 @@@ GHOST_WindowX11
  
        setTitle(title);
  
++#ifdef WITH_X11_XINPUT
        initXInputDevices();
++#endif
  
        // now set up the rendering context.
        if (installDrawingContext(type) == GHOST_kSuccess) {
        XFlush(m_display);
  }
  
++#ifdef WITH_X11_XINPUT
  /* 
        Dummy function to get around IO Handler exiting if device invalid
        Basically it will not crash blender now if you have a X device that 
@@@ -493,6 -493,6 +496,7 @@@ static bool match_token(const char *hay
        return FALSE;
  }
  
++
  /*    Determining if an X device is a Tablet style device is an imperfect science.
  **  We rely on common conventions around device names as well as the type reported
  **  by Wacom tablets.  This code will likely need to be expanded for alternate tablet types
@@@ -652,8 -652,8 +656,9 @@@ void GHOST_WindowX11::initXInputDevices
                }
                XFree(version);
        }
--}     
++}
  
++#endif /* WITH_X11_XINPUT */
  
        Window 
  GHOST_WindowX11::
@@@ -1275,14 -1275,14 +1280,16 @@@ GHOST_WindowX11:
        if (m_custom_cursor) {
                XFreeCursor(m_display, m_custom_cursor);
        }
--      
++
++#ifdef WITH_X11_XINPUT
        /* close tablet devices */
        if(m_xtablet.StylusDevice)
                XCloseDevice(m_display, m_xtablet.StylusDevice);
        
        if(m_xtablet.EraserDevice)
                XCloseDevice(m_display, m_xtablet.EraserDevice);
--      
++#endif /* WITH_X11_XINPUT */
++
        if (m_context != s_firstContext) {
                glXDestroyContext(m_display, m_context);
        }
index d46e4da790089b691f1c499d997dd8ebfc5ade3f,d46e4da790089b691f1c499d997dd8ebfc5ade3f..0c5e00ec56f3fb52484b036f4df02bf02ff23512
@@@ -292,7 -292,7 +292,7 @@@ bool Scene::update(double timestamp, do
                return true;
        }
  
--      double maxqdot;
++      // double maxqdot; // UNUSED
        e_scalar nlcoef;
        SceneLock lockCallback(this);
        Frame external_pose;
                        // We will pass the joint velocity to each object and they will recommend a maximum timestep
                        timesubstep = timeleft;
                        // get armature max joint velocity to estimate the maximum duration of integration
--                      maxqdot = m_qdot.cwise().abs().maxCoeff();
++                      // maxqdot = m_qdot.cwise().abs().maxCoeff(); // UNUSED
                        double maxsubstep = nlcoef*m_maxstep;
                        if (maxsubstep < m_minstep)
                                maxsubstep = m_minstep;
diff --cc release/scripts/io/netrender/__init__.py
index c77894d475118b366ef5f7d4a79a50dfd9bf2b2d,c77894d475118b366ef5f7d4a79a50dfd9bf2b2d..0000000000000000000000000000000000000000
deleted file mode 100644,100644
+++ /dev/null
@@@ -1,73 -1,73 +1,0 @@@
--# ##### BEGIN GPL LICENSE BLOCK #####
--#
--#  This program is free software; you can redistribute it and/or
--#  modify it under the terms of the GNU General Public License
--#  as published by the Free Software Foundation; either version 2
--#  of the License, or (at your option) any later version.
--#
--#  This program is distributed in the hope that it will be useful,
--#  but WITHOUT ANY WARRANTY; without even the implied warranty of
--#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--#  GNU General Public License for more details.
--#
--#  You should have received a copy of the GNU General Public License
--#  along with this program; if not, write to the Free Software Foundation,
--#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
--#
--# ##### END GPL LICENSE BLOCK #####
--
--# This directory is a Python package.
--
--# To support reload properly, try to access a package var, if it's there, reload everything
--if "init_data" in locals():
--    import imp
--    imp.reload(model)
--    imp.reload(operators)
--    imp.reload(client)
--    imp.reload(slave)
--    imp.reload(master)
--    imp.reload(master_html)
--    imp.reload(utils)
--    imp.reload(balancing)
--    imp.reload(ui)
--    imp.reload(repath)
--    imp.reload(versioning)
--else:
--    from . import model
--    from . import operators
--    from . import client
--    from . import slave
--    from . import master
--    from . import master_html
--    from . import utils
--    from . import balancing
--    from . import ui
--    from . import repath
--    from . import versioning
--
--jobs = []
--slaves = []
--blacklist = []
--
--init_file = ""
--valid_address = False
--init_data = True
--
--
--def register():
--    ui.addProperties()
--
--    import bpy
--    scene = bpy.context.scene
--    if scene:
--        netsettings = scene.network_render
--        ui.init_data(netsettings)
--
--    bpy.utils.register_module(__name__)
--    
--
--def unregister():
--    import bpy
--    del bpy.types.Scene.network_render
--
--    bpy.utils.unregister_module(__name__)
diff --cc release/scripts/io/netrender/balancing.py
index dde3ad53084137019d95f802d106d76560ef3ac6,dde3ad53084137019d95f802d106d76560ef3ac6..0000000000000000000000000000000000000000
deleted file mode 100644,100644
+++ /dev/null
@@@ -1,195 -1,195 +1,0 @@@
--# ##### BEGIN GPL LICENSE BLOCK #####
--#
--#  This program is free software; you can redistribute it and/or
--#  modify it under the terms of the GNU General Public License
--#  as published by the Free Software Foundation; either version 2
--#  of the License, or (at your option) any later version.
--#
--#  This program is distributed in the hope that it will be useful,
--#  but WITHOUT ANY WARRANTY; without even the implied warranty of
--#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--#  GNU General Public License for more details.
--#
--#  You should have received a copy of the GNU General Public License
--#  along with this program; if not, write to the Free Software Foundation,
--#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
--#
--# ##### END GPL LICENSE BLOCK #####
--
--import time
--
--from netrender.utils import *
--import netrender.model
--
--class RatingRule:
--    def __init__(self):
--        self.enabled = True
--
--    def id(self):
--        return str(id(self))
--
--    def rate(self, job):
--        return 0
--
--class ExclusionRule:
--    def __init__(self):
--        self.enabled = True
--
--    def id(self):
--        return str(id(self))
--
--    def test(self, job):
--        return False
--
--class PriorityRule:
--    def __init__(self):
--        self.enabled = True
--
--    def id(self):
--        return str(id(self))
--
--    def test(self, job):
--        return False
--
--class Balancer:
--    def __init__(self):
--        self.rules = []
--        self.priorities = []
--        self.exceptions = []
--
--    def ruleByID(self, rule_id):
--        for rule in self.rules:
--            if rule.id() == rule_id:
--                return rule
--        for rule in self.priorities:
--            if rule.id() == rule_id:
--                return rule
--        for rule in self.exceptions:
--            if rule.id() == rule_id:
--                return rule
--
--        return None
--
--    def addRule(self, rule):
--        self.rules.append(rule)
--
--    def addPriority(self, priority):
--        self.priorities.append(priority)
--
--    def addException(self, exception):
--        self.exceptions.append(exception)
--
--    def applyRules(self, job):
--        return sum((rule.rate(job) for rule in self.rules if rule.enabled))
--
--    def applyPriorities(self, job):
--        for priority in self.priorities:
--            if priority.enabled and priority.test(job):
--                return True # priorities are first
--
--        return False
--
--    def applyExceptions(self, job):
--        for exception in self.exceptions:
--            if exception.enabled and exception.test(job):
--                return True # exceptions are last
--
--        return False
--
--    def sortKey(self, job):
--        return (1 if self.applyExceptions(job) else 0, # exceptions after
--                        0 if self.applyPriorities(job) else 1, # priorities first
--                        self.applyRules(job))
--
--    def balance(self, jobs):
--        if jobs:
--            # use inline copy to make sure the list is still accessible while sorting
--            jobs[:] = sorted(jobs, key=self.sortKey)
--            return jobs[0]
--        else:
--            return None
--
--# ==========================
--
--class RatingUsage(RatingRule):
--    def __str__(self):
--        return "Usage per job"
--
--    def rate(self, job):
--        # less usage is better
--        return job.usage / job.priority
--
--class RatingUsageByCategory(RatingRule):
--    def __init__(self, get_jobs):
--        super().__init__()
--        self.getJobs = get_jobs
--
--    def __str__(self):
--        return "Usage per category"
--
--    def rate(self, job):
--        total_category_usage = sum([j.usage for j in self.getJobs() if j.category == job.category])
--        maximum_priority = max([j.priority for j in self.getJobs() if j.category == job.category])
--
--        # less usage is better
--        return total_category_usage / maximum_priority
--
--class NewJobPriority(PriorityRule):
--    def __init__(self, limit = 1):
--        super().__init__()
--        self.limit = limit
--
--    def setLimit(self, value):
--        self.limit = int(value)
--
--    def str_limit(self):
--        return "less than %i frame%s done" % (self.limit, "s" if self.limit > 1 else "")
--
--    def __str__(self):
--        return "Priority to new jobs"
--
--    def test(self, job):
--        return job.countFrames(status = DONE) < self.limit
--
--class MinimumTimeBetweenDispatchPriority(PriorityRule):
--    def __init__(self, limit = 10):
--        super().__init__()
--        self.limit = limit
--
--    def setLimit(self, value):
--        self.limit = int(value)
--
--    def str_limit(self):
--        return "more than %i minute%s since last" % (self.limit, "s" if self.limit > 1 else "")
--
--    def __str__(self):
--        return "Priority to jobs that haven't been dispatched recently"
--
--    def test(self, job):
--        return job.countFrames(status = DISPATCHED) == 0 and (time.time() - job.last_dispatched) / 60 > self.limit
--
--class ExcludeQueuedEmptyJob(ExclusionRule):
--    def __str__(self):
--        return "Exclude non queued or empty jobs"
--
--    def test(self, job):
--        return job.status != JOB_QUEUED or job.countFrames(status = QUEUED) == 0
--
--class ExcludeSlavesLimit(ExclusionRule):
--    def __init__(self, count_jobs, count_slaves, limit = 0.75):
--        super().__init__()
--        self.count_jobs = count_jobs
--        self.count_slaves = count_slaves
--        self.limit = limit
--
--    def setLimit(self, value):
--        self.limit = float(value)
--
--    def str_limit(self):
--        return "more than %.0f%% of all slaves" % (self.limit * 100)
--
--    def __str__(self):
--        return "Exclude jobs that would use too many slaves"
--
--    def test(self, job):
--        return not ( self.count_jobs() == 1 or self.count_slaves() <= 1 or float(job.countSlaves() + 1) / self.count_slaves() <= self.limit )
diff --cc release/scripts/io/netrender/client.py
index bc43b8cfbb77fc4c0320b470dbb9322fe6528442,bc43b8cfbb77fc4c0320b470dbb9322fe6528442..0000000000000000000000000000000000000000
deleted file mode 100644,100644
+++ /dev/null
@@@ -1,372 -1,372 +1,0 @@@
--# ##### BEGIN GPL LICENSE BLOCK #####
--#
--#  This program is free software; you can redistribute it and/or
--#  modify it under the terms of the GNU General Public License
--#  as published by the Free Software Foundation; either version 2
--#  of the License, or (at your option) any later version.
--#
--#  This program is distributed in the hope that it will be useful,
--#  but WITHOUT ANY WARRANTY; without even the implied warranty of
--#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--#  GNU General Public License for more details.
--#
--#  You should have received a copy of the GNU General Public License
--#  along with this program; if not, write to the Free Software Foundation,
--#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
--#
--# ##### END GPL LICENSE BLOCK #####
--
--import bpy
--import sys, os, re
--import http, http.client, http.server, urllib
--import subprocess, shutil, time, hashlib
--import json
--
--import netrender
--import netrender.model
--import netrender.slave as slave
--import netrender.master as master
--from netrender.utils import *
--
--def addFluidFiles(job, path):
--    if os.path.exists(path):
--        pattern = re.compile("fluidsurface_(final|preview)_([0-9]+)\.(bobj|bvel)\.gz")
--
--        for fluid_file in sorted(os.listdir(path)):
--            match = pattern.match(fluid_file)
--
--            if match:
--                # fluid frames starts at 0, which explains the +1
--                # This is stupid
--                current_frame = int(match.groups()[1]) + 1
--                job.addFile(path + fluid_file, current_frame, current_frame)
--
--def addPointCache(job, ob, point_cache, default_path):
--    if not point_cache.use_disk_cache:
--        return
--
--
--    name = point_cache.name
--    if name == "":
--        name = "".join(["%02X" % ord(c) for c in ob.name])
--
--    cache_path = bpy.path.abspath(point_cache.filepath) if point_cache.use_external else default_path
--
--    index = "%02i" % point_cache.index
--
--    if os.path.exists(cache_path):
--        pattern = re.compile(name + "_([0-9]+)_" + index + "\.bphys")
--
--        cache_files = []
--
--        for cache_file in sorted(os.listdir(cache_path)):
--            match = pattern.match(cache_file)
--
--            if match:
--                cache_frame = int(match.groups()[0])
--                cache_files.append((cache_frame, cache_file))
--
--        cache_files.sort()
--
--        if len(cache_files) == 1:
--            cache_frame, cache_file = cache_files[0]
--            job.addFile(cache_path + cache_file, cache_frame, cache_frame)
--        else:
--            for i in range(len(cache_files)):
--                current_item = cache_files[i]
--                next_item = cache_files[i+1] if i + 1 < len(cache_files) else None
--                previous_item = cache_files[i - 1] if i > 0 else None
--
--                current_frame, current_file = current_item
--
--                if  not next_item and not previous_item:
--                    job.addFile(cache_path + current_file, current_frame, current_frame)
--                elif next_item and not previous_item:
--                    next_frame = next_item[0]
--                    job.addFile(cache_path + current_file, current_frame, next_frame - 1)
--                elif not next_item and previous_item:
--                    previous_frame = previous_item[0]
--                    job.addFile(cache_path + current_file, previous_frame + 1, current_frame)
--                else:
--                    next_frame = next_item[0]
--                    previous_frame = previous_item[0]
--                    job.addFile(cache_path + current_file, previous_frame + 1, next_frame - 1)
--
--def fillCommonJobSettings(job, job_name, netsettings):
--    job.name = job_name
--    job.category = netsettings.job_category
--
--    for slave in netrender.blacklist:
--        job.blacklist.append(slave.id)
--
--    job.chunks = netsettings.chunks
--    job.priority = netsettings.priority
--    
--    if netsettings.job_type == "JOB_BLENDER":
--        job.type = netrender.model.JOB_BLENDER
--    elif netsettings.job_type == "JOB_PROCESS":
--        job.type = netrender.model.JOB_PROCESS
--    elif netsettings.job_type == "JOB_VCS":
--        job.type = netrender.model.JOB_VCS
--
--def clientSendJob(conn, scene, anim = False):
--    netsettings = scene.network_render
--    if netsettings.job_type == "JOB_BLENDER":
--        return clientSendJobBlender(conn, scene, anim)
--    elif netsettings.job_type == "JOB_VCS":
--        return clientSendJobVCS(conn, scene, anim)
--
--def clientSendJobVCS(conn, scene, anim = False):
--    netsettings = scene.network_render
--    job = netrender.model.RenderJob()
--
--    if anim:
--        for f in range(scene.frame_start, scene.frame_end + 1):
--            job.addFrame(f)
--    else:
--        job.addFrame(scene.frame_current)
--
--    filename = bpy.data.filepath
--    
--    if not filename.startswith(netsettings.vcs_wpath):
--        # this is an error, need better way to handle this
--        return
--
--    filename = filename[len(netsettings.vcs_wpath):]
--    
--    if filename[0] in (os.sep, os.altsep):
--        filename = filename[1:]
--    
--    print("CREATING VCS JOB", filename)
--    
--    job.addFile(filename, signed=False)
--
--    job_name = netsettings.job_name
--    path, name = os.path.split(filename)
--    if job_name == "[default]":
--        job_name = name
--
--
--    fillCommonJobSettings(job, job_name, netsettings)
--    
--    # VCS Specific code
--    job.version_info = netrender.model.VersioningInfo()
--    job.version_info.system = netsettings.vcs_system
--    job.version_info.wpath = netsettings.vcs_wpath
--    job.version_info.rpath = netsettings.vcs_rpath
--    job.version_info.revision = netsettings.vcs_revision
--
--    # try to send path first
--    conn.request("POST", "/job", json.dumps(job.serialize()))
--    response = conn.getresponse()
--    response.read()
--
--    job_id = response.getheader("job-id")
--    
--    # a VCS job is always good right now, need error handling
--
--    return job_id
--
--def clientSendJobBlender(conn, scene, anim = False):
--    netsettings = scene.network_render
--    job = netrender.model.RenderJob()
--
--    if anim:
--        for f in range(scene.frame_start, scene.frame_end + 1):
--            job.addFrame(f)
--    else:
--        job.addFrame(scene.frame_current)
--
--    filename = bpy.data.filepath
--    job.addFile(filename)
--
--    job_name = netsettings.job_name
--    path, name = os.path.split(filename)
--    if job_name == "[default]":
--        job_name = name
--
--    ###########################
--    # LIBRARIES
--    ###########################
--    for lib in bpy.data.libraries:
--        file_path = bpy.path.abspath(lib.filepath)
--        if os.path.exists(file_path):
--            job.addFile(file_path)
--
--    ###########################
--    # IMAGES
--    ###########################
--    for image in bpy.data.images:
--        if image.source == "FILE" and not image.packed_file:
--            file_path = bpy.path.abspath(image.filepath)
--            if os.path.exists(file_path):
--                job.addFile(file_path)
--                
--                tex_path = os.path.splitext(file_path)[0] + ".tex"
--                if os.path.exists(tex_path):
--                    job.addFile(tex_path)
--
--    ###########################
--    # FLUID + POINT CACHE
--    ###########################
--    root, ext = os.path.splitext(name)
--    default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
--
--    for object in bpy.data.objects:
--        for modifier in object.modifiers:
--            if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
--                addFluidFiles(job, bpy.path.abspath(modifier.settings.filepath))
--            elif modifier.type == "CLOTH":
--                addPointCache(job, object, modifier.point_cache, default_path)
--            elif modifier.type == "SOFT_BODY":
--                addPointCache(job, object, modifier.point_cache, default_path)
--            elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
--                addPointCache(job, object, modifier.domain_settings.point_cache, default_path)
--            elif modifier.type == "MULTIRES" and modifier.is_external:
--                file_path = bpy.path.abspath(modifier.filepath)
--                job.addFile(file_path)
--
--        # particles modifier are stupid and don't contain data
--        # we have to go through the object property
--        for psys in object.particle_systems:
--            addPointCache(job, object, psys.point_cache, default_path)
--
--    #print(job.files)
--
--    fillCommonJobSettings(job, job_name, netsettings)
--
--    # try to send path first
--    conn.request("POST", "/job", json.dumps(job.serialize()))
--    response = conn.getresponse()
--    response.read()
--
--    job_id = response.getheader("job-id")
--
--    # if not ACCEPTED (but not processed), send files
--    if response.status == http.client.ACCEPTED:
--        for rfile in job.files:
--            f = open(rfile.filepath, "rb")
--            conn.request("PUT", fileURL(job_id, rfile.index), f)
--            f.close()
--            response = conn.getresponse()
--            response.read()
--
--    # server will reply with ACCEPTED until all files are found
--
--    return job_id
--
--def requestResult(conn, job_id, frame):
--    conn.request("GET", renderURL(job_id, frame))
--
--class NetworkRenderEngine(bpy.types.RenderEngine):
--    bl_idname = 'NET_RENDER'
--    bl_label = "Network Render"
--    bl_use_postprocess = False
--    def render(self, scene):
--        if scene.network_render.mode == "RENDER_CLIENT":
--            self.render_client(scene)
--        elif scene.network_render.mode == "RENDER_SLAVE":
--            self.render_slave(scene)
--        elif scene.network_render.mode == "RENDER_MASTER":
--            self.render_master(scene)
--        else:
--            print("UNKNOWN OPERATION MODE")
--
--    def render_master(self, scene):
--        netsettings = scene.network_render
--
--        address = "" if netsettings.server_address == "[default]" else netsettings.server_address
--
--        master.runMaster((address, netsettings.server_port), netsettings.use_master_broadcast, netsettings.use_master_clear, bpy.path.abspath(netsettings.path), self.update_stats, self.test_break)
--
--
--    def render_slave(self, scene):
--        slave.render_slave(self, scene.network_render, scene.render.threads)
--
--    def render_client(self, scene):
--        netsettings = scene.network_render
--        self.update_stats("", "Network render client initiation")
--
--
--        conn = clientConnection(netsettings.server_address, netsettings.server_port)
--
--        if conn:
--            # Sending file
--
--            self.update_stats("", "Network render exporting")
--
--            new_job = False
--
--            job_id = netsettings.job_id
--
--            # reading back result
--
--            self.update_stats("", "Network render waiting for results")
--            
--             
--            requestResult(conn, job_id, scene.frame_current)
--            response = conn.getresponse()
--            buf = response.read()
--
--            if response.status == http.client.NO_CONTENT:
--                new_job = True
--                netsettings.job_id = clientSendJob(conn, scene)
--                job_id = netsettings.job_id
--
--                requestResult(conn, job_id, scene.frame_current)
--                response = conn.getresponse()
--                buf = response.read()
--                
--            while response.status == http.client.ACCEPTED and not self.test_break():
--                time.sleep(1)
--                requestResult(conn, job_id, scene.frame_current)
--                response = conn.getresponse()
--                buf = response.read()
--
--            # cancel new jobs (animate on network) on break
--            if self.test_break() and new_job:
--                conn.request("POST", cancelURL(job_id))
--                response = conn.getresponse()
--                response.read()
--                print( response.status, response.reason )
--                netsettings.job_id = 0
--
--            if response.status != http.client.OK:
--                conn.close()
--                return
--
--            r = scene.render
--            x= int(r.resolution_x*r.resolution_percentage*0.01)
--            y= int(r.resolution_y*r.resolution_percentage*0.01)
--            
--            result_path = os.path.join(bpy.path.abspath(netsettings.path), "output.exr")
--            
--            folder = os.path.split(result_path)[0]
--            
--            if not os.path.exists(folder):
--                os.mkdir(folder)
--
--            f = open(result_path, "wb")
--
--            f.write(buf)
--
--            f.close()
--
--            result = self.begin_result(0, 0, x, y)
--            result.load_from_file(result_path)
--            self.end_result(result)
--
--            conn.close()
--
--def compatible(module):
--    module = __import__(module)
--    for subclass in module.__dict__.values():
--        try:          subclass.COMPAT_ENGINES.add('NET_RENDER')
--        except:       pass
--    del module
--
--compatible("properties_world")
--compatible("properties_material")
--compatible("properties_data_mesh")
--compatible("properties_data_camera")
--compatible("properties_texture")
diff --cc release/scripts/io/netrender/master.py
index 793e3bb51bf1fd706c080a2cc0b04cf380a04bab,793e3bb51bf1fd706c080a2cc0b04cf380a04bab..0000000000000000000000000000000000000000
deleted file mode 100644,100644
+++ /dev/null
@@@ -1,1079 -1,1079 +1,0 @@@
--# ##### BEGIN GPL LICENSE BLOCK #####
--#
--#  This program is free software; you can redistribute it and/or
--#  modify it under the terms of the GNU General Public License
--#  as published by the Free Software Foundation; either version 2
--#  of the License, or (at your option) any later version.
--#
--#  This program is distributed in the hope that it will be useful,
--#  but WITHOUT ANY WARRANTY; without even the implied warranty of
--#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--#  GNU General Public License for more details.
--#
--#  You should have received a copy of the GNU General Public License
--#  along with this program; if not, write to the Free Software Foundation,
--#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
--#
--# ##### END GPL LICENSE BLOCK #####
--
--import sys, os
--import http, http.client, http.server, urllib, socket, socketserver, threading
--import subprocess, shutil, time, hashlib
--import pickle
--import select # for select.error
--import json
--
--from netrender.utils import *
--import netrender.model
--import netrender.balancing
--import netrender.master_html
--import netrender.thumbnail as thumbnail
--
--class MRenderFile(netrender.model.RenderFile):
--    def __init__(self, filepath, index, start, end, signature):
--        super().__init__(filepath, index, start, end, signature)
--        self.found = False
--
--    def test(self):
--        self.found = os.path.exists(self.filepath)
--        if self.found and self.signature != None:
--            found_signature = hashFile(self.filepath)
--            self.found = self.signature == found_signature
--            
--        return self.found
--
--
--class MRenderSlave(netrender.model.RenderSlave):
--    def __init__(self, name, address, stats):
--        super().__init__()
--        self.id = hashlib.md5(bytes(repr(name) + repr(address), encoding='utf8')).hexdigest()
--        self.name = name
--        self.address = address
--        self.stats = stats
--        self.last_seen = time.time()
--
--        self.job = None
--        self.job_frames = []
--
--        netrender.model.RenderSlave._slave_map[self.id] = self
--
--    def seen(self):
--        self.last_seen = time.time()
--
--    def finishedFrame(self, frame_number):
--        self.job_frames.remove(frame_number)
--        if not self.job_frames:
--            self.job = None
--
--class MRenderJob(netrender.model.RenderJob):
--    def __init__(self, job_id, job_info):
--        super().__init__(job_info)
--        self.id = job_id
--        self.last_dispatched = time.time()
--
--        # force one chunk for process jobs
--        if self.type == netrender.model.JOB_PROCESS:
--            self.chunks = 1
--
--        # Force WAITING status on creation
--        self.status = JOB_WAITING
--
--        # special server properties
--        self.last_update = 0
--        self.save_path = ""
--        self.files = [MRenderFile(rfile.filepath, rfile.index, rfile.start, rfile.end, rfile.signature) for rfile in job_info.files]
--
--    def initInfo(self):
--        if not self.resolution:
--            self.resolution = tuple(getFileInfo(self.files[0].filepath, ["bpy.context.scene.render.resolution_x", "bpy.context.scene.render.resolution_y", "bpy.context.scene.render.resolution_percentage"]))
--
--    def save(self):
--        if self.save_path:
--            f = open(os.path.join(self.save_path, "job.txt"), "w")
--            f.write(json.dumps(self.serialize()))
--            f.close()
--
--    def edit(self, info_map):
--        if "status" in info_map:
--            self.status = info_map["status"]
--
--        if "priority" in info_map:
--            self.priority = info_map["priority"]
--
--        if "chunks" in info_map:
--            self.chunks = info_map["chunks"]
--
--    def testStart(self):
--        # Don't test files for versionned jobs
--        if not self.version_info:
--            for f in self.files:
--                if not f.test():
--                    return False
--
--        self.start()
--        self.initInfo()
--        return True
--
--    def testFinished(self):
--        for f in self.frames:
--            if f.status == QUEUED or f.status == DISPATCHED:
--                break
--        else:
--            self.status = JOB_FINISHED
--
--    def pause(self, status = None):
--        if self.status not in {JOB_PAUSED, JOB_QUEUED}:
--            return
--
--        if status is None:
--            self.status = JOB_PAUSED if self.status == JOB_QUEUED else JOB_QUEUED
--        elif status:
--            self.status = JOB_QUEUED
--        else:
--            self.status = JOB_PAUSED
--
--    def start(self):
--        self.status = JOB_QUEUED
--
--    def addLog(self, frames):
--        log_name = "_".join(("%06d" % f for f in frames)) + ".log"
--        log_path = os.path.join(self.save_path, log_name)
--
--        for number in frames:
--            frame = self[number]
--            if frame:
--                frame.log_path = log_path
--
--    def addFrame(self, frame_number, command):
--        frame = MRenderFrame(frame_number, command)
--        self.frames.append(frame)
--        return frame
--
--    def reset(self, all):
--        for f in self.frames:
--            f.reset(all)
--
--    def getFrames(self):
--        frames = []
--        for f in self.frames:
--            if f.status == QUEUED:
--                self.last_dispatched = time.time()
--                frames.append(f)
--                if len(frames) >= self.chunks:
--                    break
--
--        return frames
--
--class MRenderFrame(netrender.model.RenderFrame):
--    def __init__(self, frame, command):
--        super().__init__()
--        self.number = frame
--        self.slave = None
--        self.time = 0
--        self.status = QUEUED
--        self.command = command
--
--        self.log_path = None
--
--    def reset(self, all):
--        if all or self.status == ERROR:
--            self.log_path = None
--            self.slave = None
--            self.time = 0
--            self.status = QUEUED
--
--
--# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
--# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
--# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--file_pattern = re.compile("/file_([a-zA-Z0-9]+)_([0-9]+)")
--render_pattern = re.compile("/render_([a-zA-Z0-9]+)_([0-9]+).exr")
--thumb_pattern = re.compile("/thumb_([a-zA-Z0-9]+)_([0-9]+).jpg")
--log_pattern = re.compile("/log_([a-zA-Z0-9]+)_([0-9]+).log")
--reset_pattern = re.compile("/reset(all|)_([a-zA-Z0-9]+)_([0-9]+)")
--cancel_pattern = re.compile("/cancel_([a-zA-Z0-9]+)")
--pause_pattern = re.compile("/pause_([a-zA-Z0-9]+)")
--edit_pattern = re.compile("/edit_([a-zA-Z0-9]+)")
--
--class RenderHandler(http.server.BaseHTTPRequestHandler):
--    def log_message(self, format, *args):
--        # override because the original calls self.address_string(), which
--        # is extremely slow due to some timeout..
--        sys.stderr.write("[%s] %s\n" % (self.log_date_time_string(), format%args))
--
--    def getInfoMap(self):
--        length = int(self.headers['content-length'])
--
--        if length > 0:
--            msg = str(self.rfile.read(length), encoding='utf8')
--            return json.loads(msg)
--        else:
--            return {}
--
--    def send_head(self, code = http.client.OK, headers = {}, content = "application/octet-stream"):
--        self.send_response(code)
--        self.send_header("Content-type", content)
--
--        for key, value in headers.items():
--            self.send_header(key, value)
--
--        self.end_headers()
--
--    def do_HEAD(self):
--
--        if self.path == "/status":
--            job_id = self.headers.get('job-id', "")
--            job_frame = int(self.headers.get('job-frame', -1))
--
--            job = self.server.getJobID(job_id)
--            if job:
--                frame = job[job_frame]
--
--
--                if frame:
--                    self.send_head(http.client.OK)
--                else:
--                    # no such frame
--                    self.send_head(http.client.NO_CONTENT)
--            else:
--                # no such job id
--                self.send_head(http.client.NO_CONTENT)
--
--    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--    # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
--    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--    # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
--    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--
--    def do_GET(self):
--
--        if self.path == "/version":
--            self.send_head()
--            self.server.stats("", "Version check")
--            self.wfile.write(VERSION)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path.startswith("/render"):
--            match = render_pattern.match(self.path)
--
--            if match:
--                job_id = match.groups()[0]
--                frame_number = int(match.groups()[1])
--
--                job = self.server.getJobID(job_id)
--
--                if job:
--                    frame = job[frame_number]
--
--                    if frame:
--                        if frame.status in (QUEUED, DISPATCHED):
--                            self.send_head(http.client.ACCEPTED)
--                        elif frame.status == DONE:
--                            self.server.stats("", "Sending result to client")
--
--                            filename = os.path.join(job.save_path, "%06d.exr" % frame_number)
--
--                            f = open(filename, 'rb')
--                            self.send_head(content = "image/x-exr")
--                            shutil.copyfileobj(f, self.wfile)
--                            f.close()
--                        elif frame.status == ERROR:
--                            self.send_head(http.client.PARTIAL_CONTENT)
--                    else:
--                        # no such frame
--                        self.send_head(http.client.NO_CONTENT)
--                else:
--                    # no such job id
--                    self.send_head(http.client.NO_CONTENT)
--            else:
--                # invalid url
--                self.send_head(http.client.NO_CONTENT)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path.startswith("/thumb"):
--            match = thumb_pattern.match(self.path)
--
--            if match:
--                job_id = match.groups()[0]
--                frame_number = int(match.groups()[1])
--
--                job = self.server.getJobID(job_id)
--
--                if job:
--                    frame = job[frame_number]
--
--                    if frame:
--                        if frame.status in (QUEUED, DISPATCHED):
--                            self.send_head(http.client.ACCEPTED)
--                        elif frame.status == DONE:
--                            filename = os.path.join(job.save_path, "%06d.exr" % frame_number)
--
--                            thumbname = thumbnail.generate(filename)
--
--                            if thumbname:
--                                f = open(thumbname, 'rb')
--                                self.send_head(content = "image/jpeg")
--                                shutil.copyfileobj(f, self.wfile)
--                                f.close()
--                            else: # thumbnail couldn't be generated
--                                self.send_head(http.client.PARTIAL_CONTENT)
--                                return
--                        elif frame.status == ERROR:
--                            self.send_head(http.client.PARTIAL_CONTENT)
--                    else:
--                        # no such frame
--                        self.send_head(http.client.NO_CONTENT)
--                else:
--                    # no such job id
--                    self.send_head(http.client.NO_CONTENT)
--            else:
--                # invalid url
--                self.send_head(http.client.NO_CONTENT)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path.startswith("/log"):
--            match = log_pattern.match(self.path)
--
--            if match:
--                job_id = match.groups()[0]
--                frame_number = int(match.groups()[1])
--
--                job = self.server.getJobID(job_id)
--
--                if job:
--                    frame = job[frame_number]
--
--                    if frame:
--                        if not frame.log_path or frame.status in (QUEUED, DISPATCHED):
--                            self.send_head(http.client.PROCESSING)
--                        else:
--                            self.server.stats("", "Sending log to client")
--                            f = open(frame.log_path, 'rb')
--
--                            self.send_head(content = "text/plain")
--
--                            shutil.copyfileobj(f, self.wfile)
--
--                            f.close()
--                    else:
--                        # no such frame
--                        self.send_head(http.client.NO_CONTENT)
--                else:
--                    # no such job id
--                    self.send_head(http.client.NO_CONTENT)
--            else:
--                # invalid URL
--                self.send_head(http.client.NO_CONTENT)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path == "/status":
--            job_id = self.headers.get('job-id', "")
--            job_frame = int(self.headers.get('job-frame', -1))
--
--            if job_id:
--
--                job = self.server.getJobID(job_id)
--                if job:
--                    if job_frame != -1:
--                        frame = job[frame]
--
--                        if frame:
--                            message = frame.serialize()
--                        else:
--                            # no such frame
--                            self.send_heat(http.client.NO_CONTENT)
--                            return
--                    else:
--                        message = job.serialize()
--                else:
--                    # no such job id
--                    self.send_head(http.client.NO_CONTENT)
--                    return
--            else: # status of all jobs
--                message = []
--
--                for job in self.server:
--                    message.append(job.serialize())
--
--
--            self.server.stats("", "Sending status")
--            self.send_head()
--            self.wfile.write(bytes(json.dumps(message), encoding='utf8'))
--
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path == "/job":
--            self.server.balance()
--
--            slave_id = self.headers['slave-id']
--
--            slave = self.server.getSeenSlave(slave_id)
--
--            if slave: # only if slave id is valid
--                job, frames = self.server.newDispatch(slave_id)
--
--                if job and frames:
--                    for f in frames:
--                        print("dispatch", f.number)
--                        f.status = DISPATCHED
--                        f.slave = slave
--
--                    slave.job = job
--                    slave.job_frames = [f.number for f in frames]
--
--                    self.send_head(headers={"job-id": job.id})
--
--                    message = job.serialize(frames)
--
--                    self.wfile.write(bytes(json.dumps(message), encoding='utf8'))
--
--                    self.server.stats("", "Sending job to slave")
--                else:
--                    # no job available, return error code
--                    slave.job = None
--                    slave.job_frames = []
--
--                    self.send_head(http.client.ACCEPTED)
--            else: # invalid slave id
--                self.send_head(http.client.NO_CONTENT)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path.startswith("/file"):
--            match = file_pattern.match(self.path)
--
--            if match:
--                slave_id = self.headers['slave-id']
--                slave = self.server.getSeenSlave(slave_id)
--
--                if not slave:
--                    # invalid slave id
--                    print("invalid slave id")
--
--                job_id = match.groups()[0]
--                file_index = int(match.groups()[1])
--
--                job = self.server.getJobID(job_id)
--
--                if job:
--                    render_file = job.files[file_index]
--
--                    if render_file:
--                        self.server.stats("", "Sending file to slave")
--                        f = open(render_file.filepath, 'rb')
--
--                        self.send_head()
--                        shutil.copyfileobj(f, self.wfile)
--
--                        f.close()
--                    else:
--                        # no such file
--                        self.send_head(http.client.NO_CONTENT)
--                else:
--                    # no such job id
--                    self.send_head(http.client.NO_CONTENT)
--            else: # invalid url
--                self.send_head(http.client.NO_CONTENT)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path == "/slaves":
--            message = []
--
--            self.server.stats("", "Sending slaves status")
--
--            for slave in self.server.slaves:
--                message.append(slave.serialize())
--
--            self.send_head()
--
--            self.wfile.write(bytes(json.dumps(message), encoding='utf8'))
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        else:
--            # hand over the rest to the html section
--            netrender.master_html.get(self)
--
--    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--    # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
--    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--    # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
--    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--    def do_POST(self):
--
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        if self.path == "/job":
--
--            length = int(self.headers['content-length'])
--
--            job_info = netrender.model.RenderJob.materialize(json.loads(str(self.rfile.read(length), encoding='utf8')))
--
--            job_id = self.server.nextJobID()
--
--            job = MRenderJob(job_id, job_info)
--
--            for frame in job_info.frames:
--                frame = job.addFrame(frame.number, frame.command)
--
--            self.server.addJob(job)
--
--            headers={"job-id": job_id}
--
--            if job.testStart():
--                self.server.stats("", "New job, started")
--                self.send_head(headers=headers)
--            else:
--                self.server.stats("", "New job, missing files (%i total)" % len(job.files))
--                self.send_head(http.client.ACCEPTED, headers=headers)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path.startswith("/edit"):
--            match = edit_pattern.match(self.path)
--
--            if match:
--                job_id = match.groups()[0]
--
--                job = self.server.getJobID(job_id)
--
--                if job:
--                    info_map = self.getInfoMap()
--
--                    job.edit(info_map)
--                    self.send_head()
--                else:
--                    # no such job id
--                    self.send_head(http.client.NO_CONTENT)
--            else:
--                # invalid url
--                self.send_head(http.client.NO_CONTENT)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path == "/balance_limit":
--            info_map = self.getInfoMap()
--            for rule_id, limit in info_map.items():
--                try:
--                    rule = self.server.balancer.ruleByID(rule_id)
--                    if rule:
--                        rule.setLimit(limit)
--                except:
--                    pass # invalid type
--
--            self.send_head()
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path == "/balance_enable":
--            info_map = self.getInfoMap()
--            for rule_id, enabled in info_map.items():
--                rule = self.server.balancer.ruleByID(rule_id)
--                if rule:
--                    rule.enabled = enabled
--
--            self.send_head()
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path.startswith("/cancel"):
--            match = cancel_pattern.match(self.path)
--
--            if match:
--                info_map = self.getInfoMap()
--                clear = info_map.get("clear", False)
--
--                job_id = match.groups()[0]
--
--                job = self.server.getJobID(job_id)
--
--                if job:
--                    self.server.stats("", "Cancelling job")
--                    self.server.removeJob(job, clear)
--                    self.send_head()
--                else:
--                    # no such job id
--                    self.send_head(http.client.NO_CONTENT)
--            else:
--                # invalid url
--                self.send_head(http.client.NO_CONTENT)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path.startswith("/pause"):
--            match = pause_pattern.match(self.path)
--
--            if match:
--                info_map = self.getInfoMap()
--                status = info_map.get("status", None)
--
--                job_id = match.groups()[0]
--
--                job = self.server.getJobID(job_id)
--
--                if job:
--                    self.server.stats("", "Pausing job")
--                    job.pause(status)
--                    self.send_head()
--                else:
--                    # no such job id
--                    self.send_head(http.client.NO_CONTENT)
--            else:
--                # invalid url
--                self.send_head(http.client.NO_CONTENT)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path == "/clear":
--            # cancel all jobs
--            info_map = self.getInfoMap()
--            clear = info_map.get("clear", False)
--
--            self.server.stats("", "Clearing jobs")
--            self.server.clear(clear)
--
--            self.send_head()
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path.startswith("/reset"):
--            match = reset_pattern.match(self.path)
--
--            if match:
--                all = match.groups()[0] == 'all'
--                job_id = match.groups()[1]
--                job_frame = int(match.groups()[2])
--
--                job = self.server.getJobID(job_id)
--
--                if job:
--                    if job_frame != 0:
--
--                        frame = job[job_frame]
--                        if frame:
--                            self.server.stats("", "Reset job frame")
--                            frame.reset(all)
--                            self.send_head()
--                        else:
--                            # no such frame
--                            self.send_head(http.client.NO_CONTENT)
--
--                    else:
--                        self.server.stats("", "Reset job")
--                        job.reset(all)
--                        self.send_head()
--
--                else: # job not found
--                    self.send_head(http.client.NO_CONTENT)
--            else: # invalid url
--                self.send_head(http.client.NO_CONTENT)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path == "/slave":
--            length = int(self.headers['content-length'])
--            job_frame_string = self.headers['job-frame']
--
--            self.server.stats("", "New slave connected")
--
--            slave_info = netrender.model.RenderSlave.materialize(json.loads(str(self.rfile.read(length), encoding='utf8')), cache = False)
--
--            slave_id = self.server.addSlave(slave_info.name, self.client_address, slave_info.stats)
--
--            self.send_head(headers = {"slave-id": slave_id})
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path == "/log":
--            length = int(self.headers['content-length'])
--
--            log_info = netrender.model.LogFile.materialize(json.loads(str(self.rfile.read(length), encoding='utf8')))
--
--            slave_id = log_info.slave_id
--
--            slave = self.server.getSeenSlave(slave_id)
--
--            if slave: # only if slave id is valid
--                job = self.server.getJobID(log_info.job_id)
--
--                if job:
--                    self.server.stats("", "Log announcement")
--                    job.addLog(log_info.frames)
--                    self.send_head(http.client.OK)
--                else:
--                    # no such job id
--                    self.send_head(http.client.NO_CONTENT)
--            else: # invalid slave id
--                self.send_head(http.client.NO_CONTENT)
--    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--    # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
--    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--    # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
--    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--    def do_PUT(self):
--
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        if self.path.startswith("/file"):
--            match = file_pattern.match(self.path)
--
--            if match:
--                self.server.stats("", "Receiving job")
--
--                length = int(self.headers['content-length'])
--                job_id = match.groups()[0]
--                file_index = int(match.groups()[1])
--
--                job = self.server.getJobID(job_id)
--
--                if job:
--
--                    render_file = job.files[file_index]
--
--                    if render_file:
--                        main_file = job.files[0].filepath # filename of the first file
--
--                        main_path, main_name = os.path.split(main_file)
--
--                        if file_index > 0:
--                            file_path = prefixPath(job.save_path, render_file.filepath, main_path)
--                        else:
--                            file_path = os.path.join(job.save_path, main_name)
--
--                        buf = self.rfile.read(length)
--
--                        # add same temp file + renames as slave
--                        
--                        f = open(file_path, "wb")
--                        f.write(buf)
--                        f.close()
--                        del buf
--
--                        render_file.filepath = file_path # set the new path
--
--                        if job.testStart():
--                            self.server.stats("", "File upload, starting job")
--                            self.send_head(http.client.OK)
--                        else:
--                            self.server.stats("", "File upload, file missings")
--                            self.send_head(http.client.ACCEPTED)
--                    else: # invalid file
--                        print("file not found", job_id, file_index)
--                        self.send_head(http.client.NO_CONTENT)
--                else: # job not found
--                    print("job not found", job_id, file_index)
--                    self.send_head(http.client.NO_CONTENT)
--            else: # invalid url
--                print("no match")
--                self.send_head(http.client.NO_CONTENT)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path == "/render":
--            self.server.stats("", "Receiving render result")
--
--            # need some message content here or the slave doesn't like it
--            self.wfile.write(bytes("foo", encoding='utf8'))
--
--            slave_id = self.headers['slave-id']
--
--            slave = self.server.getSeenSlave(slave_id)
--
--            if slave: # only if slave id is valid
--                job_id = self.headers['job-id']
--
--                job = self.server.getJobID(job_id)
--
--                if job:
--                    job_frame = int(self.headers['job-frame'])
--                    job_result = int(self.headers['job-result'])
--                    job_time = float(self.headers['job-time'])
--
--                    frame = job[job_frame]
--
--                    if frame:
--                        if job.hasRenderResult():
--                            if job_result == DONE:
--                                length = int(self.headers['content-length'])
--                                buf = self.rfile.read(length)
--                                f = open(os.path.join(job.save_path, "%06d.exr" % job_frame), 'wb')
--                                f.write(buf)
--                                f.close()
--
--                                del buf
--                            elif job_result == ERROR:
--                                # blacklist slave on this job on error
--                                # slaves might already be in blacklist if errors on the whole chunk
--                                if not slave.id in job.blacklist:
--                                    job.blacklist.append(slave.id)
--
--                        slave.finishedFrame(job_frame)
--
--                        frame.status = job_result
--                        frame.time = job_time
--
--                        job.testFinished()
--
--                        self.send_head()
--                    else: # frame not found
--                        self.send_head(http.client.NO_CONTENT)
--                else: # job not found
--                    self.send_head(http.client.NO_CONTENT)
--            else: # invalid slave id
--                self.send_head(http.client.NO_CONTENT)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path == "/thumb":
--            self.server.stats("", "Receiving thumbnail result")
--
--            # need some message content here or the slave doesn't like it
--            self.wfile.write(bytes("foo", encoding='utf8'))
--
--            slave_id = self.headers['slave-id']
--
--            slave = self.server.getSeenSlave(slave_id)
--
--            if slave: # only if slave id is valid
--                job_id = self.headers['job-id']
--
--                job = self.server.getJobID(job_id)
--
--                if job:
--                    job_frame = int(self.headers['job-frame'])
--
--                    frame = job[job_frame]
--
--                    if frame:
--                        if job.hasRenderResult():
--                            length = int(self.headers['content-length'])
--                            buf = self.rfile.read(length)
--                            f = open(os.path.join(job.save_path, "%06d.jpg" % job_frame), 'wb')
--                            f.write(buf)
--                            f.close()
--
--                            del buf
--
--                    else: # frame not found
--                        self.send_head(http.client.NO_CONTENT)
--                else: # job not found
--                    self.send_head(http.client.NO_CONTENT)
--            else: # invalid slave id
--                self.send_head(http.client.NO_CONTENT)
--        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--        elif self.path.startswith("/log"):
--            self.server.stats("", "Receiving log file")
--
--            match = log_pattern.match(self.path)
--
--            if match:
--                job_id = match.groups()[0]
--
--                job = self.server.getJobID(job_id)
--
--                if job:
--                    job_frame = int(match.groups()[1])
--
--                    frame = job[job_frame]
--
--                    if frame and frame.log_path:
--                        length = int(self.headers['content-length'])
--                        buf = self.rfile.read(length)
--                        f = open(frame.log_path, 'ab')
--                        f.write(buf)
--                        f.close()
--
--                        del buf
--
--                        self.server.getSeenSlave(self.headers['slave-id'])
--
--                        self.send_head()
--                    else: # frame not found
--                        self.send_head(http.client.NO_CONTENT)
--                else: # job not found
--                    self.send_head(http.client.NO_CONTENT)
--            else: # invalid url
--                self.send_head(http.client.NO_CONTENT)
--
--class RenderMasterServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
--    def __init__(self, address, handler_class, path, subdir=True):
--        super().__init__(address, handler_class)
--        self.jobs = []
--        self.jobs_map = {}
--        self.slaves = []
--        self.slaves_map = {}
--        self.job_id = 0
--
--        if subdir:
--            self.path = os.path.join(path, "master_" + str(os.getpid()))
--        else:
--            self.path = path
--
--        self.slave_timeout = 5 # 5 mins: need a parameter for that
--
--        self.balancer = netrender.balancing.Balancer()
--        self.balancer.addRule(netrender.balancing.RatingUsageByCategory(self.getJobs))
--        self.balancer.addRule(netrender.balancing.RatingUsage())
--        self.balancer.addException(netrender.balancing.ExcludeQueuedEmptyJob())
--        self.balancer.addException(netrender.balancing.ExcludeSlavesLimit(self.countJobs, self.countSlaves, limit = 0.9))
--        self.balancer.addPriority(netrender.balancing.NewJobPriority())
--        self.balancer.addPriority(netrender.balancing.MinimumTimeBetweenDispatchPriority(limit = 2))
--
--        if not os.path.exists(self.path):
--            os.mkdir(self.path)
--
--    def restore(self, jobs, slaves, balancer = None):
--        self.jobs = jobs
--        self.jobs_map = {}
--        
--        for job in self.jobs:
--            self.jobs_map[job.id] = job
--            self.job_id = max(self.job_id, int(job.id))
--
--        self.slaves = slaves
--        for slave in self.slaves:
--            self.slaves_map[slave.id] = slave
--        
--        if balancer:
--            self.balancer = balancer
--        
--
--    def nextJobID(self):
--        self.job_id += 1
--        return str(self.job_id)
--
--    def addSlave(self, name, address, stats):
--        slave = MRenderSlave(name, address, stats)
--        self.slaves.append(slave)
--        self.slaves_map[slave.id] = slave
--
--        return slave.id
--
--    def removeSlave(self, slave):
--        self.slaves.remove(slave)
--        self.slaves_map.pop(slave.id)
--
--    def getSlave(self, slave_id):
--        return self.slaves_map.get(slave_id)
--
--    def getSeenSlave(self, slave_id):
--        slave = self.getSlave(slave_id)
--        if slave:
--            slave.seen()
--
--        return slave
--
--    def timeoutSlaves(self):
--        removed = []
--
--        t = time.time()
--
--        for slave in self.slaves:
--            if (t - slave.last_seen) / 60 > self.slave_timeout:
--                removed.append(slave)
--
--                if slave.job:
--                    for f in slave.job_frames:
--                        slave.job[f].status = ERROR
--
--        for slave in removed:
--            self.removeSlave(slave)
--
--    def updateUsage(self):
--        blend = 0.5
--        for job in self.jobs:
--            job.usage *= (1 - blend)
--
--        if self.slaves:
--            slave_usage = blend / self.countSlaves()
--
--            for slave in self.slaves:
--                if slave.job:
--                    slave.job.usage += slave_usage
--
--
--    def clear(self, clear_files = False):
--        removed = self.jobs[:]
--
--        for job in removed:
--            self.removeJob(job, clear_files)
--
--    def balance(self):
--        self.balancer.balance(self.jobs)
--
--    def getJobs(self):
--        return self.jobs
--
--    def countJobs(self, status = JOB_QUEUED):
--        total = 0
--        for j in self.jobs:
--            if j.status == status:
--                total += 1
--
--        return total
--
--    def countSlaves(self):
--        return len(self.slaves)
--
--    def removeJob(self, job, clear_files = False):
--        self.jobs.remove(job)
--        self.jobs_map.pop(job.id)
--
--        if clear_files:
--            shutil.rmtree(job.save_path)
--
--        for slave in self.slaves:
--            if slave.job == job:
--                slave.job = None
--                slave.job_frames = []
--
--    def addJob(self, job):
--        self.jobs.append(job)
--        self.jobs_map[job.id] = job
--
--        # create job directory
--        job.save_path = os.path.join(self.path, "job_" + job.id)
--        if not os.path.exists(job.save_path):
--            os.mkdir(job.save_path)
--
--        job.save()
--
--    def getJobID(self, id):
--        return self.jobs_map.get(id)
--
--    def __iter__(self):
--        for job in self.jobs:
--            yield job
--
--    def newDispatch(self, slave_id):
--        if self.jobs:
--            for job in self.jobs:
--                if not self.balancer.applyExceptions(job) and slave_id not in job.blacklist:
--                    return job, job.getFrames()
--
--        return None, None
--
--def clearMaster(path):
--    shutil.rmtree(path)
--
--def createMaster(address, clear, path):
--    filepath = os.path.join(path, "blender_master.data")
--
--    if not clear and os.path.exists(filepath):
--        print("loading saved master:", filepath)
--        with open(filepath, 'rb') as f:
--            path, jobs, slaves = pickle.load(f)
--            
--            httpd = RenderMasterServer(address, RenderHandler, path, subdir=False)
--            httpd.restore(jobs, slaves)
--            
--            return httpd
--
--    return RenderMasterServer(address, RenderHandler, path)
--
--def saveMaster(path, httpd):
--    filepath = os.path.join(path, "blender_master.data")
--    
--    with open(filepath, 'wb') as f:
--        pickle.dump((httpd.path, httpd.jobs, httpd.slaves), f, pickle.HIGHEST_PROTOCOL)
--
--def runMaster(address, broadcast, clear, path, update_stats, test_break):
--        httpd = createMaster(address, clear, path)
--        httpd.timeout = 1
--        httpd.stats = update_stats
--
--        if broadcast:
--            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
--            s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
--
--        start_time = time.time() - 2
--
--        while not test_break():
--            try:
--                httpd.handle_request()
--            except select.error:
--                pass
--
--            if time.time() - start_time >= 2: # need constant here
--                httpd.timeoutSlaves()
--
--                httpd.updateUsage()
--
--                if broadcast:
--                        print("broadcasting address")
--                        s.sendto(bytes("%i" % address[1], encoding='utf8'), 0, ('<broadcast>', 8000))
--                        start_time = time.time()
--
--        httpd.server_close()
--        if clear:
--            clearMaster(httpd.path)
--        else:
--            saveMaster(path, httpd)
--
diff --cc release/scripts/io/netrender/master_html.py
index 877273207a84a28e896ccbd38d37c74fb06e5556,877273207a84a28e896ccbd38d37c74fb06e5556..0000000000000000000000000000000000000000
deleted file mode 100644,100644
+++ /dev/null
@@@ -1,315 -1,315 +1,0 @@@
--# ##### BEGIN GPL LICENSE BLOCK #####
--#
--#  This program is free software; you can redistribute it and/or
--#  modify it under the terms of the GNU General Public License
--#  as published by the Free Software Foundation; either version 2
--#  of the License, or (at your option) any later version.
--#
--#  This program is distributed in the hope that it will be useful,
--#  but WITHOUT ANY WARRANTY; without even the implied warranty of
--#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--#  GNU General Public License for more details.
--#
--#  You should have received a copy of the GNU General Public License
--#  along with this program; if not, write to the Free Software Foundation,
--#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
--#
--# ##### END GPL LICENSE BLOCK #####
--
--import os
--import re
--import shutil
--from netrender.utils import *
--import netrender.model
--
--src_folder = os.path.split(__file__)[0]
--
--def get(handler):
--    def output(text):
--        handler.wfile.write(bytes(text, encoding='utf8'))
--
--    def head(title, refresh = False):
--        output("<html><head>")
--        if refresh:
--            output("<meta http-equiv='refresh' content=5>")
--        output("<script src='/html/netrender.js' type='text/javascript'></script>")
--#             output("<script src='/html/json2.js' type='text/javascript'></script>")
--        output("<title>")
--        output(title)
--        output("</title></head><body>")
--        output("<link rel='stylesheet' href='/html/netrender.css' type='text/css'>")
--
--
--    def link(text, url, script=""):
--        return "<a href='%s' %s>%s</a>" % (url, script, text)
--
--    def tag(name, text, attr=""):
--        return "<%s %s>%s</%s>" % (name, attr, text, name)
--
--    def startTable(border=1, class_style = None, caption = None):
--        output("<table border='%i'" % border)
--
--        if class_style:
--            output(" class='%s'" % class_style)
--
--        output(">")
--
--        if caption:
--            output("<caption>%s</caption>" % caption)
--
--    def headerTable(*headers):
--        output("<thead><tr>")
--
--        for c in headers:
--            output("<td>" + c + "</td>")
--
--        output("</tr></thead>")
--
--    def rowTable(*data, id = None, class_style = None, extra = None):
--        output("<tr")
--
--        if id:
--            output(" id='%s'" % id)
--
--        if class_style:
--            output(" class='%s'" % class_style)
--
--        if extra:
--            output(" %s" % extra)
--
--        output(">")
--
--        for c in data:
--            output("<td>" + str(c) + "</td>")
--
--        output("</tr>")
--
--    def endTable():
--        output("</table>")
--
--    def checkbox(title, value, script=""):
--        return """<input type="checkbox" title="%s" %s %s>""" % (title, "checked" if value else "", ("onclick=\"%s\"" % script) if script else "")
--
--    if handler.path == "/html/netrender.js":
--        f = open(os.path.join(src_folder, "netrender.js"), 'rb')
--
--        handler.send_head(content = "text/javascript")
--        shutil.copyfileobj(f, handler.wfile)
--
--        f.close()
--    elif handler.path == "/html/netrender.css":
--        f = open(os.path.join(src_folder, "netrender.css"), 'rb')
--
--        handler.send_head(content = "text/css")
--        shutil.copyfileobj(f, handler.wfile)
--
--        f.close()
--    elif handler.path == "/html" or handler.path == "/":
--        handler.send_head(content = "text/html")
--        head("NetRender", refresh = True)
--
--        output("<h2>Jobs</h2>")
--
--        startTable()
--        headerTable(
--                        "&nbsp;",
--                        "id",
--                        "name",
--                        "category",
--                        "type",
--                        "chunks",
--                        "priority",
--                        "usage",
--                        "wait",
--                        "status",
--                        "length",
--                        "done",
--                        "dispatched",
--                        "error",
--                        "priority",
--                        "exception"
--                    )
--
--        handler.server.balance()
--
--        for job in handler.server.jobs:
--            results = job.framesStatus()
--            rowTable(
--                        """<button title="cancel job" onclick="cancel_job('%s');">X</button>""" % job.id +
--                        """<button title="pause job" onclick="request('/pause_%s', null);">P</button>""" % job.id +
--                        """<button title="reset all frames" onclick="request('/resetall_%s_0', null);">R</button>""" % job.id,
--                        job.id,
--                        link(job.name, "/html/job" + job.id),
--                        job.category if job.category else "<i>None</i>",
--                        netrender.model.JOB_TYPES[job.type],
--                        str(job.chunks) +
--                        """<button title="increase chunks size" onclick="request('/edit_%s', &quot;{'chunks': %i}&quot;);">+</button>""" % (job.id, job.chunks + 1) +
--                        """<button title="decrease chunks size" onclick="request('/edit_%s', &quot;{'chunks': %i}&quot;);" %s>-</button>""" % (job.id, job.chunks - 1, "disabled=True" if job.chunks == 1 else ""),
--                        str(job.priority) +
--                        """<button title="increase priority" onclick="request('/edit_%s', &quot;{'priority': %i}&quot;);">+</button>""" % (job.id, job.priority + 1) +
--                        """<button title="decrease priority" onclick="request('/edit_%s', &quot;{'priority': %i}&quot;);" %s>-</button>""" % (job.id, job.priority - 1, "disabled=True" if job.priority == 1 else ""),
--                        "%0.1f%%" % (job.usage * 100),
--                        "%is" % int(time.time() - job.last_dispatched),
--                        job.statusText(),
--                        len(job),
--                        results[DONE],
--                        results[DISPATCHED],
--                        str(results[ERROR]) +
--                        """<button title="reset error frames" onclick="request('/reset_%s_0', null);" %s>R</button>""" % (job.id, "disabled=True" if not results[ERROR] else ""),
--                        "yes" if handler.server.balancer.applyPriorities(job) else "no",
--                        "yes" if handler.server.balancer.applyExceptions(job) else "no"
--                    )
--
--        endTable()
--        
--        output("<h2>Slaves</h2>")
--
--        startTable()
--        headerTable("name", "address", "last seen", "stats", "job")
--
--        for slave in handler.server.slaves:
--            rowTable(slave.name, slave.address[0], time.ctime(slave.last_seen), slave.stats, link(slave.job.name, "/html/job" + slave.job.id) if slave.job else "None")
--
--        endTable()
--
--        output("<h2>Configuration</h2>")
--
--        output("""<button title="remove all jobs" onclick="clear_jobs();">CLEAR JOB LIST</button>""")
--
--        startTable(caption = "Rules", class_style = "rules")
--
--        headerTable("type", "enabled", "description", "limit")
--
--        for rule in handler.server.balancer.rules:
--            rowTable(
--                        "rating",
--                        checkbox("", rule.enabled, "balance_enable('%s', '%s')" % (rule.id(), str(not rule.enabled).lower())),
--                        rule,
--                        rule.str_limit() +
--                        """<button title="edit limit" onclick="balance_edit('%s', '%s');">edit</button>""" % (rule.id(), str(rule.limit)) if hasattr(rule, "limit") else "&nbsp;"
--                    )
--
--        for rule in handler.server.balancer.priorities:
--            rowTable(
--                        "priority",
--                        checkbox("", rule.enabled, "balance_enable('%s', '%s')" % (rule.id(), str(not rule.enabled).lower())),
--                        rule,
--                        rule.str_limit() +
--                        """<button title="edit limit" onclick="balance_edit('%s', '%s');">edit</button>""" % (rule.id(), str(rule.limit)) if hasattr(rule, "limit") else "&nbsp;"
--                    )
--
--        for rule in handler.server.balancer.exceptions:
--            rowTable(
--                        "exception",
--                        checkbox("", rule.enabled, "balance_enable('%s', '%s')" % (rule.id(), str(not rule.enabled).lower())),
--                        rule,
--                        rule.str_limit() +
--                        """<button title="edit limit" onclick="balance_edit('%s', '%s');">edit</button>""" % (rule.id(), str(rule.limit)) if hasattr(rule, "limit") else "&nbsp;"
--                    )
--
--        endTable()
--
--        output("</body></html>")
--
--    elif handler.path.startswith("/html/job"):
--        handler.send_head(content = "text/html")
--        job_id = handler.path[9:]
--
--        head("NetRender")
--
--        job = handler.server.getJobID(job_id)
--
--        if job:
--            output("<h2>Render Information</h2>")
--
--            job.initInfo()
--
--            startTable()
--
--            rowTable("resolution", "%ix%i at %i%%" % job.resolution)
--
--            endTable()
--
--
--            if job.type == netrender.model.JOB_BLENDER:
--                output("<h2>Files</h2>")
--                
--                startTable()
--                headerTable("path")
--    
--                tot_cache = 0
--                tot_fluid = 0
--    
--                rowTable(job.files[0].filepath)
--                rowTable("Other Files", class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.other&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
--    
--                for file in job.files:
--                    if file.filepath.endswith(".bphys"):
--                        tot_cache += 1
--                    elif file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
--                        tot_fluid += 1
--                    else:
--                        if file != job.files[0]:
--                            rowTable(file.filepath, class_style = "other")
--    
--                if tot_cache > 0:
--                    rowTable("%i physic cache files" % tot_cache, class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.cache&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
--                    for file in job.files:
--                        if file.filepath.endswith(".bphys"):
--                            rowTable(os.path.split(file.filepath)[1], class_style = "cache")
--    
--                if tot_fluid > 0:
--                    rowTable("%i fluid bake files" % tot_fluid, class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.fluid&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
--                    for file in job.files:
--                        if file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
--                            rowTable(os.path.split(file.filepath)[1], class_style = "fluid")
--    
--                endTable()
--            elif job.type == netrender.model.JOB_VCS:
--                output("<h2>Versioning</h2>")
--                
--                startTable()
--    
--                rowTable("System", job.version_info.system.name)
--                rowTable("Remote Path", job.version_info.rpath)
--                rowTable("Working Path", job.version_info.wpath)
--                rowTable("Revision", job.version_info.revision)
--                rowTable("Render File", job.files[0].filepath)
--    
--                endTable()
--
--            if job.blacklist:
--                output("<h2>Blacklist</h2>")
--
--                startTable()
--                headerTable("name", "address")
--
--                for slave_id in job.blacklist:
--                    slave = handler.server.slaves_map[slave_id]
--                    rowTable(slave.name, slave.address[0])
--
--                endTable()
--
--            output("<h2>Frames</h2>")
--
--            startTable()
--            headerTable("no", "status", "render time", "slave", "log", "result", "")
--
--            for frame in job.frames:
--                rowTable(
--                         frame.number,
--                         frame.statusText(),
--                         "%.1fs" % frame.time,
--                         frame.slave.name if frame.slave else "&nbsp;",
--                         link("view log", logURL(job_id, frame.number)) if frame.log_path else "&nbsp;",
--                         link("view result", renderURL(job_id, frame.number))  + " [" +
--                         tag("span", "show", attr="class='thumb' onclick='showThumb(%s, %i)'" % (job.id, frame.number)) + "]" if frame.status == DONE else "&nbsp;",
--                         "<img name='thumb%i' title='hide thumbnails' src='' class='thumb' onclick='showThumb(%s, %i)'>" % (frame.number, job.id, frame.number)
--                         )
--
--            endTable()
--        else:
--            output("no such job")
--
--        output("</body></html>")
--
diff --cc release/scripts/io/netrender/model.py
index 5fc0bc2a0bb9ea286cf05935567699471c6d6f06,5fc0bc2a0bb9ea286cf05935567699471c6d6f06..0000000000000000000000000000000000000000
deleted file mode 100644,100644
+++ /dev/null
@@@ -1,360 -1,360 +1,0 @@@
--# ##### BEGIN GPL LICENSE BLOCK #####
--#
--#  This program is free software; you can redistribute it and/or
--#  modify it under the terms of the GNU General Public License
--#  as published by the Free Software Foundation; either version 2
--#  of the License, or (at your option) any later version.
--#
--#  This program is distributed in the hope that it will be useful,
--#  but WITHOUT ANY WARRANTY; without even the implied warranty of
--#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--#  GNU General Public License for more details.
--#
--#  You should have received a copy of the GNU General Public License
--#  along with this program; if not, write to the Free Software Foundation,
--#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
--#
--# ##### END GPL LICENSE BLOCK #####
--
--import sys, os
--import http, http.client, http.server, urllib
--import subprocess, shutil, time, hashlib
--
--import netrender.versioning as versioning
--from netrender.utils import *
--
--class LogFile:
--    def __init__(self, job_id = 0, slave_id = 0, frames = []):
--        self.job_id = job_id
--        self.slave_id = slave_id
--        self.frames = frames
--
--    def serialize(self):
--        return        {
--                            "job_id": self.job_id,
--                            "slave_id": self.slave_id,
--                            "frames": self.frames
--                        }
--
--    @staticmethod
--    def materialize(data):
--        if not data:
--            return None
--
--        logfile = LogFile()
--        logfile.job_id = data["job_id"]
--        logfile.slave_id = data["slave_id"]
--        logfile.frames = data["frames"]
--
--        return logfile
--
--class RenderSlave:
--    _slave_map = {}
--
--    def __init__(self):
--        self.id = ""
--        self.name = ""
--        self.address = ("",0)
--        self.stats = ""
--        self.total_done = 0
--        self.total_error = 0
--        self.last_seen = 0.0
--
--    def serialize(self):
--        return        {
--                            "id": self.id,
--                            "name": self.name,
--                            "address": self.address,
--                            "stats": self.stats,
--                            "total_done": self.total_done,
--                            "total_error": self.total_error,
--                            "last_seen": self.last_seen
--                        }
--
--    @staticmethod
--    def materialize(data, cache = True):
--        if not data:
--            return None
--
--        slave_id = data["id"]
--
--        if cache and slave_id in RenderSlave._slave_map:
--            return RenderSlave._slave_map[slave_id]
--
--        slave = RenderSlave()
--        slave.id = slave_id
--        slave.name = data["name"]
--        slave.address = data["address"]
--        slave.stats = data["stats"]
--        slave.total_done = data["total_done"]
--        slave.total_error = data["total_error"]
--        slave.last_seen = data["last_seen"]
--
--        if cache:
--            RenderSlave._slave_map[slave_id] = slave
--
--        return slave
--
--JOB_BLENDER = 1
--JOB_PROCESS = 2
--JOB_VCS     = 3
--
--JOB_TYPES = {
--                JOB_BLENDER: "Blender",
--                JOB_PROCESS: "Process",
--                JOB_VCS:     "Versioned",
--            }
--
--class VersioningInfo:
--    def __init__(self, info = None):
--        self._system = None
--        self.wpath = ""
--        self.rpath = ""
--        self.revision = ""
--        
--    @property
--    def system(self):
--        return self._system
--
--    @system.setter
--    def system(self, value):
--        self._system = versioning.SYSTEMS[value]
--
--    def update(self):
--        self.system.update(self)
--    
--    def serialize(self):
--        return {
--                "wpath": self.wpath,
--                "rpath": self.rpath,
--                "revision": self.revision,
--                "system": self.system.name
--                }
--        
--    @staticmethod
--    def generate(system, path):
--        vs = VersioningInfo()
--        vs.wpath = path
--        vs.system = system
--
--        vs.rpath = vs.system.path(path)
--        vs.revision = vs.system.revision(path)
--        
--        return vs
--        
--        
--    @staticmethod
--    def materialize(data):
--        if not data:
--            return None
--        
--        vs = VersioningInfo()
--        vs.wpath = data["wpath"]
--        vs.rpath = data["rpath"]
--        vs.revision = data["revision"]
--        vs.system = data["system"]
--        
--        return vs
--        
--
--class RenderFile:
--    def __init__(self, filepath = "", index = 0, start = -1, end = -1, signature=0):
--        self.filepath = filepath
--        self.original_path = filepath
--        self.signature = signature
--        self.index = index
--        self.start = start
--        self.end = end
--
--    def serialize(self):
--        return        {
--                    "filepath": self.filepath,
--                    "original_path": self.original_path,
--                    "index": self.index,
--                    "start": self.start,
--                    "end": self.end,
--                    "signature": self.signature
--                }
--
--    @staticmethod
--    def materialize(data):
--        if not data:
--            return None
--
--        rfile = RenderFile(data["filepath"], data["index"], data["start"], data["end"], data["signature"])
--        rfile.original_path = data["original_path"]
--
--        return rfile
--
--class RenderJob:
--    def __init__(self, job_info = None):
--        self.id = ""
--        self.type = JOB_BLENDER
--        self.name = ""
--        self.category = "None"
--        self.status = JOB_WAITING
--        self.files = []
--        self.chunks = 0
--        self.priority = 0
--        self.blacklist = []
--        
--        self.version_info = None
--        
--        self.resolution = None
--
--        self.usage = 0.0
--        self.last_dispatched = 0.0
--        self.frames = []
--
--        if job_info:
--            self.type = job_info.type
--            self.name = job_info.name
--            self.category = job_info.category
--            self.status = job_info.status
--            self.files = job_info.files
--            self.chunks = job_info.chunks
--            self.priority = job_info.priority
--            self.blacklist = job_info.blacklist
--            self.version_info = job_info.version_info
--
--    def hasRenderResult(self):
--        return self.type in (JOB_BLENDER, JOB_VCS)
--
--    def rendersWithBlender(self):
--        return self.type in (JOB_BLENDER, JOB_VCS)
--
--    def addFile(self, file_path, start=-1, end=-1, signed=True):
--        if signed:
--            signature = hashFile(file_path)
--        else:
--            signature = None
--        self.files.append(RenderFile(file_path, len(self.files), start, end, signature))
--
--    def addFrame(self, frame_number, command = ""):
--        frame = RenderFrame(frame_number, command)
--        self.frames.append(frame)
--        return frame
--
--    def __len__(self):
--        return len(self.frames)
--
--    def countFrames(self, status=QUEUED):
--        total = 0
--        for f in self.frames:
--            if f.status == status:
--                total += 1
--
--        return total
--
--    def countSlaves(self):
--        return len(set((frame.slave for frame in self.frames if frame.status == DISPATCHED)))
--
--    def statusText(self):
--        return JOB_STATUS_TEXT[self.status]
--
--    def framesStatus(self):
--        results = {
--                                QUEUED: 0,
--                                DISPATCHED: 0,
--                                DONE: 0,
--                                ERROR: 0
--                            }
--
--        for frame in self.frames:
--            results[frame.status] += 1
--
--        return results
--
--    def __contains__(self, frame_number):
--        for f in self.frames:
--            if f.number == frame_number:
--                return True
--        else:
--            return False
--
--    def __getitem__(self, frame_number):
--        for f in self.frames:
--            if f.number == frame_number:
--                return f
--        else:
--            return None
--
--    def serialize(self, frames = None):
--        min_frame = min((f.number for f in frames)) if frames else -1
--        max_frame = max((f.number for f in frames)) if frames else -1
--        return        {
--                            "id": self.id,
--                            "type": self.type,
--                            "name": self.name,
--                            "category": self.category,
--                            "status": self.status,
--                            "files": [f.serialize() for f in self.files if f.start == -1 or not frames or (f.start <= max_frame and f.end >= min_frame)],
--                            "frames": [f.serialize() for f in self.frames if not frames or f in frames],
--                            "chunks": self.chunks,
--                            "priority": self.priority,
--                            "usage": self.usage,
--                            "blacklist": self.blacklist,
--                            "last_dispatched": self.last_dispatched,
--                            "version_info": self.version_info.serialize() if self.version_info else None,
--                            "resolution": self.resolution
--                        }
--
--    @staticmethod
--    def materialize(data):
--        if not data:
--            return None
--
--        job = RenderJob()
--        job.id = data["id"]
--        job.type = data["type"]
--        job.name = data["name"]
--        job.category = data["category"]
--        job.status = data["status"]
--        job.files = [RenderFile.materialize(f) for f in data["files"]]
--        job.frames = [RenderFrame.materialize(f) for f in data["frames"]]
--        job.chunks = data["chunks"]
--        job.priority = data["priority"]
--        job.usage = data["usage"]
--        job.blacklist = data["blacklist"]
--        job.last_dispatched = data["last_dispatched"]
--        job.resolution = data["resolution"]
--        
--        version_info = data.get("version_info", None)
--        if version_info:
--            job.version_info = VersioningInfo.materialize(version_info)
--
--        return job
--
--class RenderFrame:
--    def __init__(self, number = 0, command = ""):
--        self.number = number
--        self.time = 0
--        self.status = QUEUED
--        self.slave = None
--        self.command = command
--
--    def statusText(self):
--        return FRAME_STATUS_TEXT[self.status]
--
--    def serialize(self):
--        return        {
--                            "number": self.number,
--                            "time": self.time,
--                            "status": self.status,
--                            "slave": None if not self.slave else self.slave.serialize(),
--                            "command": self.command
--                        }
--
--    @staticmethod
--    def materialize(data):
--        if not data:
--            return None
--
--        frame = RenderFrame()
--        frame.number = data["number"]
--        frame.time = data["time"]
--        frame.status = data["status"]
--        frame.slave = RenderSlave.materialize(data["slave"])
--        frame.command = data["command"]
--
--        return frame
diff --cc release/scripts/io/netrender/netrender.css
index 0c54690e0024c743c681c7cb7966d262a9dcb065,0c54690e0024c743c681c7cb7966d262a9dcb065..0000000000000000000000000000000000000000
deleted file mode 100644,100644
+++ /dev/null
@@@ -1,88 -1,88 +1,0 @@@
--body {
--        background-color:#eee;
--        font-size:12px;
--      font-family: "Lucida Sans","Lucida Sans Unicode","Lucida Grande",Lucida,sans-serif;
--      
--}
--a {
--      /*text-decoration:none;*/
--      color:#666;
--}
--a:hover {
--      color:#000;
--}
--h2 {
--        background-color:#ddd;
--        font-size:120%;
--        padding:5px;
--}
--
--h2 {
--        background-color:#ddd;
--        font-size:110%;
--        padding:5px;
--}
--
--table {
--      text-align:center;
--        border:0;
--        background-color:#ddd;
--      padding: 0px;
--      margin: 0px;
--}
--thead{
--       font-size:90%;
--       color:#555;
--       background-color:#ccc;
--}
--td {
--        border:0;
--        padding:2px;
--        padding-left:10px;
--        padding-right:10px;
--        margin-left:20px;
--        background-color:#ddd;
--}
--td:hover {
--        background-color:#ccc;
--}
--tr {
--        border:0;
--}
--button {
--      color: #111;
--      width: auto;
--      height: auto;
--}
--
--.toggle {
--      text-decoration: underline;
--      cursor: pointer;
--}
--
--.cache {
--      display: none;
--}
--
--.fluid {
--      display: none;
--}
--
--.other {
--      display: none;
--}
--
--.rules {
--      width: 60em;
--      text-align: left;
--}
--
--img.thumb {
--      display: none;
--      cursor: pointer;
--}
--
--span.thumb {
--      text-decoration: underline;
--      cursor: pointer;
--}
diff --cc release/scripts/io/netrender/netrender.js
index 1024a169571804033657953fad194bfe2d7301fb,1024a169571804033657953fad194bfe2d7301fb..0000000000000000000000000000000000000000
deleted file mode 100644,100644
+++ /dev/null
@@@ -1,146 -1,146 +1,0 @@@
--lastFrame = -1
--maxFrame = -1
--minFrame = -1
--
--function request(url, data)
--{
--      xmlhttp = new XMLHttpRequest();
--      xmlhttp.open("POST", url, false);
--      xmlhttp.send(data);
--      window.location.reload()        
--}
--
--function edit(id, info)
--{
--      request("/edit_" + id, info)
--}
--
--function clear_jobs()
--{
--      var r=confirm("Also delete files on master?");
--      
--      if (r==true) {
--              request('/clear', '{"clear":true}');
--      } else {
--              request('/clear', '{"clear":false}');
--      }
--}
--
--function cancel_job(id)
--{
--      var r=confirm("Also delete files on master?");
--      
--      if (r==true) {
--              request('/cancel_' + id, '{"clear":true}');
--      } else {
--              request('/cancel_' + id, '{"clear":false}');
--      }
--}
--
--function balance_edit(id, old_value)
--{
--      var new_value = prompt("New limit", old_value);
--      if (new_value != null && new_value != "") {
--              request("/balance_limit", '{"' + id + '":"' + new_value + '"}');
--      }
--}
--
--function balance_enable(id, value)
--{
--      request("/balance_enable", '{"' + id + '":' + value + "}");
--}
--
--function showThumb(job, frame)
--{
--      if (lastFrame != -1) {
--              if (maxFrame != -1 && minFrame != -1) {
--                      if (frame >= minFrame && frame <= maxFrame) {
--                              for(i = minFrame; i <= maxFrame; i=i+1) {
--                                      toggleThumb(job, i);
--                              }
--                              minFrame = -1;
--                              maxFrame = -1;
--                              lastFrame = -1;
--                      } else if (frame > maxFrame) {
--                              for(i = maxFrame+1; i <= frame; i=i+1) {
--                                      toggleThumb(job, i);
--                              }
--                              maxFrame = frame;
--                              lastFrame = frame;
--                      } else {
--                              for(i = frame; i <= minFrame-1; i=i+1) {
--                                      toggleThumb(job, i);
--                              }
--                              minFrame = frame;
--                              lastFrame = frame;
--                      }
--              } else if (frame == lastFrame) {
--                      toggleThumb(job, frame);
--              } else if (frame < lastFrame) {
--                      minFrame = frame;
--                      maxFrame = lastFrame;
--
--                      for(i = minFrame; i <= maxFrame-1; i=i+1) {
--                              toggleThumb(job, i);
--                      }
--                      lastFrame = frame;
--              } else {
--                      minFrame = lastFrame;
--                      maxFrame = frame;
--
--                      for(i = minFrame+1; i <= maxFrame; i=i+1) {
--                              toggleThumb(job, i);
--                      }
--                      lastFrame = frame;
--              }
--      } else {
--              toggleThumb(job, frame);
--      }
--}
--
--function toggleThumb(job, frame)
--{
--      img = document.images["thumb" + frame];
--      url = "/thumb_" + job + "_" + frame + ".jpg"
--
--      if (img.style.display == "block") {
--              img.style.display = "none";
--              img.src = "";
--              lastFrame = -1;
--      } else {
--              img.src = url;
--              img.style.display = "block";
--              lastFrame = frame;
--      }
--}
--
--function returnObjById( id )
--{
--    if (document.getElementById)
--        var returnVar = document.getElementById(id);
--    else if (document.all)
--        var returnVar = document.all[id];
--    else if (document.layers)
--        var returnVar = document.layers[id];
--    return returnVar;
--}
--
--function toggleDisplay( className, value1, value2 )
--{
--      style = getStyle(className)
--      
--      if (style.style["display"] == value1) {
--              style.style["display"] = value2;
--      } else {
--              style.style["display"] = value1;
--      }
--}
--
--function getStyle(className) {
--    var classes = document.styleSheets[0].rules || document.styleSheets[0].cssRules
--    for(var x=0;x<classes.length;x++) {
--        if(classes[x].selectorText==className) {
--              return classes[x]; 
--        }
--    }
--}
diff --cc release/scripts/io/netrender/operators.py
index f2c2fda7bdee4a6a9e66d475fa308b07f1e102c5,f2c2fda7bdee4a6a9e66d475fa308b07f1e102c5..0000000000000000000000000000000000000000
deleted file mode 100644,100644
+++ /dev/null
@@@ -1,564 -1,564 +1,0 @@@
--# ##### BEGIN GPL LICENSE BLOCK #####
--#
--#  This program is free software; you can redistribute it and/or
--#  modify it under the terms of the GNU General Public License
--#  as published by the Free Software Foundation; either version 2
--#  of the License, or (at your option) any later version.
--#
--#  This program is distributed in the hope that it will be useful,
--#  but WITHOUT ANY WARRANTY; without even the implied warranty of
--#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--#  GNU General Public License for more details.
--#
--#  You should have received a copy of the GNU General Public License
--#  along with this program; if not, write to the Free Software Foundation,
--#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
--#
--# ##### END GPL LICENSE BLOCK #####
--
--import bpy
--import sys, os
--import http, http.client, http.server, urllib, socket
--import webbrowser
--import json
--
--import netrender
--from netrender.utils import *
--import netrender.client as client
--import netrender.model
--import netrender.versioning as versioning
--
--class RENDER_OT_netslave_bake(bpy.types.Operator):
--    '''NEED DESCRIPTION'''
--    bl_idname = "render.netslavebake"
--    bl_label = "Bake all in file"
--
--    @classmethod
--    def poll(cls, context):
--        return True
--
--    def execute(self, context):
--        scene = context.scene
--        netsettings = scene.network_render
--
--        filename = bpy.data.filepath
--        path, name = os.path.split(filename)
--        root, ext = os.path.splitext(name)
--        default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
--        relative_path = os.sep + os.sep + "blendcache_" + root + os.sep
--
--        # Force all point cache next to the blend file
--        for object in bpy.data.objects:
--            for modifier in object.modifiers:
--                if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
--                    modifier.settings.path = relative_path
--                    bpy.ops.fluid.bake({"active_object": object, "scene": scene})
--                elif modifier.type == "CLOTH":
--                    modifier.point_cache.frame_step = 1
--                    modifier.point_cache.use_disk_cache = True
--                    modifier.point_cache.use_external = False
--                elif modifier.type == "SOFT_BODY":
--                    modifier.point_cache.frame_step = 1
--                    modifier.point_cache.use_disk_cache = True
--                    modifier.point_cache.use_external = False
--                elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
--                    modifier.domain_settings.point_cache.use_step = 1
--                    modifier.domain_settings.point_cache.use_disk_cache = True
--                    modifier.domain_settings.point_cache.use_external = False
--
--            # particles modifier are stupid and don't contain data
--            # we have to go through the object property
--            for psys in object.particle_systems:
--                psys.point_cache.use_step = 1
--                psys.point_cache.use_disk_cache = True
--                psys.point_cache.use_external = False
--                psys.point_cache.filepath = relative_path
--
--        bpy.ops.ptcache.bake_all()
--
--        #bpy.ops.wm.save_mainfile(filepath = path + os.sep + root + "_baked.blend")
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--class RENDER_OT_netclientanim(bpy.types.Operator):
--    '''Start rendering an animation on network'''
--    bl_idname = "render.netclientanim"
--    bl_label = "Animation on network"
--
--    @classmethod
--    def poll(cls, context):
--        return True
--
--    def execute(self, context):
--        scene = context.scene
--        netsettings = scene.network_render
--
--        conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
--
--        if conn:
--            # Sending file
--            scene.network_render.job_id = client.clientSendJob(conn, scene, True)
--            conn.close()
--
--        bpy.ops.render.render('INVOKE_AREA', animation=True)
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--class RENDER_OT_netclientrun(bpy.types.Operator):
--    '''Start network rendering service'''
--    bl_idname = "render.netclientstart"
--    bl_label = "Start Service"
--
--    @classmethod
--    def poll(cls, context):
--        return True
--
--    def execute(self, context):
--        bpy.ops.render.render('INVOKE_AREA', animation=True)
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--class RENDER_OT_netclientsend(bpy.types.Operator):
--    '''Send Render Job to the Network'''
--    bl_idname = "render.netclientsend"
--    bl_label = "Send job"
--
--    @classmethod
--    def poll(cls, context):
--        return True
--
--    def execute(self, context):
--        scene = context.scene
--        netsettings = scene.network_render
--
--        try:
--            conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
--
--            if conn:
--                # Sending file
--                scene.network_render.job_id = client.clientSendJob(conn, scene, True)
--                conn.close()
--                self.report('INFO', "Job sent to master")
--        except Exception as err:
--            self.report('ERROR', str(err))
--
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--class RENDER_OT_netclientsendframe(bpy.types.Operator):
--    '''Send Render Job with current frame to the Network'''
--    bl_idname = "render.netclientsendframe"
--    bl_label = "Send current frame job"
--
--    @classmethod
--    def poll(cls, context):
--        return True
--
--    def execute(self, context):
--        scene = context.scene
--        netsettings = scene.network_render
--
--        try:
--            conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
--
--            if conn:
--                # Sending file
--                scene.network_render.job_id = client.clientSendJob(conn, scene, False)
--                conn.close()
--                self.report('INFO', "Job sent to master")
--        except Exception as err:
--            self.report('ERROR', str(err))
--
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--class RENDER_OT_netclientstatus(bpy.types.Operator):
--    '''Refresh the status of the current jobs'''
--    bl_idname = "render.netclientstatus"
--    bl_label = "Client Status"
--
--    @classmethod
--    def poll(cls, context):
--        return True
--
--    def execute(self, context):
--        netsettings = context.scene.network_render
--        conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
--
--        if conn:
--            conn.request("GET", "/status")
--
--            response = conn.getresponse()
--            content = response.read()
--            print( response.status, response.reason )
--
--            jobs = (netrender.model.RenderJob.materialize(j) for j in json.loads(str(content, encoding='utf8')))
--
--            while(len(netsettings.jobs) > 0):
--                netsettings.jobs.remove(0)
--
--            netrender.jobs = []
--
--            for j in jobs:
--                netrender.jobs.append(j)
--                netsettings.jobs.add()
--                job = netsettings.jobs[-1]
--
--                j.results = j.framesStatus() # cache frame status
--
--                job.name = j.name
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--class RENDER_OT_netclientblacklistslave(bpy.types.Operator):
--    '''Operator documentation text, will be used for the operator tooltip and python docs.'''
--    bl_idname = "render.netclientblacklistslave"
--    bl_label = "Client Blacklist Slave"
--
--    @classmethod
--    def poll(cls, context):
--        return True
--
--    def execute(self, context):
--        netsettings = context.scene.network_render
--
--        if netsettings.active_slave_index >= 0:
--
--            # deal with data
--            slave = netrender.slaves.pop(netsettings.active_slave_index)
--            netrender.blacklist.append(slave)
--
--            # deal with rna
--            netsettings.slaves_blacklist.add()
--            netsettings.slaves_blacklist[-1].name = slave.name
--
--            netsettings.slaves.remove(netsettings.active_slave_index)
--            netsettings.active_slave_index = -1
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--class RENDER_OT_netclientwhitelistslave(bpy.types.Operator):
--    '''Operator documentation text, will be used for the operator tooltip and python docs.'''
--    bl_idname = "render.netclientwhitelistslave"
--    bl_label = "Client Whitelist Slave"
--
--    @classmethod
--    def poll(cls, context):
--        return True
--
--    def execute(self, context):
--        netsettings = context.scene.network_render
--
--        if netsettings.active_blacklisted_slave_index >= 0:
--
--            # deal with data
--            slave = netrender.blacklist.pop(netsettings.active_blacklisted_slave_index)
--            netrender.slaves.append(slave)
--
--            # deal with rna
--            netsettings.slaves.add()
--            netsettings.slaves[-1].name = slave.name
--
--            netsettings.slaves_blacklist.remove(netsettings.active_blacklisted_slave_index)
--            netsettings.active_blacklisted_slave_index = -1
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--
--class RENDER_OT_netclientslaves(bpy.types.Operator):
--    '''Refresh status about available Render slaves'''
--    bl_idname = "render.netclientslaves"
--    bl_label = "Client Slaves"
--
--    @classmethod
--    def poll(cls, context):
--        return True
--
--    def execute(self, context):
--        netsettings = context.scene.network_render
--        conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
--
--        if conn:
--            conn.request("GET", "/slaves")
--
--            response = conn.getresponse()
--            content = response.read()
--            print( response.status, response.reason )
--
--            slaves = (netrender.model.RenderSlave.materialize(s) for s in json.loads(str(content, encoding='utf8')))
--
--            while(len(netsettings.slaves) > 0):
--                netsettings.slaves.remove(0)
--
--            netrender.slaves = []
--
--            for s in slaves:
--                for i in range(len(netrender.blacklist)):
--                    slave = netrender.blacklist[i]
--                    if slave.id == s.id:
--                        netrender.blacklist[i] = s
--                        netsettings.slaves_blacklist[i].name = s.name
--                        break
--                else:
--                    netrender.slaves.append(s)
--
--                    netsettings.slaves.add()
--                    slave = netsettings.slaves[-1]
--                    slave.name = s.name
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--class RENDER_OT_netclientcancel(bpy.types.Operator):
--    '''Cancel the selected network rendering job.'''
--    bl_idname = "render.netclientcancel"
--    bl_label = "Client Cancel"
--
--    @classmethod
--    def poll(cls, context):
--        netsettings = context.scene.network_render
--        return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
--
--    def execute(self, context):
--        netsettings = context.scene.network_render
--        conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
--
--        if conn:
--            job = netrender.jobs[netsettings.active_job_index]
--
--            conn.request("POST", cancelURL(job.id), json.dumps({'clear':False}))
--
--            response = conn.getresponse()
--            response.read()
--            print( response.status, response.reason )
--
--            netsettings.jobs.remove(netsettings.active_job_index)
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--class RENDER_OT_netclientcancelall(bpy.types.Operator):
--    '''Cancel all running network rendering jobs.'''
--    bl_idname = "render.netclientcancelall"
--    bl_label = "Client Cancel All"
--
--    @classmethod
--    def poll(cls, context):
--        return True
--
--    def execute(self, context):
--        netsettings = context.scene.network_render
--        conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
--
--        if conn:
--            conn.request("POST", "/clear", json.dumps({'clear':False}))
--
--            response = conn.getresponse()
--            response.read()
--            print( response.status, response.reason )
--
--            while(len(netsettings.jobs) > 0):
--                netsettings.jobs.remove(0)
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--class netclientdownload(bpy.types.Operator):
--    '''Download render results from the network'''
--    bl_idname = "render.netclientdownload"
--    bl_label = "Client Download"
--
--    @classmethod
--    def poll(cls, context):
--        netsettings = context.scene.network_render
--        return netsettings.active_job_index >= 0 and len(netsettings.jobs) > netsettings.active_job_index
--
--    def execute(self, context):
--        netsettings = context.scene.network_render
--        rd = context.scene.render
--
--        conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
--
--        if conn:
--            job_id = netrender.jobs[netsettings.active_job_index].id
--    
--            conn.request("GET", "/status", headers={"job-id":job_id})
--    
--            response = conn.getresponse()
--            
--            if response.status != http.client.OK:
--                self.report('ERROR', "Job ID %i not defined on master" % job_id)
--                return {'ERROR'}
--            
--            content = response.read()
--    
--            job = netrender.model.RenderJob.materialize(json.loads(str(content, encoding='utf8')))
--            
--            conn.close()  
--    
--            finished_frames = []
--            
--            nb_error = 0
--            nb_missing = 0
--                
--            for frame in job.frames:
--                if frame.status == DONE:
--                    finished_frames.append(frame.number)
--                elif frame.status == ERROR:
--                    nb_error += 1
--                else:
--                    nb_missing += 1
--            
--            if not finished_frames:
--                return
--            
--            frame_ranges = []
--    
--            first = None
--            last = None
--            
--            for i in range(len(finished_frames)):
--                current = finished_frames[i]
--                
--                if not first:
--                    first = current
--                    last = current
--                elif last + 1 == current:
--                    last = current
--                
--                if last + 1 < current or i + 1 == len(finished_frames):
--                    if first < last:
--                        frame_ranges.append((first, last))
--                    else:
--                        frame_ranges.append((first,))
--                    
--                    first = current
--                    last = current
--            
--            getResults(netsettings.server_address, netsettings.server_port, job_id, job.resolution[0], job.resolution[1], job.resolution[2], frame_ranges)
--            
--            if nb_error and nb_missing:
--                self.report('ERROR', "Results downloaded but skipped %i frames with errors and %i unfinished frames" % (nb_error, nb_missing))
--            elif nb_error:
--                self.report('ERROR', "Results downloaded but skipped %i frames with errors" % nb_error)
--            elif nb_missing:
--                self.report('WARNING', "Results downloaded but skipped %i unfinished frames" % nb_missing)
--            else:
--                self.report('INFO', "All results downloaded")
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--class netclientscan(bpy.types.Operator):
--    '''Listen on network for master server broadcasting its address and port.'''
--    bl_idname = "render.netclientscan"
--    bl_label = "Client Scan"
--
--    @classmethod
--    def poll(cls, context):
--        return True
--
--    def execute(self, context):
--        address, port = clientScan(self.report)
--
--        if address:
--            scene = context.scene
--            netsettings = scene.network_render
--            netsettings.server_address = address
--            netsettings.server_port = port
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--class netclientvcsguess(bpy.types.Operator):
--    '''Guess VCS setting for the current file'''
--    bl_idname = "render.netclientvcsguess"
--    bl_label = "VCS Guess"
--
--    @classmethod
--    def poll(cls, context):
--        return True
--
--    def execute(self, context):
--        netsettings = context.scene.network_render
--        
--        system = versioning.SYSTEMS.get(netsettings.vcs_system, None)
--        
--        if system:
--            wpath, name = os.path.split(os.path.abspath(bpy.data.filepath))
--            
--            rpath = system.path(wpath)
--            revision = system.revision(wpath)
--            
--            netsettings.vcs_wpath = wpath
--            netsettings.vcs_rpath = rpath
--            netsettings.vcs_revision = revision
--            
--        
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
--
--
--class netclientweb(bpy.types.Operator):
--    '''Open new window with information about running rendering jobs'''
--    bl_idname = "render.netclientweb"
--    bl_label = "Open Master Monitor"
--
--    @classmethod
--    def poll(cls, context):
--        netsettings = context.scene.network_render
--        return netsettings.server_address != "[default]"
--
--    def execute(self, context):
--        netsettings = context.scene.network_render
--
--
--        # open connection to make sure server exists
--        conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
--
--        if conn:
--            conn.close()
--
--            webbrowser.open("http://%s:%i" % (netsettings.server_address, netsettings.server_port))
--
--        return {'FINISHED'}
--
--    def invoke(self, context, event):
--        return self.execute(context)
diff --cc release/scripts/io/netrender/repath.py
index 3ac9636b6281153c68d87e8883ad5d0bea9d2062,3ac9636b6281153c68d87e8883ad5d0bea9d2062..0000000000000000000000000000000000000000
deleted file mode 100644,100644
+++ /dev/null
@@@ -1,150 -1,150 +1,0 @@@
--# ##### BEGIN GPL LICENSE BLOCK #####
--#
--#  This program is free software; you can redistribute it and/or
--#  modify it under the terms of the GNU General Public License
--#  as published by the Free Software Foundation; either version 2
--#  of the License, or (at your option) any later version.
--#
--#  This program is distributed in the hope that it will be useful,
--#  but WITHOUT ANY WARRANTY; without even the implied warranty of
--#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--#  GNU General Public License for more details.
--#
--#  You should have received a copy of the GNU General Public License
--#  along with this program; if not, write to the Free Software Foundation,
--#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
--#
--# ##### END GPL LICENSE BLOCK #####
--
--import sys, os
--import subprocess
--
--import bpy
--
--from netrender.utils import *
--import netrender.model
--
--BLENDER_PATH = sys.argv[0]
--
--def reset(job):
--    main_file = job.files[0]
--    
--    job_full_path = main_file.filepath
--
--    if os.path.exists(job_full_path + ".bak"):
--        os.remove(job_full_path) # repathed file
--        os.renames(job_full_path + ".bak", job_full_path)
--
--def update(job):
--    paths = []
--    
--    main_file = job.files[0]
--    
--    job_full_path = main_file.filepath
--
--        
--    path, ext = os.path.splitext(job_full_path)
--    
--    new_path = path + ".remap" + ext 
--    
--    # Disable for now. Partial repath should work anyway
--    #all = main_file.filepath != main_file.original_path
--    all = False 
--    
--    for rfile in job.files[1:]:
--        if all or rfile.original_path != rfile.filepath:
--            paths.append(rfile.original_path)
--            paths.append(rfile.filepath)
--    
--    # Only update if needed
--    if paths:        
--        process = subprocess.Popen([BLENDER_PATH, "-b", "-noaudio", job_full_path, "-P", __file__, "--", new_path] + paths, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
--        process.wait()
--        
--        os.renames(job_full_path, job_full_path + ".bak")
--        os.renames(new_path, job_full_path)
--
--def process(paths):
--    def processPointCache(point_cache):
--        point_cache.use_external = False
--
--    def processFluid(fluid):
--        new_path = path_map.get(fluid.filepath, None)
--        if new_path:
--            fluid.path = new_path
--    
--    path_map = {}
--    for i in range(0, len(paths), 2):
--        # special case for point cache
--        if paths[i].endswith(".bphys"):
--            pass # Don't need them in the map, they all use the default external path
--            # NOTE: This is probably not correct all the time, need to be fixed.
--        # special case for fluids
--        elif paths[i].endswith(".bobj.gz"):
--            path_map[os.path.split(paths[i])[0]] = os.path.split(paths[i+1])[0]
--        else:
--            path_map[os.path.split(paths[i])[1]] = paths[i+1]
--            
--    # TODO original paths aren't really the orignal path (they are the normalized path
--    # so we repath using the filenames only. 
--    
--    ###########################
--    # LIBRARIES
--    ###########################
--    for lib in bpy.data.libraries:
--        file_path = bpy.path.abspath(lib.filepath)
--        new_path = path_map.get(os.path.split(file_path)[1], None)
--        if new_path:
--            lib.filepath = new_path
--
--    ###########################
--    # IMAGES
--    ###########################
--    for image in bpy.data.images:
--        if image.source == "FILE" and not image.packed_file:
--            file_path = bpy.path.abspath(image.filepath)
--            new_path = path_map.get(os.path.split(file_path)[1], None)
--            if new_path:
--                image.filepath = new_path
--            
--
--    ###########################
--    # FLUID + POINT CACHE
--    ###########################
--    for object in bpy.data.objects:
--        for modifier in object.modifiers:
--            if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
--                processFluid(settings)
--            elif modifier.type == "CLOTH":
--                processPointCache(modifier.point_cache)
--            elif modifier.type == "SOFT_BODY":
--                processPointCache(modifier.point_cache)
--            elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
--                processPointCache(modifier.domain_settings.point_cache_low)
--                if modifier.domain_settings.use_high_resolution:
--                    processPointCache(modifier.domain_settings.point_cache_high)
--            elif modifier.type == "MULTIRES" and modifier.is_external:
--                file_path = bpy.path.abspath(modifier.filepath)
--                new_path = path_map.get(file_path, None)
--                if new_path:
--                    modifier.filepath = new_path
--
--        # particles modifier are stupid and don't contain data
--        # we have to go through the object property
--        for psys in object.particle_systems:
--            processPointCache(psys.point_cache)
--                
--
--if __name__ == "__main__":
--    try:
--        i = sys.argv.index("--")
--    except:
--        i = 0
--    
--    if i:
--        new_path = sys.argv[i+1]
--        args = sys.argv[i+2:]
--        
--        process(args)
--        
--        bpy.ops.wm.save_as_mainfile(filepath=new_path, check_existing=False)
diff --cc release/scripts/io/netrender/slave.py
index b05de0afeb96d75bf913f4be5b5bafb093e7f5ca,b05de0afeb96d75bf913f4be5b5bafb093e7f5ca..0000000000000000000000000000000000000000
deleted file mode 100644,100644
+++ /dev/null
@@@ -1,349 -1,349 +1,0 @@@
--# ##### BEGIN GPL LICENSE BLOCK #####
--#
--#  This program is free software; you can redistribute it and/or
--#  modify it under the terms of the GNU General Public License
--#  as published by the Free Software Foundation; either version 2
--#  of the License, or (at your option) any later version.
--#
--#  This program is distributed in the hope that it will be useful,
--#  but WITHOUT ANY WARRANTY; without even the implied warranty of
--#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--#  GNU General Public License for more details.
--#
--#  You should have received a copy of the GNU General Public License
--#  along with this program; if not, write to the Free Software Foundation,
--#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
--#
--# ##### END GPL LICENSE BLOCK #####
--
--import sys, os, platform, shutil
--import http, http.client, http.server, urllib
--import subprocess, time
--import json
--
--import bpy
--
--from netrender.utils import *
--import netrender.model
--import netrender.repath
--import netrender.thumbnail as thumbnail
--
--BLENDER_PATH = sys.argv[0]
--
--CANCEL_POLL_SPEED = 2
--MAX_TIMEOUT = 10
--INCREMENT_TIMEOUT = 1
--MAX_CONNECT_TRY = 10
--try:
--    system = platform.system()
--except UnicodeDecodeError:
--    import sys
--    system = sys.platform
--
--if system in ('Windows', 'win32') and platform.version() >= '5': # Error mode is only available on Win2k or higher, that's version 5
--    import ctypes
--    def SetErrorMode():
--        val = ctypes.windll.kernel32.SetErrorMode(0x0002)
--        ctypes.windll.kernel32.SetErrorMode(val | 0x0002)
--        return val
--
--    def RestoreErrorMode(val):
--        ctypes.windll.kernel32.SetErrorMode(val)
--else:
--    def SetErrorMode():
--        return 0
--
--    def RestoreErrorMode(val):
--        pass
--
--def clearSlave(path):
--    shutil.rmtree(path)
--
--def slave_Info():
--    sysname, nodename, release, version, machine, processor = platform.uname()
--    slave = netrender.model.RenderSlave()
--    slave.name = nodename
--    slave.stats = sysname + " " + release + " " + machine + " " + processor
--    return slave
--
--def testCancel(conn, job_id, frame_number):
--        conn.request("HEAD", "/status", headers={"job-id":job_id, "job-frame": str(frame_number)})
--
--        # canceled if job isn't found anymore
--        if responseStatus(conn) == http.client.NO_CONTENT:
--            return True
--        else:
--            return False
--
--def testFile(conn, job_id, slave_id, rfile, JOB_PREFIX, main_path = None):
--    job_full_path = prefixPath(JOB_PREFIX, rfile.filepath, main_path)
--    
--    found = os.path.exists(job_full_path)
--    
--    if found and rfile.signature != None:
--        found_signature = hashFile(job_full_path)
--        found = found_signature == rfile.signature
--        
--        if not found:
--            print("Found file %s at %s but signature mismatch!" % (rfile.filepath, job_full_path))
--            job_full_path = prefixPath(JOB_PREFIX, rfile.filepath, main_path, force = True)
--
--    if not found:
--        # Force prefix path if not found
--        job_full_path = prefixPath(JOB_PREFIX, rfile.filepath, main_path, force = True)
--        temp_path = os.path.join(JOB_PREFIX, "slave.temp")
--        conn.request("GET", fileURL(job_id, rfile.index), headers={"slave-id":slave_id})
--        response = conn.getresponse()
--
--        if response.status != http.client.OK:
--            return None # file for job not returned by server, need to return an error code to server
--
--        f = open(temp_path, "wb")
--        buf = response.read(1024)
--
--        while buf:
--            f.write(buf)
--            buf = response.read(1024)
--
--        f.close()
--
--        os.renames(temp_path, job_full_path)
--        
--    rfile.filepath = job_full_path
--
--    return job_full_path
--
--def breakable_timeout(timeout):
--    for i in range(timeout):
--        time.sleep(1)
--        if engine.test_break():
--            break
--
--def render_slave(engine, netsettings, threads):
--    timeout = 1
--    
--    bisleep = BreakableIncrementedSleep(INCREMENT_TIMEOUT, 1, MAX_TIMEOUT, engine.test_break)
--
--    engine.update_stats("", "Network render node initiation")
--
--    conn = clientConnection(netsettings.server_address, netsettings.server_port)
--    
--    if not conn:
--        timeout = 1
--        print("Connection failed, will try connecting again at most %i times" % MAX_CONNECT_TRY)
--        bisleep.reset()
--        
--        for i in range(MAX_CONNECT_TRY):
--            bisleep.sleep()
--            
--            conn = clientConnection(netsettings.server_address, netsettings.server_port)
--            
--            if conn or engine.test_break():
--                break
--            
--            print("Retry %i failed, waiting %is before retrying" % (i + 1, bisleep.current))
--    
--    if conn:
--        conn.request("POST", "/slave", json.dumps(slave_Info().serialize()))
--        response = conn.getresponse()
--        response.read()
--
--        slave_id = response.getheader("slave-id")
--
--        NODE_PREFIX = os.path.join(bpy.path.abspath(netsettings.path), "slave_" + slave_id)
--        if not os.path.exists(NODE_PREFIX):
--            os.mkdir(NODE_PREFIX)
--
--        engine.update_stats("", "Network render connected to master, waiting for jobs")
--
--        while not engine.test_break():
--            conn.request("GET", "/job", headers={"slave-id":slave_id})
--            response = conn.getresponse()
--
--            if response.status == http.client.OK:
--                bisleep.reset()
--
--                job = netrender.model.RenderJob.materialize(json.loads(str(response.read(), encoding='utf8')))
--                engine.update_stats("", "Network render processing job from master")
--
--                JOB_PREFIX = os.path.join(NODE_PREFIX, "job_" + job.id)
--                if not os.path.exists(JOB_PREFIX):
--                    os.mkdir(JOB_PREFIX)
--
--                # set tempdir for fsaa temp files
--                # have to set environ var because render is done in a subprocess and that's the easiest way to propagate the setting
--                os.environ["TMP"] = JOB_PREFIX
--
--
--                if job.type == netrender.model.JOB_BLENDER:
--                    job_path = job.files[0].filepath # path of main file
--                    main_path, main_file = os.path.split(job_path)
--
--                    job_full_path = testFile(conn, job.id, slave_id, job.files[0], JOB_PREFIX)
--                    print("Fullpath", job_full_path)
--                    print("File:", main_file, "and %i other files" % (len(job.files) - 1,))
--
--                    for rfile in job.files[1:]:
--                        testFile(conn, job.id, slave_id, rfile, JOB_PREFIX, main_path)
--                        print("\t", rfile.filepath)
--                        
--                    netrender.repath.update(job)
--
--                    engine.update_stats("", "Render File "+ main_file+ " for job "+ job.id)
--                elif job.type == netrender.model.JOB_VCS:
--                    if not job.version_info:
--                        # Need to return an error to server, incorrect job type
--                        pass
--                        
--                    job_path = job.files[0].filepath # path of main file
--                    main_path, main_file = os.path.split(job_path)
--                    
--                    job.version_info.update()
--                    
--                    # For VCS jobs, file path is relative to the working copy path
--                    job_full_path = os.path.join(job.version_info.wpath, job_path)
--                    
--                    engine.update_stats("", "Render File "+ main_file+ " for job "+ job.id)
--
--                # announce log to master
--                logfile = netrender.model.LogFile(job.id, slave_id, [frame.number for frame in job.frames])
--                conn.request("POST", "/log", bytes(json.dumps(logfile.serialize()), encoding='utf8'))
--                response = conn.getresponse()
--                response.read()
--
--
--                first_frame = job.frames[0].number
--
--                # start render
--                start_t = time.time()
--
--                if job.rendersWithBlender():
--                    frame_args = []
--
--                    for frame in job.frames:
--                        print("frame", frame.number)
--                        frame_args += ["-f", str(frame.number)]
--
--                    val = SetErrorMode()
--                    process = subprocess.Popen([BLENDER_PATH, "-b", "-noaudio", job_full_path, "-t", str(threads), "-o", os.path.join(JOB_PREFIX, "######"), "-E", "BLENDER_RENDER", "-F", "MULTILAYER"] + frame_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
--                    RestoreErrorMode(val)
--                elif job.type == netrender.model.JOB_PROCESS:
--                    command = job.frames[0].command
--                    val = SetErrorMode()
--                    process = subprocess.Popen(command.split(" "), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
--                    RestoreErrorMode(val)
--
--                headers = {"slave-id":slave_id}
--
--                cancelled = False
--                stdout = bytes()
--                run_t = time.time()
--                while not cancelled and process.poll() is None:
--                    stdout += process.stdout.read(1024)
--                    current_t = time.time()
--                    cancelled = engine.test_break()
--                    if current_t - run_t > CANCEL_POLL_SPEED:
--
--                        # update logs if needed
--                        if stdout:
--                            # (only need to update on one frame, they are linked
--                            conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
--                            response = conn.getresponse()
--                            response.read()
--                            
--                            # Also output on console
--                            if netsettings.use_slave_output_log:
--                                print(str(stdout, encoding='utf8'), end="")
--
--                            stdout = bytes()
--
--                        run_t = current_t
--                        if testCancel(conn, job.id, first_frame):
--                            cancelled = True
--
--                if job.type == netrender.model.JOB_BLENDER:
--                    netrender.repath.reset(job)
--
--                # read leftovers if needed
--                stdout += process.stdout.read()
--
--                if cancelled:
--                    # kill process if needed
--                    if process.poll() is None:
--                        try:
--                            process.terminate()
--                        except OSError:
--                            pass
--                    continue # to next frame
--
--                # flush the rest of the logs
--                if stdout:
--                    # Also output on console
--                    if netsettings.use_slave_thumb:
--                        print(str(stdout, encoding='utf8'), end="")
--                    
--                    # (only need to update on one frame, they are linked
--                    conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
--                    if responseStatus(conn) == http.client.NO_CONTENT:
--                        continue
--
--                total_t = time.time() - start_t
--
--                avg_t = total_t / len(job.frames)
--
--                status = process.returncode
--
--                print("status", status)
--
--                headers = {"job-id":job.id, "slave-id":slave_id, "job-time":str(avg_t)}
--
--
--                if status == 0: # non zero status is error
--                    headers["job-result"] = str(DONE)
--                    for frame in job.frames:
--                        headers["job-frame"] = str(frame.number)
--                        if job.hasRenderResult():
--                            # send image back to server
--
--                            filename = os.path.join(JOB_PREFIX, "%06d.exr" % frame.number)
--
--                            # thumbnail first
--                            if netsettings.use_slave_thumb:
--                                thumbname = thumbnail.generate(filename)
--                                
--                                if thumbname:
--                                    f = open(thumbname, 'rb')
--                                    conn.request("PUT", "/thumb", f, headers=headers)
--                                    f.close()
--                                    responseStatus(conn)
--
--                            f = open(filename, 'rb')
--                            conn.request("PUT", "/render", f, headers=headers)
--                            f.close()
--                            if responseStatus(conn) == http.client.NO_CONTENT:
--                                continue
--
--                        elif job.type == netrender.model.JOB_PROCESS:
--                            conn.request("PUT", "/render", headers=headers)
--                            if responseStatus(conn) == http.client.NO_CONTENT:
--                                continue
--                else:
--                    headers["job-result"] = str(ERROR)
--                    for frame in job.frames:
--                        headers["job-frame"] = str(frame.number)
--                        # send error result back to server
--                        conn.request("PUT", "/render", headers=headers)
--                        if responseStatus(conn) == http.client.NO_CONTENT:
--                            continue
--
--                engine.update_stats("", "Network render connected to master, waiting for jobs")
--            else:
--                bisleep.sleep()
--
--        conn.close()
--
--        if netsettings.use_slave_clear: