to load a script file called `stress` and start executing it.
-Adding a new language
+## Adding a new language
- Edit src/wx/config_dialog.cc to add the language to languages.
- Add to platform/windows/wscript, platform/osx/make_dmg.sh, cscript.
# -*- mode: python -*-
#
-# Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
+# Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
#
# This file is part of DCP-o-matic.
#
'libx264-155',
'libcurl4'])
+def can_build_disk(target):
+ # We can build dcpomatic2_disk on platforms that have Boost process and can build the lwext4
+ # library. For now, just whitelist good ones here.
+ #
+ # - Lots of Linux distros don't have a new enough boost (1.64 or above)
+ # - On Centos 6 we can't build lwext4 because it needs a new CMake which Centos 6's g++ is not new enough to build.
+ # - On Centos 7 there is a build error in lwext4 related to __unused
+ return target.platform == 'windows' or target.platform == 'osx' or (target.platform == 'linux' and target.distro == 'ubuntu' and target.version in ['18.04', '18.10', '19.04'])
+
def packages(name, packages, f):
s = '%s: ' % name
for p in packages:
def make_spec(filename, version, target, options, requires=None):
"""Make a .spec file for a RPM build"""
+ tools = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(filename))), "src/tools")
f = open(filename, 'w')
print('Summary:A program that generates Digital Cinema Packages (DCPs) from video and audio files', file=f)
print('Name:dcpomatic2', file=f)
if options['variant'] == 'swaroop-studio':
print('%{_bindir}/dcpomatic2_ecinema', file=f)
print('%{_bindir}/dcpomatic2_uuid', file=f)
+ if os.path.exists(os.path.join(tools, "dcpomatic2_disk")):
+ print('%{_bindir}/dcpomatic2_disk', file=f)
+ print('%{_bindir}/dcpomatic2_disk_writer', file=f)
print('%{_datadir}/applications/dcpomatic2.desktop', file=f)
print('%{_datadir}/applications/dcpomatic2_batch.desktop', file=f)
print('%{_datadir}/applications/dcpomatic2_server.desktop', file=f)
print('%posttrans', file=f)
print('/usr/bin/gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :', file=f)
-def dependencies(target):
+def dependencies(target, options):
if target.platform == 'linux':
ffmpeg_options = { 'shared': False }
# Use distro-provided FFmpeg on Arch
deps = []
- deps.append(('libdcp', None))
- deps.append(('libsub', None))
+ # Let's use C++11 mode if we can
+ cpp_lib_options = {'force-cpp11': True} if target.platform == 'osx' and target.bits == 64 else {}
+
+ deps.append(('libdcp', None, cpp_lib_options))
+ deps.append(('libsub', None, cpp_lib_options))
deps.append(('rtaudio', 'carl'))
# We get our OpenSSL libraries from the environment, but we
# also need a patched openssl binary to make certificates.
# This dependency is to get that binary, which is added into
# the appropriate place later
deps.append(('openssl', 'carl'))
+ if can_build_disk(target):
+ deps.append(('lwext4', 'carl2'))
return deps
-def option_defaults():
- return { "gui": True, "variant": None }
+option_defaults = { "gui": True, "variant": None, "disk": False }
def configure_options(target, options):
opt = ''
# I worry that this will cause ABI problems but I don't have
# a better solution.
opt += ' --force-cpp11'
+ elif target.platform == 'osx' and target.bits == 64:
+ opt += ' --force-cpp11'
if not options['gui']:
opt += ' --disable-gui'
if target.debug and target.platform == 'windows':
opt += ' --static-dcpomatic'
+ if can_build_disk(target) and options['disk']:
+ opt += ' --enable-disk'
+
return opt
def build(target, options):
override_dh_fixperms:
dh_fixperms --exclude usr/bin/dcpomatic2_uuid
+ dh_fixperms --exclude usr/bin/dcpomatic2_disk_writer
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1066.6666"
+ height="1066.6666"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="dcpomatic2_disk.svg"
+ viewBox="0 0 1000 1000">
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.061741495"
+ inkscape:cx="-2231.4864"
+ inkscape:cy="438.2963"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1680"
+ inkscape:window-height="995"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:document-rotation="0"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true" />
+ <defs
+ id="defs4">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient979">
+ <stop
+ style="stop-color:#255a54;stop-opacity:1;"
+ offset="0"
+ id="stop975" />
+ <stop
+ style="stop-color:#133430;stop-opacity:1"
+ offset="1"
+ id="stop977" />
+ </linearGradient>
+ <linearGradient
+ x1="403.63"
+ y1="448.35001"
+ gradientTransform="matrix(0.70711,-0.70711,0.70711,0.70711,-203.97741,756.21351)"
+ x2="382.89999"
+ gradientUnits="userSpaceOnUse"
+ y2="448.35001"
+ id="linearGradient855">
+ <stop
+ offset="0"
+ stop-opacity=".39216"
+ stop-color="#7f7f7f"
+ id="stop893"
+ style="stop-color:#7f7f7f;stop-opacity:1" />
+ <stop
+ offset="1"
+ stop-opacity=".39216"
+ stop-color="#6f6f6f"
+ id="stop895"
+ style="stop-color:#6f6f6f;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient855-3"
+ y2="448.35001"
+ gradientUnits="userSpaceOnUse"
+ x2="382.89999"
+ gradientTransform="matrix(0.70711,-0.70711,0.70711,0.70711,-203.97741,756.21351)"
+ y1="448.35001"
+ x1="403.63">
+ <stop
+ style="stop-color:#7f7f7f;stop-opacity:1"
+ id="stop851"
+ stop-color="#7f7f7f"
+ stop-opacity=".39216"
+ offset="0" />
+ <stop
+ style="stop-color:#6f6f6f;stop-opacity:0"
+ id="stop853"
+ stop-color="#6f6f6f"
+ stop-opacity=".39216"
+ offset="1" />
+ </linearGradient>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="573.17725 : 70.615423 : 0"
+ inkscape:vp_y="0 : 1048.4799 : 0"
+ inkscape:vp_z="1196.4109 : -308.5085 : 0"
+ inkscape:persp3d-origin="531.47195 : 173.77218 : 1"
+ id="perspective4530" />
+ <linearGradient
+ x1="-772.01001"
+ y1="742.5"
+ gradientTransform="matrix(-0.84033,-0.84033,-0.84033,0.84033,136.32259,-691.39649)"
+ x2="-886.76001"
+ gradientUnits="userSpaceOnUse"
+ y2="742.5"
+ id="linearGradient3594">
+ <stop
+ offset="0"
+ stop-color="#ffffff"
+ id="stop4687" />
+ <stop
+ offset="1"
+ stop-opacity="0"
+ stop-color="#ffffff"
+ id="stop4689" />
+ </linearGradient>
+ <linearGradient
+ x1="386.39001"
+ y1="63.870998"
+ gradientTransform="matrix(0.70711,-0.70711,0.70711,0.70711,-203.97741,756.21351)"
+ x2="385.04001"
+ gradientUnits="userSpaceOnUse"
+ y2="613.94"
+ id="linearGradient3601">
+ <stop
+ offset="0"
+ stop-color="#7f7f7f"
+ id="stop3797" />
+ <stop
+ offset="1"
+ stop-color="#6f6f6f"
+ id="stop3799" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="translate(-77.797413,384.00351)"
+ x1="409.38"
+ y1="358.29999"
+ x2="212.92999"
+ gradientUnits="userSpaceOnUse"
+ y2="161.84"
+ id="linearGradient3609">
+ <stop
+ offset="0"
+ stop-color="#6f6f6f"
+ id="stop4034" />
+ <stop
+ offset=".5"
+ stop-color="#6f6f6f"
+ id="stop3374" />
+ <stop
+ offset="1"
+ stop-color="#6f6f6f"
+ id="stop3376" />
+ </linearGradient>
+ <linearGradient
+ x1="403.63"
+ y1="448.35001"
+ gradientTransform="matrix(0.70711,-0.70711,0.70711,0.70711,-203.97741,756.21351)"
+ x2="382.89999"
+ gradientUnits="userSpaceOnUse"
+ y2="448.35001"
+ id="linearGradient3632">
+ <stop
+ offset="0"
+ stop-opacity=".39216"
+ stop-color="#7f7f7f"
+ id="stop3636"
+ style="stop-color:#7f7f7f;stop-opacity:1" />
+ <stop
+ offset="1"
+ stop-opacity=".39216"
+ stop-color="#6f6f6f"
+ id="stop3638"
+ style="stop-color:#6f6f6f;stop-opacity:0.01941748" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient979"
+ id="linearGradient981"
+ x1="258.13272"
+ y1="581.64954"
+ x2="280.90915"
+ y2="487.69684"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.2956992,0,0,1.1173853,-70.442976,-75.323286)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient979"
+ id="linearGradient981-5"
+ x1="258.13272"
+ y1="581.64954"
+ x2="280.90915"
+ y2="487.69684"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.2956992,0,0,1.1173853,346.73201,-93.640862)" />
+ </defs>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-52.362188)">
+ <rect
+ style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:#ececec;stroke-width:15.00000095;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-end:none;paint-order:stroke fill markers"
+ id="rect945"
+ width="1167.6385"
+ height="1151.5332"
+ x="1217.4148"
+ y="1347.8682"
+ rx="33.75"
+ ry="33.75" />
+ <image
+ sodipodi:absref="/home/carl/src/dcpomatic/graphics/src/dcpomatic.png"
+ xlink:href="dcpomatic.png"
+ style="stroke:#000000;stroke-opacity:1;stroke-width:2.5000001;stroke-miterlimit:4;stroke-dasharray:none;opacity:1"
+ id="image4358"
+ preserveAspectRatio="none"
+ height="960.00006"
+ width="960.00006"
+ x="10.670144"
+ y="80.386467" />
+ <rect
+ style="opacity:1;vector-effect:none;fill:url(#linearGradient981);fill-opacity:1;stroke:none;stroke-width:15.00000095;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-end:none;paint-order:stroke fill markers"
+ id="rect958"
+ width="95.643623"
+ height="189.7067"
+ x="238.2251"
+ y="451.96884"
+ rx="43.729847"
+ ry="36.004051" />
+ <rect
+ style="opacity:1;vector-effect:none;fill:url(#linearGradient981-5);fill-opacity:1;stroke:none;stroke-width:15.00000095;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-end:none;paint-order:stroke fill markers"
+ id="rect958-6"
+ width="95.643623"
+ height="189.70668"
+ x="655.40009"
+ y="433.65125"
+ rx="43.729847"
+ ry="36.004051" />
+ <path
+ style="fill:#808080;stroke:#ffffff;stroke-width:15.00000095;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 635.01872,349.90824 85.16099,283.02659 H 263.60389 l 87.18344,-281.69086 c 3.82184,-13.96906 23.00671,-28.47528 36.85535,-28.47528 l 213.6276,-0.35249 c 17.09674,0.72772 28.6937,11.26128 33.74844,27.49204 z"
+ id="path947"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccc" />
+ <rect
+ style="opacity:1;vector-effect:none;fill:#999999;fill-opacity:1;stroke:#ececec;stroke-width:30.00000191;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-end:none;paint-order:stroke fill markers"
+ id="rect943"
+ width="456.57584"
+ height="105.73843"
+ x="263.60385"
+ y="632.93481"
+ ry="17.726633"
+ rx="17.773174" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:22.50000191px;line-height:1.20000005;font-family:'Bitstream Vera Sans Mono';-inkscape-font-specification:'Bitstream Vera Sans Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.9375;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;"
+ x="-1064.7976"
+ y="826.49603"
+ id="text1027"><tspan
+ sodipodi:role="line"
+ id="tspan1025"
+ x="-1064.7976"
+ y="846.88104"
+ style="stroke-width:0.9375;-inkscape-font-specification:'Bitstream Vera Sans Mono';font-family:'Bitstream Vera Sans Mono';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;" /></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:128.48579407px;line-height:1.20000005;font-family:'Latin Modern Roman';-inkscape-font-specification:'Latin Modern Roman, Bold';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.93749988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ x="394.93756"
+ y="729.72614"
+ id="text1035"><tspan
+ sodipodi:role="line"
+ id="tspan1033"
+ x="394.93756"
+ y="729.72614"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Latin Modern Roman';-inkscape-font-specification:'Latin Modern Roman, Bold';stroke-width:0.93749988">DCP</tspan></text>
+ <path
+ style="opacity:1;vector-effect:none;fill:#999999;fill-opacity:1;stroke:#ececec;stroke-width:30.00000191;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-end:none;paint-order:stroke fill markers"
+ d="m 292.58069,687.62725 h 37.57918"
+ id="path1037"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
exit 1
fi
+svg_apps="dcpomatic2_kdm dcpomatic2_server dcpomatic2_batch dcpomatic2_player dcpomatic2_playlist dcpomatic2_disk"
+
if [ `uname -s` == "Darwin" ]; then
# Convert OS X icons using OS X-only iconutil
- for p in dcpomatic2 dcpomatic2_kdm dcpomatic2_server dcpomatic2_batch dcpomatic2_player dcpomatic2_playlist; do
+ for p in dcpomatic2 $svg_apps; do
iconutil --convert icns --output osx/$p.icns osx/$p.iconset
done
else
convert src/dcpomatic.png -resize $rx$r osx/$p.iconset/icon_${r}x${r}.png
convert src/dcpomatic.png -resize $rx$r osx/$p.iconset/icon_${r}x${r}@2x.png
done
- for p in dcpomatic2_kdm dcpomatic2_server dcpomatic2_batch dcpomatic2_player dcpomatic2_playlist; do
+ for p in $svg_apps; do
mkdir -p osx/$p.iconset
$INKSCAPE osx/$p.iconset/icon_${r}x${r}.png -w $r -h $r src/$p.svg
$INKSCAPE osx/$p.iconset/icon_${r}x${r}@2x.png -w $r -h $r src/$p.svg
for r in 16 22 32 48 64 128 256 512; do
mkdir -p linux/$r
convert src/dcpomatic.png -resize $rx$r linux/$r/dcpomatic2.png
- for p in dcpomatic2_kdm dcpomatic2_server dcpomatic2_batch dcpomatic2_player dcpomatic2_playlist; do
+ for p in $svg_apps; do
$INKSCAPE linux/$r/$p.png src/$p.svg -w $r -h $r
done
done
# Windows application icons
mkdir -p windows
- for p in dcpomatic2 dcpomatic2_kdm dcpomatic2_server dcpomatic2_batch dcpomatic2_player dcpomatic2_playlist; do
+ for p in dcpomatic2 $svg_apps; do
icotool -c -o windows/$p.ico linux/16/$p.png linux/32/$p.png linux/48/$p.png linux/64/$p.png linux/128/$p.png
done
convert src/dcpomatic.png -resize 400x400 windows/dcpomatic.bmp
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>dcpomatic2_disk</string>
+ <key>CFBundleGetInfoString</key>
+ <string>DCP-o-matic 2 Disk Writer</string>
+ <key>CFBundleIconFile</key>
+ <string>dcpomatic2_disk.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.dcpomatic.disk</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>DCP-o-matic 2 Disk Writer</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersions</key>
+ <string>@VERSION@</string>
+ <key>CFBundleSignature</key>
+ <string>DOMC</string>
+ <key>CFBundleVersion</key>
+ <string>@VERSION@</string>
+ <key>CFBundleAllowMixedLocalizations</key>
+ <true/>
+ <key>LSUIElement</key>
+ <string>0</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
copy_lib_root libswscale "$dest"
copy_lib_root libpostproc "$dest"
copy_lib_root libswresample "$dest"
+ copy_lib_root liblwext4 "$dest"
+ copy_lib_root libblockdev "$dest"
copy $ROOT src/dcpomatic/build/src/lib/libdcpomatic2.dylib "$dest"
copy $ROOT src/dcpomatic/build/src/wx/libdcpomatic2-wx.dylib "$dest"
copy_lib_env libboost_system "$dest"
cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_player.icns "$dest"
cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_batch.icns "$dest"
cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_playlist.icns "$dest"
+ cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_disk.icns "$dest"
cp $prefix/src/dcpomatic/graphics/osx/preferences/colour_conversions.png "$dest"
cp $prefix/src/dcpomatic/graphics/osx/preferences/defaults.png "$dest"
cp $prefix/src/dcpomatic/graphics/osx/preferences/kdm_email.png "$dest"
}
# param $1 list of things that link to other things
-function relink {
+function relink_relative {
to_relink=`echo $to_relink | sed -e "s/\+//g"`
local linkers=("$@")
done
}
+# param $1 directory things should be relinked into
+# $2 list of things that link to other things
+function relink_absolute {
+ to_relink=`echo $to_relink | sed -e "s/\+//g"`
+ target=$1
+ shift
+ local linkers=("$@")
+
+ for obj in "${linkers[@]}"; do
+ deps=`otool -L "$obj" | awk '{print $1}' | egrep "($to_relink)" | egrep "($ENV|$ROOT|boost|libicu)"`
+ for dep in $deps; do
+ base=`basename $dep`
+ install_name_tool -change "$dep" "$target"/$base -id `basename "$obj"` "$obj"
+ done
+ done
+}
+
+function sign {
+ codesign --deep --force --verify --verbose --options runtime --sign "Developer ID Application: Carl Hetherington (R82DXSR997)" "$1"
+ if [ "$?" != "0" ]; then
+ echo "Failed to sign $1"
+ exit 1
+ fi
+}
+
+
# @param #1 .app directory
-# @param #2 full name e.g. DCP-o-matic Batch Converter
-# @param #3 bundle id e.g. com.dcpomatic.batch
+# @param #2 .pkg or ""
+# @param #3 full name e.g. DCP-o-matic Batch Converter
+# @param #4 bundle id e.g. com.dcpomatic.batch
function make_dmg {
local appdir="$1"
- local full_name="$2"
- local bundle_id="$3"
+ local pkg="$2"
+ local full_name="$3"
+ local bundle_id="$4"
tmp_dmg=dcpomatic_tmp.dmg
dmg="$full_name $version.dmg"
vol_name=DCP-o-matic-$version
- codesign --deep --force --verify --verbose --options runtime --sign "Developer ID Application: Carl Hetherington (R82DXSR997)" "$appdir"
- if [ "$?" != "0" ]; then
- echo "Failed to sign .app"
- exit 1
+ sign "$appdir"
+
+ if [ "$pkg" != "" ]; then
+ productsign --sign "Developer ID Installer: Carl Hetherington (R82DXSR997)" "$pkg" "signed_temp.pkg"
+ if [ "$?" != "0" ]; then
+ echo "Failed to sign .pkg"
+ exit 1
+ fi
+ mv signed_temp.pkg "$pkg"
fi
mkdir -p $vol_name
cp -a "$appdir" $vol_name
+ if [ "$pkg" != "" ]; then
+ cp -a "$pkg" $vol_name
+ fi
ln -s /Applications "$vol_name/Applications"
cat<<EOF > "$vol_name/READ ME.txt"
Welcome to DCP-o-matic! The first time you run the program there may be
mehr auftreten.
EOF
+ if [ "$pkg" != "" ]; then
+ cat<<EOF > "$vol_name/READ ME.txt"
+
+To run this software successfully you must install $pkg before running
+the .app
+EOF
+ fi
+
+ if [ "$pkg" != "" ]; then
+ cat<<EOF > "$vol_name/READ ME.de_DE.txt"
+
+To run this software successfully you must install $pkg before running
+the .app
+EOF
+
+ fi
rm -f $tmp_dmg "$dmg"
hdiutil create -srcfolder $vol_name -volname $vol_name -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW -size $DMG_SIZE $tmp_dmg
attach=$(hdiutil attach -readwrite -noverify -noautoopen $tmp_dmg)
set current view of container window to icon view
set toolbar visible of container window to false
set statusbar visible of container window to false
- set the bounds of container window to {400, 200, 940, 300}
+ set the bounds of container window to {400, 200, 1160, 600}
+ set the bounds of container window to {400, 200, 1160, 600}
+ set the bounds of container window to {400, 200, 1160, 600}
set theViewOptions to the icon view options of container window
set arrangement of theViewOptions to not arranged
set icon size of theViewOptions to 64
set position of item "Applications" of container window to {265, 80}
set position of item "READ ME.txt" of container window to {430, 80}
set position of item "READ ME.de_DE.txt" of container window to {595, 80}
+ set position of item "DCP-o-matic Disk Writer.pkg" of container window to {90, 255}
close
open
update without registering applications
copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2.Info.plist "$approot/Info.plist"
rl=("$approot/MacOS/dcpomatic2" "$approot/MacOS/dcpomatic2_cli" "$approot/MacOS/dcpomatic2_create" "$approot/MacOS/ffprobe" "$approot/Frameworks/"*.dylib)
-relink "${rl[@]}"
-make_dmg "$appdir" "DCP-o-matic" com.dcpomatic
+relink_relative "${rl[@]}"
+make_dmg "$appdir" "" "DCP-o-matic" com.dcpomatic
# DCP-o-matic KDM Creator
setup "DCP-o-matic 2 KDM Creator.app"
copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_kdm.Info.plist "$approot/Info.plist"
rl=("$approot/MacOS/dcpomatic2_kdm" "$approot/MacOS/dcpomatic2_kdm_cli" "$approot/Frameworks/"*.dylib)
-relink "${rl[@]}"
-make_dmg "$appdir" "DCP-o-matic KDM Creator" com.dcpomatic.kdm
+relink_relative "${rl[@]}"
+make_dmg "$appdir" "" "DCP-o-matic KDM Creator" com.dcpomatic.kdm
# DCP-o-matic Encode Server
setup "DCP-o-matic 2 Encode Server.app"
copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_server.Info.plist "$approot/Info.plist"
rl=("$approot/MacOS/dcpomatic2_server" "$approot/MacOS/dcpomatic2_server_cli" "$approot/Frameworks/"*.dylib)
-relink "${rl[@]}"
-make_dmg "$appdir" "DCP-o-matic Encode Server" com.dcpomatic.server
+relink_relative "${rl[@]}"
+make_dmg "$appdir" "" "DCP-o-matic Encode Server" com.dcpomatic.server
# DCP-o-matic Batch Converter
setup "DCP-o-matic 2 Batch converter.app"
copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_batch.Info.plist "$approot/Info.plist"
rl=("$approot/MacOS/dcpomatic2_batch" "$approot/Frameworks/"*.dylib)
-relink "${rl[@]}"
-make_dmg "$appdir" "DCP-o-matic Batch Converter" com.dcpomatic.batch
+relink_relative "${rl[@]}"
+make_dmg "$appdir" "" "DCP-o-matic Batch Converter" com.dcpomatic.batch
# DCP-o-matic Player
setup "DCP-o-matic 2 Player.app"
copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_player.Info.plist "$approot/Info.plist"
rl=("$approot/MacOS/dcpomatic2_player" "$approot/Frameworks/"*.dylib)
-relink "${rl[@]}"
-make_dmg "$appdir" "DCP-o-matic Player" com.dcpomatic.player
+relink_relative "${rl[@]}"
+make_dmg "$appdir" "" "DCP-o-matic Player" com.dcpomatic.player
# DCP-o-matic Playlist Editor
setup "DCP-o-matic 2 Playlist Editor.app"
copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_playlist.Info.plist "$approot/Info.plist"
rl=("$approot/MacOS/dcpomatic2_playlist" "$approot/Frameworks/"*.dylib)
-relink "${rl[@]}"
-make_dmg "$appdir" "DCP-o-matic Playlist Editor" com.dcpomatic.playlist
+relink_relative "${rl[@]}"
+make_dmg "$appdir" "" "DCP-o-matic Playlist Editor" com.dcpomatic.playlist
+
+# DCP-o-matic Disk Writer .app
+setup "DCP-o-matic 2 Disk Writer.app"
+copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_disk "$approot/MacOS"
+cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_disk.Info.plist "$approot/Info.plist"
+rl=("$approot/MacOS/dcpomatic2_disk" "$approot/Frameworks/"*.dylib)
+relink_relative "${rl[@]}"
+
+# DCP-o-matic Disk Writer daemon .pkg
+
+pkgbase=tmp-disk-writer
+rm -rf $pkgbase
+mkdir $pkgbase
+pkgbin=$pkgbase/bin
+pkgroot=$pkgbase/root
+
+mkdir -p $pkgroot/Library/LaunchDaemons
+cat > $pkgroot/Library/LaunchDaemons/com.dcpomatic.disk.writer.plist <<EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.dcpomatic.disk.writer</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/Library/Application Support/com.dcpomatic/dcpomatic2_disk_writer</string>
+ </array>
+ <key>EnvironmentVariables</key>
+ <dict>
+ <key>DYLD_LIBRARY_PATH</key>
+ <string><![CDATA[/Library/Application Support/com.dcpomatic]]></string>
+ </dict>
+ <key>KeepAlive</key>
+ <true/>
+ <key>RunAtLoad</key>
+ <true/>
+ <key>Debug</key>
+ <true/>
+ <key>StandardOutPath</key>
+ <string>/Users/carl/damon.out.log</string>
+ <key>StandardErrorPath</key>
+ <string>/Users/carl/damon.err.log</string>
+</dict>
+</plist>
+EOF
+
+# Get the binaries together in $pkgbin then move them to the
+# place with spaces in the filename to avoid some of the pain of escaping
+
+mkdir $pkgbin
+copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_disk_writer "$pkgbin"
+copy_libs "$pkgbin"
+
+rl=("$pkgbin/dcpomatic2_disk_writer" "$pkgbin/"*.dylib)
+relink_absolute "/Library/Application Support/com.dcpomatic" "${rl[@]}"
+
+mkdir $pkgbase/scripts
+cat > $pkgbase/scripts/postinstall <<EOF
+#!/bin/sh
+/bin/launchctl load "/Library/LaunchDaemons/com.dcpomatic.disk.writer.plist"
+exit 0
+EOF
+chmod gou+x $pkgbase/scripts/postinstall
+
+find "$pkgbin" -iname "*.dylib" -print0 | while IFS= read -r -d '' f; do
+ sign "$f"
+done
+sign "$pkgbin/dcpomatic2_disk_writer"
+
+mkdir -p "$pkgroot/Library/Application Support/com.dcpomatic"
+mv $pkgbin/* "$pkgroot/Library/Application Support/com.dcpomatic/"
+pkgbuild --root $pkgroot --identifier com.dcpomatic.disk.writer --scripts $pkgbase/scripts "DCP-o-matic Disk Writer.pkg"
+
+make_dmg "$appdir" "DCP-o-matic Disk Writer.pkg" "DCP-o-matic Disk Writer" com.dcpomatic.disk
--- /dev/null
+base=$HOME/dcpomatic
+env=$HOME/osx-environment
+sdk=$HOME/SDK
+
+export CPPFLAGS= LDFLAGS="-L$base/lib -L$env/lib -isysroot $sdk/MacOSX10.9.sdk -arch x86_64"
+export LINKFLAGS="-L$base/lib -L$env/lib -isysroot $sdk/MacOSX10.9.sdk -arch x86_64"
+export MACOSX_DEPLOYMENT_TARGET=10.9
+export CXXFLAGS="-I$base/include -I$env/include -isysroot $sdk/MacOSX10.9.sdk -arch x86_64"
+export CFLAGS="-I$base/include -I$env/include -isysroot $sdk/MacOSX10.9.sdk -arch x86_64"
+export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$base/lib:$env/64/lib
+export PATH=$env/64/bin:$PATH
+export PKG_CONFIG_PATH=$env/64/lib/pkgconfig:$base/lib/pkgconfig
obj = bld(features='subst', source='dcpomatic2_batch.Info.plist.in', target='dcpomatic2_batch.Info.plist', version=bld.env.VERSION)
obj = bld(features='subst', source='dcpomatic2_player.Info.plist.in', target='dcpomatic2_player.Info.plist', version=bld.env.VERSION)
obj = bld(features='subst', source='dcpomatic2_playlist.Info.plist.in', target='dcpomatic2_playlist.Info.plist', version=bld.env.VERSION)
+ obj = bld(features='subst', source='dcpomatic2_disk.Info.plist.in', target='dcpomatic2_disk.Info.plist', version=bld.env.VERSION)
--- /dev/null
+#!/bin/bash
+
+MXE=/opt/mxe/usr/x86_64-w64-mingw32.shared/bin
+
+dir=$1
+if [ "$dir" == "" ]; then
+ echo "Syntax: $0 <dir>"
+ exit 1
+fi
+
+cp $MXE/libgcc_s_seh-1.dll $dir
+cp $MXE/libstdc++-6.dll $dir
+cp $MXE/libboost*.dll $dir
+cp $MXE/libglibmm*.dll $dir
+cp $MXE/libcrypto*.dll $dir
+cp $MXE/libwinpthread*.dll $dir
+cp $MXE/wxbase*.dll $dir
+cp $MXE/wxmsw*.dll $dir
+cp $MXE/libcurl*.dll $dir
+cp $MXE/libxml*.dll $dir
+cp $MXE/libjpeg*.dll $dir
+cp $MXE/zlib*.dll $dir
+cp $MXE/libpng*.dll $dir
+cp $MXE/libtiff*.dll $dir
+cp $MXE/libssh*.dll $dir
+cp $MXE/libidn*.dll $dir
+cp $MXE/liblzma*.dll $dir
+cp $MXE/libiconv*.dll $dir
+cp $MXE/libxslt*.dll $dir
+cp $MXE/libltdl*.dll $dir
+cp $MXE/libintl*.dll $dir
+cp $MXE/libunistring*.dll $dir
+cp $MXE/libwebp*.dll $dir
+cp $MXE/libgcrypt*.dll $dir
+cp $MXE/libdl*.dll $dir
+cp $MXE/libgpg*.dll $dir
+cp $MXE/libcairo*.dll $dir
+cp $MXE/libfontconfig*.dll $dir
+cp $MXE/libglib*.dll $dir
+cp $MXE/icu*.dll $dir
+cp $MXE/libnettle*.dll $dir
+cp $MXE/libpango*.dll $dir
+cp $MXE/libsamplerate*.dll $dir
+cp $MXE/libzip*.dll $dir
+cp $MXE/libgmodule*.dll $dir
+cp $MXE/libgobject*.dll $dir
+cp $MXE/libsigc*.dll $dir
+cp $MXE/libpcre*.dll $dir
+cp $MXE/libx264*.dll $dir
+cp $MXE/libbz2*.dll $dir
+cp $MXE/libexpat*.dll $dir
+cp $MXE/libfreetype*.dll $dir
+cp $MXE/libffi*.dll $dir
+cp $MXE/libharfbuzz*.dll $dir
+cp $MXE/libpixman*.dll $dir
+cp $MXE/libnanomsg*.dll $dir
--- /dev/null
+gdb.exe -x gdb_script dcpomatic2_disk.exe > %HOMEPATH%/Documents/dcpomatic_debug_log.txt
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">\r
+ <assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="dcpomatic2_disk_writer" type="win32"/> \r
+ <description>DCP-o-matic Disk Writer</description> \r
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">\r
+ <security>\r
+ <requestedPrivileges>\r
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>\r
+ </requestedPrivileges>\r
+ </security>\r
+ </trustInfo>\r
+</assembly>\r
--- /dev/null
+id ICON "../../graphics/windows/dcpomatic2_disk.ico"
+#include "wx-3.0/wx/msw/wx.rc"
--- /dev/null
+#include "winuser.h"\r
+1 RT_MANIFEST "dcpomatic2_disk_writer.exe.manifest"\r
--- /dev/null
+set DCPOMATIC=z:
+set PATH=%PATH%;%DCPOMATIC%\lib;%DCPOMATIC%\bin;%DCPOMATIC%\src\dcpomatic\build\src\lib;%DCPOMATIC%\src\dcpomatic\build\src\wx
from __future__ import print_function
import os
-def write_installer(bits, windows_version, dcpomatic_version, debug, variant):
+def write_installer(bits, windows_version, dcpomatic_version, debug, variant, disk):
try:
os.makedirs('build/platform/windows')
except:
File "%static_deps%/bin/libboost_chrono-mt.dll"
File "%static_deps%/bin/libboost_filesystem-mt.dll"
File "%static_deps%/bin/libboost_system-mt.dll"
-File "%static_deps%/bin/libboost_thread_win32-mt.dll"
+File "%static_deps%/bin/libboost_thread-mt.dll"
File "%static_deps%/bin/libboost_date_time-mt.dll"
File "%static_deps%/bin/libboost_locale-mt.dll"
File "%static_deps%/bin/libboost_regex-mt.dll"
-File "%static_deps%/bin/libeay32.dll"
""", file=f)
if bits == 32:
File "%static_deps%/bin/libffi-6.dll"
File "%static_deps%/bin/openssl.exe"
File "%static_deps%/bin/libcurl-4.dll"
-File "%static_deps%/bin/ssleay32.dll"
-File "%static_deps%/bin/libzip-4.dll"
+File "%static_deps%/bin/libzip.dll"
File "%static_deps%/bin/libcairomm-1.0-1.dll"
File "%static_deps%/bin/libpangomm-1.4-1.dll"
File "%static_deps%/bin/libsamplerate-0.dll"
-File "%static_deps%/bin/libnettle-6.dll"
-File "%static_deps%/bin/icuuc56.dll"
-File "%static_deps%/bin/icudt56.dll"
-File "%static_deps%/bin/icuin56.dll"
+File "%static_deps%/bin/libnettle-7.dll"
+File "%static_deps%/bin/icuuc65.dll"
+File "%static_deps%/bin/icudt65.dll"
+File "%static_deps%/bin/icuin65.dll"
File "%static_deps%/bin/liblzma-5.dll"
File "%static_deps%/bin/libpcre-1.dll"
File "%static_deps%/bin/libharfbuzz-0.dll"
File "%static_deps%/bin/libwinpthread-1.dll"
File "%static_deps%/bin/libgnutls-30.dll"
File "%static_deps%/bin/libgmp-10.dll"
-File "%static_deps%/bin/libhogweed-4.dll"
+File "%static_deps%/bin/libhogweed-5.dll"
File "%static_deps%/bin/libidn2-0.dll"
File "%static_deps%/bin/libunistring-2.dll"
File "%static_deps%/bin/libssh2-1.dll"
File "%static_deps%/bin/libgcrypt-20.dll"
-""", file=f)
-
- if bits == 32:
- print('File "%static_deps%/bin/libgpg-error-0.dll"', file=f)
- else:
- print('File "%static_deps%/bin/libgpg-error6-0.dll"', file=f)
-
- print("""
+File "%static_deps%/bin/libgpg-error-0.dll"
File "%static_deps%/bin/libpangoft2-1.0-0.dll"
-File "%static_deps%/bin/libx264-152.dll"
-
+File "%static_deps%/bin/libx264-155.dll"
+File "%static_deps%/bin/libwebp-7.dll"
+File "%static_deps%/bin/libcrypto-1_1-x64.dll"
+File "%static_deps%/bin/libltdl-7.dll"
+File "%static_deps%/bin/libdl.dll"
File "%cdist_deps%/bin/asdcp-carl.dll"
File "%cdist_deps%/bin/kumu-carl.dll"
""", file=f)
+ if disk:
+ print("""
+File "%static_deps%/bin/libnanomsg.dll"
+File "%cdist_deps%/lib/libblockdev.dll"
+File "%cdist_deps%/lib/liblwext4.dll"
+ """, file=f)
+
if windows_version == 'xp':
print("""
File "%cdist_deps%/bin/avcodec-57.dll"
print('File "%resources%/dcpomatic2_batch_debug.bat"', file=f)
print('File "%resources%/dcpomatic2_kdm_debug.bat"', file=f)
print('File "%resources%/dcpomatic2_player_debug.bat"', file=f)
+ if disk:
+ print('File "%resources%/dcpomatic2_disk_debug.bat"', file=f)
print('File "%mingw%/bin/gdb.exe"', file=f)
else:
print('File "%binaries%/src/wx/dcpomatic2-wx.dll"', file=f)
File "%binaries%/src/tools/dcpomatic2_kdm_cli.exe"
""", file=f)
+ if disk:
+ print("""
+File "%binaries%/src/tools/dcpomatic2_disk.exe"
+File "%binaries%/src/tools/dcpomatic2_disk_writer.exe"
+File "%resources%/dcpomatic2_disk_writer.exe.manifest"
+ """, file=f)
+
print("""
File "%binaries%/src/tools/dcpomatic2_player.exe"
File "%binaries%/src/tools/dcpomatic2_playlist.exe"
print('CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2 debug\\DCP-o-matic 2 debug.lnk" "$INSTDIR\\bin\\dcpomatic2_debug.bat"', file=f)
print('CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2 debug\\DCP-o-matic 2 Batch Converter debug.lnk" "$INSTDIR\\bin\\dcpomatic2_batch_debug.bat" ""', file=f)
print('CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2 debug\\DCP-o-matic 2 KDM Creator debug.lnk" "$INSTDIR\\bin\\dcpomatic2_kdm_debug.bat" ""', file=f)
+ if disk:
+ print('CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2 debug\\DCP-o-matic 2 Disk Writer debug.lnk" "$INSTDIR\\bin\\dcpomatic2_disk_debug.bat" ""', file=f)
print('CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2 debug\\Uninstall DCP-o-matic 2 debug.lnk" "$INSTDIR\\Uninstall.exe"', file=f)
print('CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2 debug\\DCP-o-matic 2 Player debug.lnk" "$INSTDIR\\bin\\dcpomatic2_player_debug.bat" ""', file=f)
print('WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\DCP-o-matic 2 debug" "DisplayName" "DCP-o-matic 2 debug (remove only)"', file=f)
print('CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2\\DCP-o-matic 2.lnk" "$INSTDIR\\bin\\dcpomatic2.exe"', file=f)
print('CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2\\DCP-o-matic 2 Batch Converter.lnk" "$INSTDIR\\bin\\dcpomatic2_batch.exe"', file=f)
print('CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2\\DCP-o-matic 2 KDM Creator.lnk" "$INSTDIR\\bin\\dcpomatic2_kdm.exe"', file=f)
+ if disk:
+ print('CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2\\DCP-o-matic 2 Disk Writer.lnk" "$INSTDIR\\bin\\dcpomatic2_disk.exe"', file=f)
print('CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2\\Uninstall DCP-o-matic 2.lnk" "$INSTDIR\\Uninstall.exe"', file=f)
print('CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2\\DCP-o-matic 2 Player.lnk" "$INSTDIR\\bin\\dcpomatic2_player.exe"', file=f)
print('CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2\\DCP-o-matic 2 Playlist Editor.lnk" "$INSTDIR\\bin\\dcpomatic2_playlist.exe"', file=f)
print('CreateShortCut "$DESKTOP\\DCP-o-matic 2 debug.lnk" "$INSTDIR\\bin\\dcpomatic2_debug.bat" ""', file=f)
print('CreateShortCut "$DESKTOP\\DCP-o-matic 2 Batch Converter debug.lnk" "$INSTDIR\\bin\\dcpomatic2_batch_debug.bat" ""', file=f)
print('CreateShortCut "$DESKTOP\\DCP-o-matic 2 KDM Creator debug.lnk" "$INSTDIR\\bin\\dcpomatic2_kdm_debug.bat" ""', file=f)
+ if disk:
+ print('CreateShortCut "$DESKTOP\\DCP-o-matic 2 Disk Writer debug.lnk" "$INSTDIR\\bin\\dcpomatic2_disk_debug.bat" ""', file=f)
else:
print('Section "DCP-o-matic 2 desktop shortcuts" SEC_MASTER_DESKTOP', file=f)
print('CreateShortCut "$DESKTOP\\DCP-o-matic 2 Player.lnk" "$INSTDIR\\bin\\dcpomatic2_player.exe"', file=f)
print('CreateShortCut "$DESKTOP\\DCP-o-matic 2.lnk" "$INSTDIR\\bin\\dcpomatic2.exe" ""', file=f)
print('CreateShortCut "$DESKTOP\\DCP-o-matic 2 Batch Converter.lnk" "$INSTDIR\\bin\\dcpomatic2_batch.exe"', file=f)
print('CreateShortCut "$DESKTOP\\DCP-o-matic 2 KDM Creator.lnk" "$INSTDIR\\bin\\dcpomatic2_kdm.exe"', file=f)
+ if disk:
+ print('CreateShortCut "$DESKTOP\\DCP-o-matic 2 Disk Writer.lnk" "$INSTDIR\\bin\\dcpomatic2_disk.exe"', file=f)
print("SectionEnd", file=f)
print("""
Section "Encode server desktop shortcuts" SEC_SERVER_DESKTOP
-CreateShortCut "$DESKTOP\\DCP-o-matic 2 encode server.lnk" "$INSTDIR\\bin\\dcpomatic2_server.exe" ""
+CreateShortCut "$DESKTOP\\DCP-o-matic 2 Encode Server.lnk" "$INSTDIR\\bin\\dcpomatic2_server.exe" ""
SectionEnd
""", file=f)
else:
print('LangString DESC_SEC_MASTER ${LANG_ENGLISH} "DCP-o-matic 2"', file=f)
print('LangString DESC_SEC_MASTER_DESKTOP ${LANG_ENGLISH} "DCP-o-matic 2 desktop shortcuts"', file=f)
- print('LangString DESC_SEC_SERVER ${LANG_ENGLISH} "DCP-o-matic 2 encode server"', file=f)
- print('LangString DESC_SEC_SERVER_DESKTOP ${LANG_ENGLISH} "DCP-o-matic 2 encode server desktop shortcuts"', file=f)
+ print('LangString DESC_SEC_SERVER ${LANG_ENGLISH} "DCP-o-matic 2 Encode Server"', file=f)
+ print('LangString DESC_SEC_SERVER_DESKTOP ${LANG_ENGLISH} "DCP-o-matic 2 Encode Server desktop shortcuts"', file=f)
print("""
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
Delete "$DESKTOP\\DCP-o-matic 2 debug.lnk"
Delete "$DESKTOP\\DCP-o-matic 2 Batch Converter debug.lnk"
Delete "$DESKTOP\\DCP-o-matic 2 KDM Creator debug.lnk"
+Delete "$DESKTOP\\DCP-o-matic 2 Disk Writer debug.lnk"
Delete "$SMPROGRAMS\\DCP-o-matic 2 debug\\*.*"
RmDir "$SMPROGRAMS\\DCP-o-matic 2 debug"
DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\\DCP-o-matic 2 debug"
Delete "$DESKTOP\\DCP-o-matic 2 Batch Converter.lnk"
Delete "$DESKTOP\\DCP-o-matic 2 Encode Server.lnk"
Delete "$DESKTOP\\DCP-o-matic 2 KDM creator.lnk"
+Delete "$DESKTOP\\DCP-o-matic 2 Disk Writer.lnk"
Delete "$SMPROGRAMS\\DCP-o-matic 2\\*.*"
RmDir "$SMPROGRAMS\\DCP-o-matic 2"
DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\\DCP-o-matic 2"
def build(bld):
- write_installer(32, None, bld.env.VERSION, bld.env.DEBUG, bld.env.VARIANT)
- write_installer(64, None, bld.env.VERSION, bld.env.DEBUG, bld.env.VARIANT)
- write_installer(32, 'xp', bld.env.VERSION, bld.env.DEBUG, bld.env.VARIANT)
- write_installer(64, 'xp', bld.env.VERSION, bld.env.DEBUG, bld.env.VARIANT)
+ write_installer(32, None, bld.env.VERSION, bld.env.DEBUG, bld.env.VARIANT, bld.env.ENABLE_DISK)
+ write_installer(64, None, bld.env.VERSION, bld.env.DEBUG, bld.env.VARIANT, bld.env.ENABLE_DISK)
+ write_installer(32, 'xp', bld.env.VERSION, bld.env.DEBUG, bld.env.VARIANT, bld.env.ENABLE_DISK)
+ write_installer(64, 'xp', bld.env.VERSION, bld.env.DEBUG, bld.env.VARIANT, bld.env.ENABLE_DISK)
--- /dev/null
+#!/bin/bash
+
+export LD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:$LD_LIBRARY_PATH
+export DYLD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:/Users/carl/Environments/dcpomatic/64/lib
+if [ "$1" == "--debug" ]; then
+ shift
+ gdb --args build/src/tools/dcpomatic2_disk $*
+elif [ "$1" == "--valgrind" ]; then
+ shift
+ valgrind --tool="memcheck" --suppressions=suppressions --track-fds=yes --show-leak-kinds=all --leak-check=full build/src/tools/dcpomatic2_disk $*
+elif [ "$1" == "--callgrind" ]; then
+ shift
+ valgrind --tool="callgrind" build/src/tools/dcpomatic2_disk $*
+elif [ "$1" == "--massif" ]; then
+ shift
+ valgrind --tool="massif" build/src/tools/dcpomatic2_disk $*
+elif [ "$1" == "--i18n" ]; then
+ shift
+ LANGUAGE=de_DE.UTF8 LANG=de_DE.UTF8 LC_ALL=de_DE.UTF8 build/src/tools/dcpomatic2_disk "$*"
+elif [ "$1" == "--perf" ]; then
+ shift
+ perf record build/src/tools/dcpomatic2_disk $*
+elif [ "$1" == "--scaled" ]; then
+ shift
+ ~/src/run_scaled/run_scaled --sleep=5 --scale=0.5 build/src/tools/dcpomatic2_disk $*
+else
+ build/src/tools/dcpomatic2_disk $*
+fi
--- /dev/null
+#!/bin/bash
+
+export LD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:$LD_LIBRARY_PATH
+export DYLD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:/Users/carl/Environments/dcpomatic/64/lib
+exe=build/src/tools/dcpomatic2_disk_writer
+sudo chown root:root $exe
+sudo chmod 4755 $exe
+if [ "$1" == "--debug" ]; then
+ shift
+ gdb --args $exe $*
+elif [ "$1" == "--valgrind" ]; then
+ shift
+ valgrind --tool="memcheck" --suppressions=suppressions --track-fds=yes --show-leak-kinds=all --leak-check=full $exe $*
+elif [ "$1" == "--callgrind" ]; then
+ shift
+ valgrind --tool="callgrind" $exe $*
+elif [ "$1" == "--massif" ]; then
+ shift
+ valgrind --tool="massif" $exe $*
+elif [ "$1" == "--i18n" ]; then
+ shift
+ LANGUAGE=de_DE.UTF8 LANG=de_DE.UTF8 LC_ALL=de_DE.UTF8 $exe "$*"
+elif [ "$1" == "--perf" ]; then
+ shift
+ perf record $exe $*
+elif [ "$1" == "--scaled" ]; then
+ shift
+ ~/src/run_scaled/run_scaled --sleep=5 --scale=0.5 $exe $*
+else
+ $exe $*
+fi
--- /dev/null
+/*
+ Copyright (C) 2019-2020 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "disk_writer_messages.h"
+#include "copy_to_drive_job.h"
+#include "compose.hpp"
+#include "exceptions.h"
+#include <dcp/raw_convert.h>
+#include <nanomsg/nn.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <iostream>
+
+#include "i18n.h"
+
+using std::string;
+using std::cout;
+using std::min;
+using boost::shared_ptr;
+using dcp::raw_convert;
+
+CopyToDriveJob::CopyToDriveJob (boost::filesystem::path dcp, Drive drive, Nanomsg& nanomsg)
+ : Job (shared_ptr<Film>())
+ , _dcp (dcp)
+ , _drive (drive)
+ , _nanomsg (nanomsg)
+{
+
+}
+
+string
+CopyToDriveJob::name () const
+{
+ return String::compose (_("Copying %1 to %2"), _dcp.filename().string(), _drive.description());
+}
+
+string
+CopyToDriveJob::json_name () const
+{
+ return N_("copy");
+}
+
+void
+CopyToDriveJob::run ()
+{
+ if (!_nanomsg.nonblocking_send(String::compose("W\n%1\n%2\n", _dcp.string(), _drive.internal_name()))) {
+ throw CopyError ("Could not communicate with writer process", 0);
+ }
+
+ bool formatting = false;
+ while (true) {
+ string s = _nanomsg.blocking_get ();
+ if (s == DISK_WRITER_OK) {
+ set_state (FINISHED_OK);
+ return;
+ } else if (s == DISK_WRITER_ERROR) {
+ string const m = _nanomsg.blocking_get ();
+ string const n = _nanomsg.blocking_get ();
+ throw CopyError (m, raw_convert<int>(n));
+ } else if (s == DISK_WRITER_FORMATTING) {
+ sub ("Formatting drive");
+ set_progress_unknown ();
+ formatting = true;
+ } else if (s == DISK_WRITER_PROGRESS) {
+ if (formatting) {
+ sub ("Copying DCP");
+ formatting = false;
+ }
+ set_progress (raw_convert<float>(_nanomsg.blocking_get()));
+ }
+ }
+}
--- /dev/null
+/*
+ Copyright (C) 2019-2020 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "cross.h"
+#include "job.h"
+#include "nanomsg.h"
+
+class CopyToDriveJob : public Job
+{
+public:
+ CopyToDriveJob (boost::filesystem::path dcp, Drive drive, Nanomsg& nanomsg);
+
+ std::string name () const;
+ std::string json_name () const;
+ void run ();
+
+private:
+ void count (boost::filesystem::path dir, uint64_t& total_bytes);
+ void copy (boost::filesystem::path from, boost::filesystem::path to, uint64_t& total_remaining, uint64_t total);
+ boost::filesystem::path _dcp;
+ Drive _drive;
+ Nanomsg& _nanomsg;
+};
+++ /dev/null
-/*
- Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- DCP-o-matic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include "cross.h"
-#include "compose.hpp"
-#include "log.h"
-#include "dcpomatic_log.h"
-#include "config.h"
-#include "exceptions.h"
-extern "C" {
-#include <libavformat/avio.h>
-}
-#include <boost/algorithm/string.hpp>
-#ifdef DCPOMATIC_LINUX
-#include <unistd.h>
-#include <mntent.h>
-#endif
-#ifdef DCPOMATIC_WINDOWS
-#include <windows.h>
-#undef DATADIR
-#include <shlwapi.h>
-#include <shellapi.h>
-#include <fcntl.h>
-#endif
-#ifdef DCPOMATIC_OSX
-#include <sys/sysctl.h>
-#include <mach-o/dyld.h>
-#include <IOKit/pwr_mgt/IOPMLib.h>
-#endif
-#ifdef DCPOMATIC_POSIX
-#include <sys/types.h>
-#include <ifaddrs.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#endif
-#include <fstream>
-
-#include "i18n.h"
-
-using std::pair;
-using std::list;
-using std::ifstream;
-using std::string;
-using std::wstring;
-using std::make_pair;
-using std::runtime_error;
-using boost::shared_ptr;
-
-/** @param s Number of seconds to sleep for */
-void
-dcpomatic_sleep_seconds (int s)
-{
-#ifdef DCPOMATIC_POSIX
- sleep (s);
-#endif
-#ifdef DCPOMATIC_WINDOWS
- Sleep (s * 1000);
-#endif
-}
-
-void
-dcpomatic_sleep_milliseconds (int ms)
-{
-#ifdef DCPOMATIC_POSIX
- usleep (ms * 1000);
-#endif
-#ifdef DCPOMATIC_WINDOWS
- Sleep (ms);
-#endif
-}
-
-/** @return A string of CPU information (model name etc.) */
-string
-cpu_info ()
-{
- string info;
-
-#ifdef DCPOMATIC_LINUX
- /* This use of ifstream is ok; the filename can never
- be non-Latin
- */
- ifstream f ("/proc/cpuinfo");
- while (f.good ()) {
- string l;
- getline (f, l);
- if (boost::algorithm::starts_with (l, "model name")) {
- string::size_type const c = l.find (':');
- if (c != string::npos) {
- info = l.substr (c + 2);
- }
- }
- }
-#endif
-
-#ifdef DCPOMATIC_OSX
- char buffer[64];
- size_t N = sizeof (buffer);
- if (sysctlbyname ("machdep.cpu.brand_string", buffer, &N, 0, 0) == 0) {
- info = buffer;
- }
-#endif
-
-#ifdef DCPOMATIC_WINDOWS
- HKEY key;
- if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &key) != ERROR_SUCCESS) {
- return info;
- }
-
- DWORD type;
- DWORD data;
- if (RegQueryValueEx (key, L"ProcessorNameString", 0, &type, 0, &data) != ERROR_SUCCESS) {
- return info;
- }
-
- if (type != REG_SZ) {
- return info;
- }
-
- wstring value (data / sizeof (wchar_t), L'\0');
- if (RegQueryValueEx (key, L"ProcessorNameString", 0, 0, reinterpret_cast<LPBYTE> (&value[0]), &data) != ERROR_SUCCESS) {
- RegCloseKey (key);
- return info;
- }
-
- info = string (value.begin(), value.end());
-
- RegCloseKey (key);
-
-#endif
-
- return info;
-}
-
-#ifdef DCPOMATIC_OSX
-/** @return Path of the Contents directory in the .app */
-boost::filesystem::path
-app_contents ()
-{
- uint32_t size = 1024;
- char buffer[size];
- if (_NSGetExecutablePath (buffer, &size)) {
- throw runtime_error ("_NSGetExecutablePath failed");
- }
-
- boost::filesystem::path path (buffer);
- path = boost::filesystem::canonical (path);
- path = path.parent_path ();
- path = path.parent_path ();
- return path;
-}
-#endif
-
-boost::filesystem::path
-shared_path ()
-{
-#ifdef DCPOMATIC_LINUX
- char const * p = getenv ("DCPOMATIC_LINUX_SHARE_PREFIX");
- if (p) {
- return p;
- }
- return boost::filesystem::canonical (LINUX_SHARE_PREFIX);
-#endif
-#ifdef DCPOMATIC_WINDOWS
- wchar_t dir[512];
- GetModuleFileName (GetModuleHandle (0), dir, sizeof (dir));
- PathRemoveFileSpec (dir);
- boost::filesystem::path path = dir;
- return path.parent_path();
-#endif
-#ifdef DCPOMATIC_OSX
- return app_contents() / "Resources";
-#endif
-}
-
-void
-run_ffprobe (boost::filesystem::path content, boost::filesystem::path out)
-{
-#ifdef DCPOMATIC_WINDOWS
- SECURITY_ATTRIBUTES security;
- security.nLength = sizeof (security);
- security.bInheritHandle = TRUE;
- security.lpSecurityDescriptor = 0;
-
- HANDLE child_stderr_read;
- HANDLE child_stderr_write;
- if (!CreatePipe (&child_stderr_read, &child_stderr_write, &security, 0)) {
- LOG_ERROR_NC ("ffprobe call failed (could not CreatePipe)");
- return;
- }
-
- wchar_t dir[512];
- GetModuleFileName (GetModuleHandle (0), dir, sizeof (dir));
- PathRemoveFileSpec (dir);
- SetCurrentDirectory (dir);
-
- STARTUPINFO startup_info;
- ZeroMemory (&startup_info, sizeof (startup_info));
- startup_info.cb = sizeof (startup_info);
- startup_info.hStdError = child_stderr_write;
- startup_info.dwFlags |= STARTF_USESTDHANDLES;
-
- wchar_t command[512];
- wcscpy (command, L"ffprobe.exe \"");
-
- wchar_t file[512];
- MultiByteToWideChar (CP_UTF8, 0, content.string().c_str(), -1, file, sizeof(file));
- wcscat (command, file);
-
- wcscat (command, L"\"");
-
- PROCESS_INFORMATION process_info;
- ZeroMemory (&process_info, sizeof (process_info));
- if (!CreateProcess (0, command, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &startup_info, &process_info)) {
- LOG_ERROR_NC (N_("ffprobe call failed (could not CreateProcess)"));
- return;
- }
-
- FILE* o = fopen_boost (out, "w");
- if (!o) {
- LOG_ERROR_NC (N_("ffprobe call failed (could not create output file)"));
- return;
- }
-
- CloseHandle (child_stderr_write);
-
- while (true) {
- char buffer[512];
- DWORD read;
- if (!ReadFile(child_stderr_read, buffer, sizeof(buffer), &read, 0) || read == 0) {
- break;
- }
- fwrite (buffer, read, 1, o);
- }
-
- fclose (o);
-
- WaitForSingleObject (process_info.hProcess, INFINITE);
- CloseHandle (process_info.hProcess);
- CloseHandle (process_info.hThread);
- CloseHandle (child_stderr_read);
-#endif
-
-#ifdef DCPOMATIC_LINUX
- string ffprobe = "ffprobe \"" + content.string() + "\" 2> \"" + out.string() + "\"";
- LOG_GENERAL (N_("Probing with %1"), ffprobe);
- system (ffprobe.c_str ());
-#endif
-
-#ifdef DCPOMATIC_OSX
- boost::filesystem::path path = app_contents();
- path /= "MacOS";
- path /= "ffprobe";
-
- string ffprobe = "\"" + path.string() + "\" \"" + content.string() + "\" 2> \"" + out.string() + "\"";
- LOG_GENERAL (N_("Probing with %1"), ffprobe);
- system (ffprobe.c_str ());
-#endif
-}
-
-list<pair<string, string> >
-mount_info ()
-{
- list<pair<string, string> > m;
-
-#ifdef DCPOMATIC_LINUX
- FILE* f = setmntent ("/etc/mtab", "r");
- if (!f) {
- return m;
- }
-
- while (true) {
- struct mntent* mnt = getmntent (f);
- if (!mnt) {
- break;
- }
-
- m.push_back (make_pair (mnt->mnt_dir, mnt->mnt_type));
- }
-
- endmntent (f);
-#endif
-
- return m;
-}
-
-boost::filesystem::path
-openssl_path ()
-{
-#ifdef DCPOMATIC_WINDOWS
- wchar_t dir[512];
- GetModuleFileName (GetModuleHandle (0), dir, sizeof (dir));
- PathRemoveFileSpec (dir);
-
- boost::filesystem::path path = dir;
- path /= "openssl.exe";
- return path;
-#endif
-
-#ifdef DCPOMATIC_OSX
- boost::filesystem::path path = app_contents();
- path /= "MacOS";
- path /= "openssl";
- return path;
-#endif
-
-#ifdef DCPOMATIC_LINUX
- return "dcpomatic2_openssl";
-#endif
-
-}
-
-/* Apparently there is no way to create an ofstream using a UTF-8
- filename under Windows. We are hence reduced to using fopen
- with this wrapper.
-*/
-FILE *
-fopen_boost (boost::filesystem::path p, string t)
-{
-#ifdef DCPOMATIC_WINDOWS
- wstring w (t.begin(), t.end());
- /* c_str() here should give a UTF-16 string */
- return _wfopen (p.c_str(), w.c_str ());
-#else
- return fopen (p.c_str(), t.c_str ());
-#endif
-}
-
-int
-dcpomatic_fseek (FILE* stream, int64_t offset, int whence)
-{
-#ifdef DCPOMATIC_WINDOWS
- return _fseeki64 (stream, offset, whence);
-#else
- return fseek (stream, offset, whence);
-#endif
-}
-
-void
-Waker::nudge ()
-{
-#ifdef DCPOMATIC_WINDOWS
- boost::mutex::scoped_lock lm (_mutex);
- SetThreadExecutionState (ES_SYSTEM_REQUIRED);
-#endif
-}
-
-Waker::Waker ()
-{
-#ifdef DCPOMATIC_OSX
- boost::mutex::scoped_lock lm (_mutex);
- /* We should use this */
- // IOPMAssertionCreateWithName (kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR ("Encoding DCP"), &_assertion_id);
- /* but it's not available on 10.5, so we use this */
- IOPMAssertionCreate (kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &_assertion_id);
-#endif
-}
-
-Waker::~Waker ()
-{
-#ifdef DCPOMATIC_OSX
- boost::mutex::scoped_lock lm (_mutex);
- IOPMAssertionRelease (_assertion_id);
-#endif
-}
-
-void
-start_tool (boost::filesystem::path dcpomatic, string executable,
-#ifdef DCPOMATIC_OSX
- string app
-#else
- string
-#endif
- )
-{
-#if defined(DCPOMATIC_LINUX) || defined(DCPOMATIC_WINDOWS)
- boost::filesystem::path batch = dcpomatic.parent_path() / executable;
-#endif
-
-#ifdef DCPOMATIC_OSX
- boost::filesystem::path batch = dcpomatic.parent_path ();
- batch = batch.parent_path (); // MacOS
- batch = batch.parent_path (); // Contents
- batch = batch.parent_path (); // DCP-o-matic.app
- batch = batch.parent_path (); // Applications
- batch /= app;
- batch /= "Contents";
- batch /= "MacOS";
- batch /= executable;
-#endif
-
-#if defined(DCPOMATIC_LINUX) || defined(DCPOMATIC_OSX)
- pid_t pid = fork ();
- if (pid == 0) {
- int const r = system (batch.string().c_str());
- exit (WEXITSTATUS (r));
- }
-#endif
-
-#ifdef DCPOMATIC_WINDOWS
- STARTUPINFO startup_info;
- ZeroMemory (&startup_info, sizeof (startup_info));
- startup_info.cb = sizeof (startup_info);
-
- PROCESS_INFORMATION process_info;
- ZeroMemory (&process_info, sizeof (process_info));
-
- wchar_t cmd[512];
- MultiByteToWideChar (CP_UTF8, 0, batch.string().c_str(), -1, cmd, sizeof(cmd));
- CreateProcess (0, cmd, 0, 0, FALSE, 0, 0, 0, &startup_info, &process_info);
-#endif
-}
-
-void
-start_batch_converter (boost::filesystem::path dcpomatic)
-{
- start_tool (dcpomatic, "dcpomatic2_batch", "DCP-o-matic\\ 2\\ Batch\\ Converter.app");
-}
-
-void
-start_player (boost::filesystem::path dcpomatic)
-{
- start_tool (dcpomatic, "dcpomatic2_player", "DCP-o-matic\\ 2\\ Player.app");
-}
-
-uint64_t
-thread_id ()
-{
-#ifdef DCPOMATIC_WINDOWS
- return (uint64_t) GetCurrentThreadId ();
-#else
- return (uint64_t) pthread_self ();
-#endif
-}
-
-int
-avio_open_boost (AVIOContext** s, boost::filesystem::path file, int flags)
-{
-#ifdef DCPOMATIC_WINDOWS
- int const length = (file.string().length() + 1) * 2;
- char* utf8 = new char[length];
- WideCharToMultiByte (CP_UTF8, 0, file.c_str(), -1, utf8, length, 0, 0);
- int const r = avio_open (s, utf8, flags);
- delete[] utf8;
- return r;
-#else
- return avio_open (s, file.c_str(), flags);
-#endif
-}
-
-#ifdef DCPOMATIC_WINDOWS
-void
-maybe_open_console ()
-{
- if (Config::instance()->win32_console ()) {
- AllocConsole();
-
- HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);
- int hCrt = _open_osfhandle((intptr_t) handle_out, _O_TEXT);
- FILE* hf_out = _fdopen(hCrt, "w");
- setvbuf(hf_out, NULL, _IONBF, 1);
- *stdout = *hf_out;
-
- HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);
- hCrt = _open_osfhandle((intptr_t) handle_in, _O_TEXT);
- FILE* hf_in = _fdopen(hCrt, "r");
- setvbuf(hf_in, NULL, _IONBF, 128);
- *stdin = *hf_in;
- }
-}
-#endif
-
-boost::filesystem::path
-home_directory ()
-{
-#if defined(DCPOMATIC_LINUX) || defined(DCPOMATIC_OSX)
- return getenv("HOME");
-#endif
-#ifdef DCPOMATIC_WINDOWS
- return boost::filesystem::path(getenv("HOMEDRIVE")) / boost::filesystem::path(getenv("HOMEPATH"));
-#endif
-}
-
-string
-command_and_read (string cmd)
-{
-#ifdef DCPOMATIC_LINUX
- FILE* pipe = popen (cmd.c_str(), "r");
- if (!pipe) {
- throw runtime_error ("popen failed");
- }
-
- string result;
- char buffer[128];
- try {
- while (fgets(buffer, sizeof(buffer), pipe)) {
- result += buffer;
- }
- } catch (...) {
- pclose (pipe);
- throw;
- }
-
- pclose (pipe);
- return result;
-#endif
-
- return "";
-}
-
-/** @return true if this process is a 32-bit one running on a 64-bit-capable OS */
-bool
-running_32_on_64 ()
-{
-#ifdef DCPOMATIC_WINDOWS
- BOOL p;
- IsWow64Process (GetCurrentProcess(), &p);
- return p;
-#endif
- /* XXX: assuming nobody does this on Linux / OS X */
- return false;
-}
/*
- Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
#endif
#include <boost/filesystem.hpp>
#include <boost/thread/mutex.hpp>
+#include <boost/optional.hpp>
#ifdef DCPOMATIC_WINDOWS
#define WEXITSTATUS(w) (w)
extern void run_ffprobe (boost::filesystem::path, boost::filesystem::path);
extern std::list<std::pair<std::string, std::string> > mount_info ();
extern boost::filesystem::path openssl_path ();
+extern boost::filesystem::path disk_writer_path ();
#ifdef DCPOMATIC_OSX
extern boost::filesystem::path app_contents ();
#endif
extern boost::filesystem::path home_directory ();
extern std::string command_and_read (std::string cmd);
extern bool running_32_on_64 ();
+extern void unprivileged ();
+extern boost::filesystem::path config_path ();
+
+class PrivilegeEscalator
+{
+public:
+ PrivilegeEscalator ();
+ ~PrivilegeEscalator ();
+};
/** @class Waker
* @brief A class which tries to keep the computer awake on various operating systems.
#endif
};
+class Drive
+{
+public:
+ Drive (std::string internal_name, uint64_t size, bool mounted, boost::optional<std::string> vendor, boost::optional<std::string> model)
+ : _internal_name(internal_name)
+ , _size(size)
+ , _mounted(mounted)
+ , _vendor(vendor)
+ , _model(model)
+ {}
+
+ std::string description () const;
+ std::string internal_name () const {
+ return _internal_name;
+ }
+ bool mounted () const {
+ return _mounted;
+ }
+
+private:
+ std::string _internal_name;
+ /** size in bytes */
+ uint64_t _size;
+ bool _mounted;
+ boost::optional<std::string> _vendor;
+ boost::optional<std::string> _model;
+};
+
+std::vector<Drive> get_drives ();
+
#endif
--- /dev/null
+/*
+ Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "cross.h"
+#include "compose.hpp"
+
+#include "i18n.h"
+
+using std::string;
+
+string
+Drive::description () const
+{
+ char gb[64];
+ snprintf(gb, 64, "%.1f", _size / 1000000000.0);
+
+ string name;
+ if (_vendor) {
+ name += *_vendor;
+ }
+ if (_model) {
+ if (name.size() > 0) {
+ name += " " + *_model;
+ } else {
+ name = *_model;
+ }
+ }
+ if (name.size() == 0) {
+ name = _("Unknown");
+ }
+
+ return String::compose("%1 (%2 GB) [%3]", name, gb, _internal_name);
+}
+
--- /dev/null
+/*
+ Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "cross.h"
+#include "compose.hpp"
+#include "log.h"
+#include "dcpomatic_log.h"
+#include "config.h"
+#include "exceptions.h"
+#include <dcp/raw_convert.h>
+#include <glib.h>
+extern "C" {
+#include <libavformat/avio.h>
+}
+#include <boost/algorithm/string.hpp>
+#include <boost/foreach.hpp>
+#include <boost/dll/runtime_symbol_info.hpp>
+#include <unistd.h>
+#include <mntent.h>
+#include <sys/types.h>
+#include <ifaddrs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <fstream>
+
+#include "i18n.h"
+
+using std::pair;
+using std::list;
+using std::ifstream;
+using std::string;
+using std::wstring;
+using std::make_pair;
+using std::vector;
+using std::cerr;
+using std::cout;
+using std::runtime_error;
+using boost::shared_ptr;
+using boost::optional;
+
+/** @param s Number of seconds to sleep for */
+void
+dcpomatic_sleep_seconds (int s)
+{
+ sleep (s);
+}
+
+void
+dcpomatic_sleep_milliseconds (int ms)
+{
+ usleep (ms * 1000);
+}
+
+/** @return A string of CPU information (model name etc.) */
+string
+cpu_info ()
+{
+ string info;
+
+ /* This use of ifstream is ok; the filename can never
+ be non-Latin
+ */
+ ifstream f ("/proc/cpuinfo");
+ while (f.good ()) {
+ string l;
+ getline (f, l);
+ if (boost::algorithm::starts_with (l, "model name")) {
+ string::size_type const c = l.find (':');
+ if (c != string::npos) {
+ info = l.substr (c + 2);
+ }
+ }
+ }
+
+ return info;
+}
+
+boost::filesystem::path
+shared_path ()
+{
+ char const * p = getenv ("DCPOMATIC_LINUX_SHARE_PREFIX");
+ if (p) {
+ return p;
+ }
+ return boost::filesystem::canonical (LINUX_SHARE_PREFIX);
+}
+
+void
+run_ffprobe (boost::filesystem::path content, boost::filesystem::path out)
+{
+ string ffprobe = "ffprobe \"" + content.string() + "\" 2> \"" + out.string() + "\"";
+ LOG_GENERAL (N_("Probing with %1"), ffprobe);
+ system (ffprobe.c_str ());
+}
+
+list<pair<string, string> >
+mount_info ()
+{
+ list<pair<string, string> > m;
+
+ FILE* f = setmntent ("/etc/mtab", "r");
+ if (!f) {
+ return m;
+ }
+
+ while (true) {
+ struct mntent* mnt = getmntent (f);
+ if (!mnt) {
+ break;
+ }
+
+ m.push_back (make_pair (mnt->mnt_dir, mnt->mnt_type));
+ }
+
+ endmntent (f);
+
+ return m;
+}
+
+boost::filesystem::path
+openssl_path ()
+{
+ return "dcpomatic2_openssl";
+}
+
+boost::filesystem::path
+disk_writer_path ()
+{
+ return boost::dll::program_location().parent_path() / "dcpomatic2_disk_writer";
+}
+
+/* Apparently there is no way to create an ofstream using a UTF-8
+ filename under Windows. We are hence reduced to using fopen
+ with this wrapper.
+*/
+FILE *
+fopen_boost (boost::filesystem::path p, string t)
+{
+ return fopen (p.c_str(), t.c_str ());
+}
+
+int
+dcpomatic_fseek (FILE* stream, int64_t offset, int whence)
+{
+ return fseek (stream, offset, whence);
+}
+
+void
+Waker::nudge ()
+{
+
+}
+
+Waker::Waker ()
+{
+
+}
+
+Waker::~Waker ()
+{
+
+}
+
+void
+start_tool (boost::filesystem::path dcpomatic, string executable, string)
+{
+ boost::filesystem::path batch = dcpomatic.parent_path() / executable;
+
+ pid_t pid = fork ();
+ if (pid == 0) {
+ int const r = system (batch.string().c_str());
+ exit (WEXITSTATUS (r));
+ }
+}
+
+void
+start_batch_converter (boost::filesystem::path dcpomatic)
+{
+ start_tool (dcpomatic, "dcpomatic2_batch", "DCP-o-matic\\ 2\\ Batch\\ Converter.app");
+}
+
+void
+start_player (boost::filesystem::path dcpomatic)
+{
+ start_tool (dcpomatic, "dcpomatic2_player", "DCP-o-matic\\ 2\\ Player.app");
+}
+
+uint64_t
+thread_id ()
+{
+ return (uint64_t) pthread_self ();
+}
+
+int
+avio_open_boost (AVIOContext** s, boost::filesystem::path file, int flags)
+{
+ return avio_open (s, file.c_str(), flags);
+}
+
+
+boost::filesystem::path
+home_directory ()
+{
+ return getenv("HOME");
+}
+
+string
+command_and_read (string cmd)
+{
+ FILE* pipe = popen (cmd.c_str(), "r");
+ if (!pipe) {
+ throw runtime_error ("popen failed");
+ }
+
+ string result;
+ char buffer[128];
+ try {
+ while (fgets(buffer, sizeof(buffer), pipe)) {
+ result += buffer;
+ }
+ } catch (...) {
+ pclose (pipe);
+ throw;
+ }
+
+ pclose (pipe);
+ return result;
+}
+
+/** @return true if this process is a 32-bit one running on a 64-bit-capable OS */
+bool
+running_32_on_64 ()
+{
+ /* I'm assuming nobody does this on Linux */
+ return false;
+}
+
+vector<Drive>
+get_drives ()
+{
+ vector<Drive> drives;
+
+ using namespace boost::filesystem;
+ list<string> mounted_devices;
+ std::ifstream f("/proc/mounts");
+ string line;
+ while (f.good()) {
+ getline(f, line);
+ vector<string> bits;
+ boost::algorithm::split (bits, line, boost::is_any_of(" "));
+ if (bits.size() > 0 && boost::algorithm::starts_with(bits[0], "/dev/")) {
+ mounted_devices.push_back(bits[0]);
+ LOG_DISK("Mounted device %1", bits[0]);
+ }
+ }
+
+ for (directory_iterator i = directory_iterator("/sys/block"); i != directory_iterator(); ++i) {
+ string const name = i->path().filename().string();
+ path device_type_file("/sys/block/" + name + "/device/type");
+ optional<string> device_type;
+ if (exists(device_type_file)) {
+ device_type = dcp::file_to_string (device_type_file);
+ boost::trim(*device_type);
+ }
+ /* Device type 5 is "SCSI_TYPE_ROM" in blkdev.h; seems usually to be a CD/DVD drive */
+ if (!boost::algorithm::starts_with(name, "loop") && (!device_type || *device_type != "5")) {
+ uint64_t const size = dcp::raw_convert<uint64_t>(dcp::file_to_string(*i / "size")) * 512;
+ if (size == 0) {
+ continue;
+ }
+ bool mounted = false;
+ optional<string> vendor;
+ try {
+ vendor = dcp::file_to_string("/sys/block/" + name + "/device/vendor");
+ boost::trim(*vendor);
+ } catch (...) {}
+ optional<string> model;
+ try {
+ model = dcp::file_to_string("/sys/block/" + name + "/device/model");
+ boost::trim(*model);
+ } catch (...) {}
+ BOOST_FOREACH (string j, mounted_devices) {
+ if (boost::algorithm::starts_with(j, "/dev/" + name)) {
+ mounted = true;
+ }
+ }
+ drives.push_back(Drive("/dev/" + i->path().filename().string(), size, mounted, vendor, model));
+ LOG_DISK("Block device %1 size %2 %3 vendor %4 model %5", name, size, mounted ? "mounted" : "not mounted", vendor.get_value_or("[none]"), model.get_value_or("[none]"));
+ }
+ }
+
+ return drives;
+}
+
+void
+unprivileged ()
+{
+ uid_t ruid, euid, suid;
+ if (getresuid(&ruid, &euid, &suid) == -1) {
+ cerr << "getresuid() failed.\n";
+ exit (EXIT_FAILURE);
+ }
+ seteuid (ruid);
+}
+
+PrivilegeEscalator::~PrivilegeEscalator ()
+{
+ unprivileged ();
+}
+
+PrivilegeEscalator::PrivilegeEscalator ()
+{
+ seteuid (0);
+}
+
+boost::filesystem::path
+config_path ()
+{
+ boost::filesystem::path p;
+ p /= g_get_user_config_dir ();
+ p /= "dcpomatic2";
+ return p;
+}
--- /dev/null
+/*
+ Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "cross.h"
+#include "compose.hpp"
+#include "log.h"
+#include "dcpomatic_log.h"
+#include "config.h"
+#include "exceptions.h"
+#include <dcp/raw_convert.h>
+#include <glib.h>
+extern "C" {
+#include <libavformat/avio.h>
+}
+#include <boost/algorithm/string.hpp>
+#include <boost/foreach.hpp>
+#include <boost/dll/runtime_symbol_info.hpp>
+#include <boost/regex.hpp>
+#include <sys/sysctl.h>
+#include <mach-o/dyld.h>
+#include <IOKit/pwr_mgt/IOPMLib.h>
+#include <IOKit/storage/IOMedia.h>
+#include <DiskArbitration/DADisk.h>
+#include <DiskArbitration/DiskArbitration.h>
+#include <CoreFoundation/CFURL.h>
+#include <sys/types.h>
+#include <ifaddrs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <fstream>
+#include <cstring>
+
+#include "i18n.h"
+
+using std::pair;
+using std::list;
+using std::ifstream;
+using std::string;
+using std::wstring;
+using std::make_pair;
+using std::vector;
+using std::cerr;
+using std::cout;
+using std::runtime_error;
+using boost::shared_ptr;
+using boost::optional;
+
+/** @param s Number of seconds to sleep for */
+void
+dcpomatic_sleep_seconds (int s)
+{
+ sleep (s);
+}
+
+void
+dcpomatic_sleep_milliseconds (int ms)
+{
+ usleep (ms * 1000);
+}
+
+/** @return A string of CPU information (model name etc.) */
+string
+cpu_info ()
+{
+ string info;
+
+ char buffer[64];
+ size_t N = sizeof (buffer);
+ if (sysctlbyname ("machdep.cpu.brand_string", buffer, &N, 0, 0) == 0) {
+ info = buffer;
+ }
+
+ return info;
+}
+
+/** @return Path of the Contents directory in the .app */
+boost::filesystem::path
+app_contents ()
+{
+ return boost::dll::program_location().parent_path().parent_path();
+}
+
+boost::filesystem::path
+shared_path ()
+{
+ return app_contents() / "Resources";
+}
+
+void
+run_ffprobe (boost::filesystem::path content, boost::filesystem::path out)
+{
+ boost::filesystem::path path = app_contents();
+ path /= "MacOS";
+ path /= "ffprobe";
+
+ string ffprobe = "\"" + path.string() + "\" \"" + content.string() + "\" 2> \"" + out.string() + "\"";
+ LOG_GENERAL (N_("Probing with %1"), ffprobe);
+ system (ffprobe.c_str ());
+}
+
+list<pair<string, string> >
+mount_info ()
+{
+ list<pair<string, string> > m;
+ return m;
+}
+
+boost::filesystem::path
+openssl_path ()
+{
+ boost::filesystem::path path = app_contents();
+ path /= "MacOS";
+ path /= "openssl";
+ return path;
+}
+
+boost::filesystem::path
+disk_writer_path ()
+{
+ boost::filesystem::path path = app_contents();
+ path /= "MacOS";
+ path /= "dcpomatic2_disk_writer";
+ return path;
+}
+
+/* Apparently there is no way to create an ofstream using a UTF-8
+ filename under Windows. We are hence reduced to using fopen
+ with this wrapper.
+*/
+FILE *
+fopen_boost (boost::filesystem::path p, string t)
+{
+ return fopen (p.c_str(), t.c_str ());
+}
+
+int
+dcpomatic_fseek (FILE* stream, int64_t offset, int whence)
+{
+ return fseek (stream, offset, whence);
+}
+
+void
+Waker::nudge ()
+{
+
+}
+
+Waker::Waker ()
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ /* We should use this */
+ // IOPMAssertionCreateWithName (kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR ("Encoding DCP"), &_assertion_id);
+ /* but it's not available on 10.5, so we use this */
+ IOPMAssertionCreate (kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &_assertion_id);
+}
+
+Waker::~Waker ()
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ IOPMAssertionRelease (_assertion_id);
+}
+
+void
+start_tool (boost::filesystem::path dcpomatic, string executable, string app)
+{
+ boost::filesystem::path batch = dcpomatic.parent_path ();
+ batch = batch.parent_path (); // MacOS
+ batch = batch.parent_path (); // Contents
+ batch = batch.parent_path (); // DCP-o-matic.app
+ batch = batch.parent_path (); // Applications
+ batch /= app;
+ batch /= "Contents";
+ batch /= "MacOS";
+ batch /= executable;
+
+ pid_t pid = fork ();
+ if (pid == 0) {
+ int const r = system (batch.string().c_str());
+ exit (WEXITSTATUS (r));
+ }
+}
+
+void
+start_batch_converter (boost::filesystem::path dcpomatic)
+{
+ start_tool (dcpomatic, "dcpomatic2_batch", "DCP-o-matic\\ 2\\ Batch\\ Converter.app");
+}
+
+void
+start_player (boost::filesystem::path dcpomatic)
+{
+ start_tool (dcpomatic, "dcpomatic2_player", "DCP-o-matic\\ 2\\ Player.app");
+}
+
+uint64_t
+thread_id ()
+{
+ return (uint64_t) pthread_self ();
+}
+
+int
+avio_open_boost (AVIOContext** s, boost::filesystem::path file, int flags)
+{
+ return avio_open (s, file.c_str(), flags);
+}
+
+boost::filesystem::path
+home_directory ()
+{
+ return getenv("HOME");
+}
+
+string
+command_and_read (string cmd)
+{
+ return "";
+}
+
+/** @return true if this process is a 32-bit one running on a 64-bit-capable OS */
+bool
+running_32_on_64 ()
+{
+ /* I'm assuming nobody does this on OS X */
+ return false;
+}
+
+static optional<string>
+get_vendor (CFDictionaryRef& description)
+{
+ void const* str = CFDictionaryGetValue (description, kDADiskDescriptionDeviceVendorKey);
+ if (!str) {
+ return optional<string>();
+ }
+
+ string s = CFStringGetCStringPtr ((CFStringRef) str, kCFStringEncodingUTF8);
+ boost::algorithm::trim (s);
+ return s;
+}
+
+static optional<string>
+get_model (CFDictionaryRef& description)
+{
+ void const* str = CFDictionaryGetValue (description, kDADiskDescriptionDeviceModelKey);
+ if (!str) {
+ return optional<string>();
+ }
+
+ string s = CFStringGetCStringPtr ((CFStringRef) str, kCFStringEncodingUTF8);
+ boost::algorithm::trim (s);
+ return s;
+}
+
+struct MediaPath
+{
+ bool real; ///< true for a "real" disk, false for a synthesized APFS one
+ std::string prt; ///< "PRT" entry from the media path
+};
+
+static optional<MediaPath>
+analyse_media_path (CFDictionaryRef& description)
+{
+ using namespace boost::algorithm;
+
+ void const* str = CFDictionaryGetValue (description, kDADiskDescriptionMediaPathKey);
+ if (!str) {
+ return optional<MediaPath>();
+ }
+
+ string path(CFStringGetCStringPtr((CFStringRef) str, kCFStringEncodingUTF8));
+ MediaPath mp;
+ if (starts_with(path, "IODeviceTree:")) {
+ mp.real = true;
+ } else if (starts_with(path, "IOService:")) {
+ mp.real = false;
+ } else {
+ return optional<MediaPath>();
+ }
+
+ vector<string> bits;
+ split(bits, path, boost::is_any_of("/"));
+ BOOST_FOREACH (string i, bits) {
+ if (starts_with(i, "PRT")) {
+ mp.prt = i;
+ }
+ }
+
+ return mp;
+}
+
+static bool
+is_whole_drive (DADiskRef& disk)
+{
+ io_service_t service = DADiskCopyIOMedia (disk);
+ CFTypeRef whole_media_ref = IORegistryEntryCreateCFProperty (service, CFSTR(kIOMediaWholeKey), kCFAllocatorDefault, 0);
+ bool whole_media = false;
+ if (whole_media_ref) {
+ whole_media = CFBooleanGetValue((CFBooleanRef) whole_media_ref);
+ CFRelease (whole_media_ref);
+ }
+ IOObjectRelease (service);
+ return whole_media;
+}
+
+static bool
+is_mounted (CFDictionaryRef& description)
+{
+ CFURLRef volume_path_key = (CFURLRef) CFDictionaryGetValue (description, kDADiskDescriptionVolumePathKey);
+ char mount_path_buffer[1024];
+ return CFURLGetFileSystemRepresentation(volume_path_key, false, (UInt8 *) mount_path_buffer, sizeof(mount_path_buffer));
+}
+
+/* Here follows some rather intricate and (probably) fragile code to find the list of available
+ * "real" drives on macOS that we might want to write a DCP to.
+ *
+ * We use the Disk Arbitration framework to give us a series of devices (/dev/disk0, /dev/disk1,
+ * /dev/disk1s1 and so on) and we use the API to gather useful information about these devices into
+ * a vector of Disk structs.
+ *
+ * Then we read the Disks that we found and try to derive a list of drives that we should offer to the
+ * user, with details of whether those drives are currently mounted or not.
+ *
+ * At the basic level we find the "disk"-level devices, looking at whether any of their partitions are mounted.
+ *
+ * This is complicated enormously by recent-ish macOS versions' habit of making `synthesized' volumes which
+ * reflect data in `real' partitions. So, for example, we might have a real (physical) drive /dev/disk2 with
+ * a partition /dev/disk2s2 whose content is made into a synthesized /dev/disk3, itself containing some partitions
+ * which are mounted. /dev/disk2s2 is not considered to be mounted, in this case. So we need to know that
+ * disk2s2 is related to disk3 so we can consider disk2s2 as mounted if any parts of disk3 are. In order to do
+ * this I am picking out what looks like a suitable identifier prefixed with PRT from the MediaContentKey.
+ * If disk2s2 and disk3 have the same PRT code I am assuming they are linked.
+ *
+ * Lots of this is guesswork and may be broken. In my defence the documentation that I have been able to
+ * unearth is, to put it impolitely, crap.
+ */
+
+struct Disk
+{
+ string device;
+ optional<string> vendor;
+ optional<string> model;
+ bool real;
+ string prt;
+ bool whole;
+ bool mounted;
+ unsigned long size;
+};
+
+static void
+disk_appeared (DADiskRef disk, void* context)
+{
+ const char* bsd_name = DADiskGetBSDName (disk);
+ if (!bsd_name) {
+ return;
+ }
+ LOG_DISK("%1 appeared", bsd_name);
+
+ Disk this_disk;
+
+ this_disk.device = string("/dev/") + bsd_name;
+
+ CFDictionaryRef description = DADiskCopyDescription (disk);
+
+ this_disk.vendor = get_vendor (description);
+ this_disk.model = get_model (description);
+ LOG_DISK("Vendor/model: %1 %2", this_disk.vendor.get_value_or("[none]"), this_disk.model.get_value_or("[none]"));
+
+ optional<MediaPath> media_path = analyse_media_path (description);
+ if (!media_path) {
+ LOG_DISK("Finding media path for %1 failed", bsd_name);
+ return;
+ }
+
+ this_disk.real = media_path->real;
+ this_disk.prt = media_path->prt;
+ this_disk.whole = is_whole_drive (disk);
+ this_disk.mounted = is_mounted (description);
+ LOG_DISK("%1 prt %2 whole %3 mounted %4", this_disk.real ? "Real" : "Synth", this_disk.prt, this_disk.whole ? "whole" : "part", this_disk.mounted ? "mounted" : "unmounted");
+
+ CFNumberGetValue ((CFNumberRef) CFDictionaryGetValue (description, kDADiskDescriptionMediaSizeKey), kCFNumberLongType, &this_disk.size);
+ CFRelease (description);
+
+ reinterpret_cast<vector<Disk>*>(context)->push_back(this_disk);
+}
+
+vector<Drive>
+get_drives ()
+{
+ using namespace boost::algorithm;
+ vector<Disk> disks;
+
+ DASessionRef session = DASessionCreate(kCFAllocatorDefault);
+ if (!session) {
+ return vector<Drive>();
+ }
+
+ DARegisterDiskAppearedCallback (session, NULL, disk_appeared, &disks);
+ CFRunLoopRef run_loop = CFRunLoopGetCurrent ();
+ DASessionScheduleWithRunLoop (session, run_loop, kCFRunLoopDefaultMode);
+ CFRunLoopStop (run_loop);
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.05, 0);
+ DAUnregisterCallback(session, (void *) disk_appeared, &disks);
+ CFRelease(session);
+
+ /* Mark disks containing mounted partitions as themselves mounted */
+ BOOST_FOREACH (Disk& i, disks) {
+ if (!i.whole) {
+ continue;
+ }
+ BOOST_FOREACH (Disk& j, disks) {
+ if (j.mounted && starts_with(j.device, i.device)) {
+ LOG_DISK("Marking %1 as mounted because %2 is", i.device, j.device);
+ i.mounted = true;
+ }
+ }
+ }
+
+ /* Make a list of the PRT codes of mounted, synthesized disks */
+ vector<string> mounted_synths;
+ BOOST_FOREACH (Disk& i, disks) {
+ if (!i.real && i.mounted) {
+ LOG_DISK("Found a mounted synth %1 with %2", i.device, i.prt);
+ mounted_synths.push_back (i.prt);
+ }
+ }
+
+ /* Mark containers of those mounted synths as themselves mounted */
+ BOOST_FOREACH (Disk& i, disks) {
+ if (i.real && find(mounted_synths.begin(), mounted_synths.end(), i.prt) != mounted_synths.end()) {
+ LOG_DISK("Marking %1 (%2) as mounted because it contains a mounted synth", i.device, i.prt);
+ i.mounted = true;
+ }
+ }
+
+ vector<Drive> drives;
+ BOOST_FOREACH (Disk& i, disks) {
+ if (i.whole) {
+ /* A whole disk that is not a container for a mounted synth */
+ LOG_DISK("Adding drive: %1 %2 %3 %4 %5", i.device, i.size, i.mounted ? "mounted" : "unmounted", i.vendor.get_value_or("[none]"), i.model.get_value_or("[none]"));
+ drives.push_back(Drive(i.device, i.size, i.mounted, i.vendor, i.model));
+ }
+ }
+ return drives;
+}
+
+boost::filesystem::path
+config_path ()
+{
+ boost::filesystem::path p;
+ p /= g_get_home_dir ();
+ p /= "Library";
+ p /= "Preferences";
+ p /= "com.dcpomatic";
+ p /= "2";
+ return p;
+}
--- /dev/null
+/*
+ Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "cross.h"
+#include "compose.hpp"
+#include "log.h"
+#include "dcpomatic_log.h"
+#include "config.h"
+#include "exceptions.h"
+#include "dcpomatic_assert.h"
+#include <dcp/raw_convert.h>
+#include <glib.h>
+extern "C" {
+#include <libavformat/avio.h>
+}
+#include <boost/algorithm/string.hpp>
+#include <boost/foreach.hpp>
+#include <boost/dll/runtime_symbol_info.hpp>
+#include <windows.h>
+#include <winternl.h>
+#include <winioctl.h>
+#include <ntdddisk.h>
+#include <setupapi.h>
+#undef DATADIR
+#include <shlwapi.h>
+#include <shellapi.h>
+#include <fcntl.h>
+#include <fstream>
+
+#include "i18n.h"
+
+using std::pair;
+using std::list;
+using std::ifstream;
+using std::string;
+using std::wstring;
+using std::make_pair;
+using std::vector;
+using std::cerr;
+using std::cout;
+using std::runtime_error;
+using boost::shared_ptr;
+using boost::optional;
+
+/** @param s Number of seconds to sleep for */
+void
+dcpomatic_sleep_seconds (int s)
+{
+ Sleep (s * 1000);
+}
+
+void
+dcpomatic_sleep_milliseconds (int ms)
+{
+ Sleep (ms);
+}
+
+/** @return A string of CPU information (model name etc.) */
+string
+cpu_info ()
+{
+ string info;
+
+ HKEY key;
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &key) != ERROR_SUCCESS) {
+ return info;
+ }
+
+ DWORD type;
+ DWORD data;
+ if (RegQueryValueEx (key, L"ProcessorNameString", 0, &type, 0, &data) != ERROR_SUCCESS) {
+ return info;
+ }
+
+ if (type != REG_SZ) {
+ return info;
+ }
+
+ wstring value (data / sizeof (wchar_t), L'\0');
+ if (RegQueryValueEx (key, L"ProcessorNameString", 0, 0, reinterpret_cast<LPBYTE> (&value[0]), &data) != ERROR_SUCCESS) {
+ RegCloseKey (key);
+ return info;
+ }
+
+ info = string (value.begin(), value.end());
+
+ RegCloseKey (key);
+
+ return info;
+}
+
+void
+run_ffprobe (boost::filesystem::path content, boost::filesystem::path out)
+{
+ SECURITY_ATTRIBUTES security;
+ security.nLength = sizeof (security);
+ security.bInheritHandle = TRUE;
+ security.lpSecurityDescriptor = 0;
+
+ HANDLE child_stderr_read;
+ HANDLE child_stderr_write;
+ if (!CreatePipe (&child_stderr_read, &child_stderr_write, &security, 0)) {
+ LOG_ERROR_NC ("ffprobe call failed (could not CreatePipe)");
+ return;
+ }
+
+ wchar_t dir[512];
+ GetModuleFileName (GetModuleHandle (0), dir, sizeof (dir));
+ PathRemoveFileSpec (dir);
+ SetCurrentDirectory (dir);
+
+ STARTUPINFO startup_info;
+ ZeroMemory (&startup_info, sizeof (startup_info));
+ startup_info.cb = sizeof (startup_info);
+ startup_info.hStdError = child_stderr_write;
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+
+ wchar_t command[512];
+ wcscpy (command, L"ffprobe.exe \"");
+
+ wchar_t file[512];
+ MultiByteToWideChar (CP_UTF8, 0, content.string().c_str(), -1, file, sizeof(file));
+ wcscat (command, file);
+
+ wcscat (command, L"\"");
+
+ PROCESS_INFORMATION process_info;
+ ZeroMemory (&process_info, sizeof (process_info));
+ if (!CreateProcess (0, command, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &startup_info, &process_info)) {
+ LOG_ERROR_NC (N_("ffprobe call failed (could not CreateProcess)"));
+ return;
+ }
+
+ FILE* o = fopen_boost (out, "w");
+ if (!o) {
+ LOG_ERROR_NC (N_("ffprobe call failed (could not create output file)"));
+ return;
+ }
+
+ CloseHandle (child_stderr_write);
+
+ while (true) {
+ char buffer[512];
+ DWORD read;
+ if (!ReadFile(child_stderr_read, buffer, sizeof(buffer), &read, 0) || read == 0) {
+ break;
+ }
+ fwrite (buffer, read, 1, o);
+ }
+
+ fclose (o);
+
+ WaitForSingleObject (process_info.hProcess, INFINITE);
+ CloseHandle (process_info.hProcess);
+ CloseHandle (process_info.hThread);
+ CloseHandle (child_stderr_read);
+}
+
+list<pair<string, string> >
+mount_info ()
+{
+ list<pair<string, string> > m;
+ return m;
+}
+
+static boost::filesystem::path
+executable_path ()
+{
+ return boost::dll::program_location().parent_path();
+}
+
+boost::filesystem::path
+shared_path ()
+{
+ return executable_path().parent_path();
+}
+
+boost::filesystem::path
+openssl_path ()
+{
+ return executable_path() / "openssl.exe";
+}
+
+boost::filesystem::path
+disk_writer_path ()
+{
+ return executable_path() / "dcpomatic2_disk_writer.exe";
+}
+
+/* Apparently there is no way to create an ofstream using a UTF-8
+ filename under Windows. We are hence reduced to using fopen
+ with this wrapper.
+*/
+FILE *
+fopen_boost (boost::filesystem::path p, string t)
+{
+ wstring w (t.begin(), t.end());
+ /* c_str() here should give a UTF-16 string */
+ return _wfopen (p.c_str(), w.c_str ());
+}
+
+int
+dcpomatic_fseek (FILE* stream, int64_t offset, int whence)
+{
+ return _fseeki64 (stream, offset, whence);
+}
+
+void
+Waker::nudge ()
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ SetThreadExecutionState (ES_SYSTEM_REQUIRED);
+}
+
+Waker::Waker ()
+{
+
+}
+
+Waker::~Waker ()
+{
+
+}
+
+void
+start_tool (boost::filesystem::path dcpomatic, string executable, string)
+{
+ boost::filesystem::path batch = dcpomatic.parent_path() / executable;
+
+ STARTUPINFO startup_info;
+ ZeroMemory (&startup_info, sizeof (startup_info));
+ startup_info.cb = sizeof (startup_info);
+
+ PROCESS_INFORMATION process_info;
+ ZeroMemory (&process_info, sizeof (process_info));
+
+ wchar_t cmd[512];
+ MultiByteToWideChar (CP_UTF8, 0, batch.string().c_str(), -1, cmd, sizeof(cmd));
+ CreateProcess (0, cmd, 0, 0, FALSE, 0, 0, 0, &startup_info, &process_info);
+}
+
+void
+start_batch_converter (boost::filesystem::path dcpomatic)
+{
+ start_tool (dcpomatic, "dcpomatic2_batch", "DCP-o-matic\\ 2\\ Batch\\ Converter.app");
+}
+
+void
+start_player (boost::filesystem::path dcpomatic)
+{
+ start_tool (dcpomatic, "dcpomatic2_player", "DCP-o-matic\\ 2\\ Player.app");
+}
+
+uint64_t
+thread_id ()
+{
+ return (uint64_t) GetCurrentThreadId ();
+}
+
+static string
+wchar_to_utf8 (wchar_t const * s)
+{
+ int const length = (wcslen(s) + 1) * 2;
+ char* utf8 = new char[length];
+ WideCharToMultiByte (CP_UTF8, 0, s, -1, utf8, length, 0, 0);
+ string u (utf8);
+ delete[] utf8;
+ return u;
+}
+
+int
+avio_open_boost (AVIOContext** s, boost::filesystem::path file, int flags)
+{
+ return avio_open (s, wchar_to_utf8(file.c_str()).c_str(), flags);
+}
+
+void
+maybe_open_console ()
+{
+ if (Config::instance()->win32_console ()) {
+ AllocConsole();
+
+ HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);
+ int hCrt = _open_osfhandle((intptr_t) handle_out, _O_TEXT);
+ FILE* hf_out = _fdopen(hCrt, "w");
+ setvbuf(hf_out, NULL, _IONBF, 1);
+ *stdout = *hf_out;
+
+ HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);
+ hCrt = _open_osfhandle((intptr_t) handle_in, _O_TEXT);
+ FILE* hf_in = _fdopen(hCrt, "r");
+ setvbuf(hf_in, NULL, _IONBF, 128);
+ *stdin = *hf_in;
+ }
+}
+
+boost::filesystem::path
+home_directory ()
+{
+ return boost::filesystem::path(getenv("HOMEDRIVE")) / boost::filesystem::path(getenv("HOMEPATH"));
+}
+
+string
+command_and_read (string)
+{
+ return "";
+}
+
+/** @return true if this process is a 32-bit one running on a 64-bit-capable OS */
+bool
+running_32_on_64 ()
+{
+ BOOL p;
+ IsWow64Process (GetCurrentProcess(), &p);
+ return p;
+}
+
+static optional<string>
+get_friendly_name (HDEVINFO device_info, SP_DEVINFO_DATA* device_info_data)
+{
+ wchar_t buffer[MAX_PATH];
+ ZeroMemory (&buffer, sizeof(buffer));
+ bool r = SetupDiGetDeviceRegistryPropertyW (
+ device_info, device_info_data, SPDRP_FRIENDLYNAME, 0, reinterpret_cast<PBYTE>(buffer), sizeof(buffer), 0
+ );
+ if (!r) {
+ return optional<string>();
+ }
+ return wchar_to_utf8 (buffer);
+}
+
+static const GUID GUID_DEVICE_INTERFACE_DISK = {
+ 0x53F56307L, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
+};
+
+static optional<int>
+get_device_number (HDEVINFO device_info, SP_DEVINFO_DATA* device_info_data)
+{
+ /* Find the Windows path to the device */
+
+ SP_DEVICE_INTERFACE_DATA device_interface_data;
+ device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+
+ BOOL r = SetupDiEnumDeviceInterfaces (device_info, device_info_data, &GUID_DEVICE_INTERFACE_DISK, 0, &device_interface_data);
+ if (!r) {
+ LOG_DISK("SetupDiEnumDeviceInterfaces failed (%1)", GetLastError());
+ return optional<int>();
+ }
+
+ /* Find out how much space we need for our SP_DEVICE_INTERFACE_DETAIL_DATA_W */
+ DWORD size;
+ r = SetupDiGetDeviceInterfaceDetailW(device_info, &device_interface_data, 0, 0, &size, 0);
+ PSP_DEVICE_INTERFACE_DETAIL_DATA_W device_detail_data = static_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_W> (malloc(size));
+ if (!device_detail_data) {
+ LOG_DISK_NC("malloc failed");
+ return optional<int>();
+ }
+
+ device_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
+
+ /* And get the path */
+ r = SetupDiGetDeviceInterfaceDetailW (device_info, &device_interface_data, device_detail_data, size, &size, 0);
+ if (!r) {
+ LOG_DISK_NC("SetupDiGetDeviceInterfaceDetailW failed");
+ free (device_detail_data);
+ return optional<int>();
+ }
+
+ /* Open it. We would not be allowed GENERIC_READ access here but specifying 0 for
+ dwDesiredAccess allows us to query some metadata.
+ */
+ HANDLE device = CreateFileW (
+ device_detail_data->DevicePath, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
+ OPEN_EXISTING, 0, 0
+ );
+
+ free (device_detail_data);
+
+ if (device == INVALID_HANDLE_VALUE) {
+ LOG_DISK("CreateFileW failed with %1", GetLastError());
+ return optional<int>();
+ }
+
+ /* Get the device number */
+ STORAGE_DEVICE_NUMBER device_number;
+ r = DeviceIoControl (
+ device, IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0,
+ &device_number, sizeof(device_number), &size, 0
+ );
+
+ CloseHandle (device);
+
+ if (!r) {
+ return optional<int>();
+ }
+
+ return device_number.DeviceNumber;
+}
+
+/** Take a volume path (with a trailing \) and add any disk numbers related to that volume
+ * to @ref disks.
+ */
+static void
+add_volume_disk_number (wchar_t* volume, vector<int>& disks)
+{
+ /* Strip trailing \ */
+ size_t const len = wcslen (volume);
+ DCPOMATIC_ASSERT (len > 0);
+ volume[len - 1] = L'\0';
+
+ HANDLE handle = CreateFileW (
+ volume, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
+ OPEN_EXISTING, 0, 0
+ );
+
+ DCPOMATIC_ASSERT (handle != INVALID_HANDLE_VALUE);
+
+ VOLUME_DISK_EXTENTS extents;
+ DWORD size;
+ BOOL r = DeviceIoControl (handle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, 0, 0, &extents, sizeof(extents), &size, 0);
+ CloseHandle (handle);
+ if (!r) {
+ return;
+ }
+ DCPOMATIC_ASSERT (extents.NumberOfDiskExtents == 1);
+ return disks.push_back (extents.Extents[0].DiskNumber);
+}
+
+/* Return a list of disk numbers that contain volumes; i.e. a list of disk numbers that should
+ * not be offered as targets to write to as they are "mounted" (whatever that means on Windows).
+ */
+vector<int>
+disk_numbers_with_volumes ()
+{
+ vector<int> disks;
+
+ wchar_t volume_name[512];
+ HANDLE volume = FindFirstVolumeW (volume_name, sizeof(volume_name) / sizeof(wchar_t));
+ if (volume == INVALID_HANDLE_VALUE) {
+ return disks;
+ }
+
+ add_volume_disk_number (volume_name, disks);
+ while (true) {
+ if (!FindNextVolumeW(volume, volume_name, sizeof(volume_name) / sizeof(wchar_t))) {
+ break;
+ }
+ add_volume_disk_number (volume_name, disks);
+ }
+ FindVolumeClose (volume);
+
+ return disks;
+}
+
+vector<Drive>
+get_drives ()
+{
+ vector<Drive> drives;
+
+ vector<int> disks_to_ignore = disk_numbers_with_volumes ();
+
+ /* Get a `device information set' containing information about all disks */
+ HDEVINFO device_info = SetupDiGetClassDevsA (&GUID_DEVICE_INTERFACE_DISK, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if (device_info == INVALID_HANDLE_VALUE) {
+ LOG_DISK_NC ("SetupDiClassDevsA failed");
+ return drives;
+ }
+
+ int i = 0;
+ while (true) {
+ /* Find out about the next disk */
+ SP_DEVINFO_DATA device_info_data;
+ device_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
+ if (!SetupDiEnumDeviceInfo(device_info, i, &device_info_data)) {
+ DWORD e = GetLastError();
+ if (e != ERROR_NO_MORE_ITEMS) {
+ LOG_DISK ("SetupDiEnumDeviceInfo failed (%1)", GetLastError());
+ }
+ break;
+ }
+ ++i;
+
+ optional<string> const friendly_name = get_friendly_name (device_info, &device_info_data);
+ optional<int> device_number = get_device_number (device_info, &device_info_data);
+ if (!device_number) {
+ continue;
+ }
+
+ string const physical_drive = String::compose("\\\\.\\PHYSICALDRIVE%1", *device_number);
+
+ HANDLE device = CreateFileA (
+ physical_drive.c_str(), 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
+ OPEN_EXISTING, 0, 0
+ );
+
+ if (device == INVALID_HANDLE_VALUE) {
+ LOG_DISK_NC("Could not open PHYSICALDRIVE");
+ continue;
+ }
+
+ DISK_GEOMETRY geom;
+ DWORD returned;
+ BOOL r = DeviceIoControl (
+ device, IOCTL_DISK_GET_DRIVE_GEOMETRY, 0, 0,
+ &geom, sizeof(geom), &returned, 0
+ );
+
+ if (r && find(disks_to_ignore.begin(), disks_to_ignore.end(), *device_number) == disks_to_ignore.end()) {
+ uint64_t const disk_size = geom.Cylinders.QuadPart * geom.TracksPerCylinder * geom.SectorsPerTrack * geom.BytesPerSector;
+ drives.push_back (Drive(physical_drive, disk_size, false, friendly_name, optional<string>()));
+ }
+
+ CloseHandle (device);
+ }
+
+ return drives;
+}
+
+string
+Drive::description () const
+{
+ char gb[64];
+ snprintf(gb, 64, "%.1f", _size / 1000000000.0);
+
+ string name;
+ if (_vendor) {
+ name += *_vendor;
+ }
+ if (_model) {
+ if (name.size() > 0) {
+ name += " " + *_model;
+ }
+ }
+ if (name.size() == 0) {
+ name = _("Unknown");
+ }
+
+ return String::compose("%1 (%2 GB) [%3]", name, gb, _internal_name);
+}
+
+boost::filesystem::path
+config_path ()
+{
+ boost::filesystem::path p;
+ p /= g_get_user_config_dir ();
+ p /= "dcpomatic2";
+ return p;
+}
#define LOG_DEBUG_PLAYER(...) dcpomatic_log->log(String::compose(__VA_ARGS__), LogEntry::TYPE_DEBUG_PLAYER);
#define LOG_DEBUG_THREED(...) dcpomatic_log->log(String::compose(__VA_ARGS__), LogEntry::TYPE_DEBUG_THREED);
#define LOG_DEBUG_THREED_NC(...) dcpomatic_log->log(__VA_ARGS__, LogEntry::TYPE_DEBUG_THREED);
+#define LOG_DISK(...) dcpomatic_log->log(String::compose(__VA_ARGS__), LogEntry::TYPE_DISK);
+#define LOG_DISK_NC(...) dcpomatic_log->log(__VA_ARGS__, LogEntry::TYPE_DISK);
--- /dev/null
+/*
+ Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/* dcpomatic_disk_writer receives
+
+DCP pathname\n
+Internal name of drive to write to\n
+
+ Then responds with one of the following.
+*/
+
+/** Write finished and everything was OK, e.g.
+
+D\n
+
+*/
+#define DISK_WRITER_OK "D"
+
+/** There was an error. Following this will come
+
+error message\n
+error number\n
+
+e.g.
+
+E\n
+Disc full\n
+42\n
+
+*/
+#define DISK_WRITER_ERROR "E"
+
+/** The disk writer is formatting the drive. It is not possible
+ * to give progress reports on this so the writer just tells us
+ * it's happening. This is finished when DISK_WRITER_PROGRESS
+ * messages start arriving
+ */
+#define DISK_WRITER_FORMATTING "F"
+
+/** Some progress has been made in the main "copy" part of the task.
+ * Following this will come
+
+progress as a float from 0 to 1\n
+
+e.g.
+
+P\n
+0.3\n
+
+*/
+#define DISK_WRITER_PROGRESS "P"
+
+/** dcpomatic_disk_writer may also receive
+
+Q\n
+
+as a request to quit.
+*/
+#define DISK_WRITER_QUIT "Q"
+
{
}
+
+CopyError::CopyError (string m, int n)
+ : runtime_error (String::compose("%1 (%2)", m, n))
+ , _message (m)
+ , _number (n)
+{
+
+}
+
+VerifyError::VerifyError (string m, int n)
+ : runtime_error (String::compose("%1 (%2)", m, n))
+ , _message (m)
+ , _number (n)
+{
+
+}
+
GLError (char const * last, int e);
};
+/** @class CopyError
+ * @brief An error which occurs when copying a DCP to a distribution drive.
+ */
+class CopyError : public std::runtime_error
+{
+public:
+ CopyError (std::string s, int n);
+ virtual ~CopyError () throw () {}
+
+ std::string message () const {
+ return _message;
+ }
+
+ int number () const {
+ return _number;
+ }
+
+private:
+ std::string _message;
+ int _number;
+};
+/** @class VerifyError
+ * @brief An error which occurs when verifying a DCP that we copied to a distribution drive.
+ */
+class VerifyError : public std::runtime_error
+{
+public:
+ VerifyError (std::string s, int n);
+ virtual ~VerifyError () throw () {}
+
+ std::string message () const {
+ return _message;
+ }
+
+ int number () const {
+ return _number;
+ }
+
+private:
+ std::string _message;
+ int _number;
+};
#endif
#include "config.h"
#include <cstdio>
#include <iostream>
+#include <cerrno>
using std::cout;
using std::string;
set_types (Config::instance()->log_types());
}
+FileLog::FileLog (boost::filesystem::path file, int types)
+ : _file (file)
+{
+ set_types (types);
+}
+
void
FileLog::do_log (shared_ptr<const LogEntry> entry)
{
FILE* f = fopen_boost (_file, "a");
if (!f) {
- cout << "(could not log to " << _file.string() << "): " << entry.get() << "\n";
+ cout << "(could not log to " << _file.string() << " error " << errno << "): " << entry->get() << "\n";
return;
}
{
public:
explicit FileLog (boost::filesystem::path file);
+ FileLog (boost::filesystem::path file, int types);
std::string head_and_tail (int amount = 1024) const;
void dcp_log (dcp::NoteType type, std::string message);
void set_types (int types);
+ int types () const {
+ return _types;
+ }
/** @param amount Approximate number of bytes to return; the returned value
* may be shorter or longer than this.
#include "i18n.h"
-int const LogEntry::TYPE_GENERAL = 0x1;
-int const LogEntry::TYPE_WARNING = 0x2;
-int const LogEntry::TYPE_ERROR = 0x4;
-int const LogEntry::TYPE_DEBUG_THREED = 0x8;
-int const LogEntry::TYPE_DEBUG_ENCODE = 0x10;
-int const LogEntry::TYPE_TIMING = 0x20;
-int const LogEntry::TYPE_DEBUG_EMAIL = 0x40;
-int const LogEntry::TYPE_DEBUG_PLAYER = 0x80;
+int const LogEntry::TYPE_GENERAL = 0x001;
+int const LogEntry::TYPE_WARNING = 0x002;
+int const LogEntry::TYPE_ERROR = 0x004;
+int const LogEntry::TYPE_DEBUG_THREED = 0x008;
+int const LogEntry::TYPE_DEBUG_ENCODE = 0x010;
+int const LogEntry::TYPE_TIMING = 0x020;
+int const LogEntry::TYPE_DEBUG_EMAIL = 0x040;
+int const LogEntry::TYPE_DEBUG_PLAYER = 0x080;
+int const LogEntry::TYPE_DISK = 0x100;
using std::string;
/*
- Copyright (C) 2015 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2015-2020 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
static const int TYPE_TIMING;
static const int TYPE_DEBUG_EMAIL;
static const int TYPE_DEBUG_PLAYER;
+ static const int TYPE_DISK;
explicit LogEntry (int type);
virtual ~LogEntry () {}
--- /dev/null
+/*
+ Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "nanomsg.h"
+#include "dcpomatic_log.h"
+#include <nanomsg/nn.h>
+#include <nanomsg/pair.h>
+#include <stdexcept>
+#include <cerrno>
+
+using std::string;
+using std::runtime_error;
+using boost::optional;
+
+#define NANOMSG_URL "ipc:///tmp/dcpomatic.ipc"
+
+Nanomsg::Nanomsg (bool server)
+{
+ _socket = nn_socket (AF_SP, NN_PAIR);
+ if (_socket < 0) {
+ throw runtime_error("Could not set up nanomsg socket");
+ }
+ if (server) {
+ if (nn_bind(_socket, NANOMSG_URL) < 0) {
+ throw runtime_error(String::compose("Could not bind nanomsg socket (%1)", errno));
+ }
+ } else {
+ if (nn_connect(_socket, NANOMSG_URL) < 0) {
+ throw runtime_error(String::compose("Could not connect nanomsg socket (%1)", errno));
+ }
+ }
+}
+
+void
+Nanomsg::blocking_send (string s)
+{
+ int const r = nn_send (_socket, s.c_str(), s.length(), 0);
+ if (r < 0) {
+ throw runtime_error(String::compose("Could not send to nanomsg socket (%1)", errno));
+ } else if (r != int(s.length())) {
+ throw runtime_error("Could not send to nanomsg socket (message too big)");
+ }
+}
+
+bool
+Nanomsg::nonblocking_send (string s)
+{
+ int const r = nn_send (_socket, s.c_str(), s.length(), NN_DONTWAIT);
+ if (r < 0) {
+ if (errno == EAGAIN) {
+ return false;
+ }
+ throw runtime_error(String::compose("Could not send to nanomsg socket (%1)", errno));
+ } else if (r != int(s.length())) {
+ throw runtime_error("Could not send to nanomsg socket (message too big)");
+ }
+
+ return true;
+}
+
+optional<string>
+Nanomsg::get_from_pending ()
+{
+ if (_pending.empty()) {
+ return optional<string>();
+ }
+
+ string const l = _pending.back();
+ _pending.pop_back();
+ return l;
+}
+
+void
+Nanomsg::recv_and_parse (bool blocking)
+{
+ char* buf = 0;
+ int const received = nn_recv (_socket, &buf, NN_MSG, blocking ? 0 : NN_DONTWAIT);
+ if (received < 0)
+ {
+ if (!blocking && errno == EAGAIN) {
+ return;
+ }
+
+ throw runtime_error ("Could not communicate with subprocess");
+ }
+
+ char* p = buf;
+ for (int i = 0; i < received; ++i) {
+ if (*p == '\n') {
+ _pending.push_front (_current);
+ _current = "";
+ } else {
+ _current += *p;
+ }
+ ++p;
+ }
+ nn_freemsg (buf);
+}
+
+string
+Nanomsg::blocking_get ()
+{
+ optional<string> l = get_from_pending ();
+ if (l) {
+ return *l;
+ }
+
+ recv_and_parse (true);
+
+ l = get_from_pending ();
+ if (!l) {
+ throw runtime_error ("Could not communicate with subprocess");
+ }
+
+ return *l;
+}
+
+optional<string>
+Nanomsg::nonblocking_get ()
+{
+ optional<string> l = get_from_pending ();
+ if (l) {
+ return *l;
+ }
+
+ recv_and_parse (false);
+ return get_from_pending ();
+}
--- /dev/null
+/*
+ Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <string>
+#include <list>
+#include <boost/optional.hpp>
+#include <boost/noncopyable.hpp>
+
+class Nanomsg : public boost::noncopyable
+{
+public:
+ explicit Nanomsg (bool server);
+
+ void blocking_send (std::string s);
+ /** Try to send a message, returning true if successful, false
+ * if we should try again (EAGAIN) or throwing an exception on any other
+ * error.
+ */
+ bool nonblocking_send (std::string s);
+ std::string blocking_get ();
+ boost::optional<std::string> nonblocking_get ();
+
+private:
+ boost::optional<std::string> get_from_pending ();
+ void recv_and_parse (bool blocking);
+
+ int _socket;
+ std::list<std::string> _pending;
+ std::string _current;
+};
+
/*
- Copyright (C) 2018 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2018-2020 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
*/
#include "state.h"
+#include "cross.h"
#include <glib.h>
using std::string;
if (override_path) {
p = *override_path;
} else {
-#ifdef DCPOMATIC_OSX
- p /= g_get_home_dir ();
- p /= "Library";
- p /= "Preferences";
- p /= "com.dcpomatic";
- p /= "2";
-#else
- p /= g_get_user_config_dir ();
- p /= "dcpomatic2";
-#endif
+ p = config_path ();
}
boost::system::error_code ec;
if (create_directories) {
content.cc
content_factory.cc
create_cli.cc
- cross.cc
+ cross_common.cc
crypto.cc
curl_uploader.cc
datasat_ap2x.cc
"""
if bld.env.TARGET_OSX:
- obj.framework = ['IOKit', 'Foundation']
+ obj.framework = ['IOKit', 'Foundation', 'DiskArbitration']
obj.source = sources + ' version.cc'
if bld.env.VARIANT == 'swaroop-theater' or bld.env.VARIANT == 'swaroop-studio':
obj.source += ' swaroop_spl.cc swaroop_spl_entry.cc'
+ if bld.env.ENABLE_DISK:
+ obj.source += ' copy_to_drive_job.cc nanomsg.cc'
+ obj.uselib += ' LWEXT4 NANOMSG'
+ if bld.env.TARGET_LINUX:
+ obj.uselib += ' POLKIT'
+
if bld.env.TARGET_WINDOWS:
- obj.uselib += ' WINSOCK2 DBGHELP SHLWAPI MSWSOCK BOOST_LOCALE'
+ obj.uselib += ' WINSOCK2 DBGHELP SHLWAPI MSWSOCK BOOST_LOCALE SETUPAPI'
+ obj.source += ' cross_windows.cc'
+ if bld.env.TARGET_OSX:
+ obj.source += ' cross_osx.cc'
+ if bld.env.TARGET_LINUX:
+ obj.source += ' cross_linux.cc'
if bld.env.STATIC_DCPOMATIC:
obj.uselib += ' XMLPP'
--- /dev/null
+/*
+ Copyright (C) 2019-2020 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "wx/wx_signal_manager.h"
+#include "wx/wx_util.h"
+#include "wx/job_manager_view.h"
+#include "wx/drive_wipe_warning_dialog.h"
+#include "lib/file_log.h"
+#include "lib/dcpomatic_log.h"
+#include "lib/util.h"
+#include "lib/config.h"
+#include "lib/signal_manager.h"
+#include "lib/cross.h"
+#include "lib/copy_to_drive_job.h"
+#include "lib/job_manager.h"
+#include "lib/disk_writer_messages.h"
+#include <wx/wx.h>
+#include <boost/process.hpp>
+#ifdef DCPOMATIC_WINDOWS
+#include <boost/process/windows.hpp>
+#endif
+#ifdef DCPOMATIC_OSX
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+using std::string;
+using std::exception;
+using std::cout;
+using std::cerr;
+using std::runtime_error;
+using boost::shared_ptr;
+
+class DOMFrame : public wxFrame
+{
+public:
+ explicit DOMFrame (wxString const & title)
+ : wxFrame (0, -1, title)
+ , _nanomsg (true)
+ , _sizer (new wxBoxSizer(wxVERTICAL))
+ {
+ /* Use a panel as the only child of the Frame so that we avoid
+ the dark-grey background on Windows.
+ */
+ wxPanel* overall_panel = new wxPanel (this);
+ wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+ s->Add (overall_panel, 1, wxEXPAND);
+ SetSizer (s);
+
+ wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+
+ int r = 0;
+ add_label_to_sizer (grid, overall_panel, _("DCP"), true, wxGBPosition(r, 0));
+ wxBoxSizer* dcp_name_sizer = new wxBoxSizer (wxHORIZONTAL);
+ _dcp_name = new wxStaticText (overall_panel, wxID_ANY, wxEmptyString);
+ dcp_name_sizer->Add (_dcp_name, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
+ _dcp_open = new wxButton (overall_panel, wxID_ANY, _("Open..."));
+ dcp_name_sizer->Add (_dcp_open, 0);
+ grid->Add (dcp_name_sizer, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND);
+ ++r;
+
+ add_label_to_sizer (grid, overall_panel, _("Drive"), true, wxGBPosition(r, 0));
+ wxBoxSizer* drive_sizer = new wxBoxSizer (wxHORIZONTAL);
+ _drive = new wxChoice (overall_panel, wxID_ANY);
+ drive_sizer->Add (_drive, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
+ _drive_refresh = new wxButton (overall_panel, wxID_ANY, _("Refresh"));
+ drive_sizer->Add (_drive_refresh, 0);
+ grid->Add (drive_sizer, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND);
+ ++r;
+
+ _jobs = new JobManagerView (overall_panel, false);
+ grid->Add (_jobs, wxGBPosition(r, 0), wxGBSpan(6, 2), wxEXPAND);
+ r += 6;
+
+ _copy = new wxButton (overall_panel, wxID_ANY, _("Copy DCP"));
+ grid->Add (_copy, wxGBPosition(r, 0), wxGBSpan(1, 2), wxEXPAND);
+ ++r;
+
+ grid->AddGrowableCol (1);
+
+ _dcp_open->Bind (wxEVT_BUTTON, boost::bind(&DOMFrame::open, this));
+ _copy->Bind (wxEVT_BUTTON, boost::bind(&DOMFrame::copy, this));
+ _drive->Bind (wxEVT_CHOICE, boost::bind(&DOMFrame::setup_sensitivity, this));
+ _drive_refresh->Bind (wxEVT_BUTTON, boost::bind(&DOMFrame::drive_refresh, this));
+
+ _sizer->Add (grid, 1, wxALL | wxEXPAND, DCPOMATIC_DIALOG_BORDER);
+ overall_panel->SetSizer (_sizer);
+ Fit ();
+ SetSize (768, GetSize().GetHeight() + 32);
+
+ /* XXX: this is a hack, but I expect we'll need logs and I'm not sure if there's
+ * a better place to put them.
+ */
+ dcpomatic_log.reset(new FileLog(config_path() / "disk.log"));
+ dcpomatic_log->set_types (dcpomatic_log->types() | LogEntry::TYPE_DISK);
+ LOG_DISK_NC("dcpomatic_disk started");
+
+ drive_refresh ();
+
+ Bind (wxEVT_SIZE, boost::bind (&DOMFrame::sized, this, _1));
+
+ JobManager::instance()->ActiveJobsChanged.connect(boost::bind(&DOMFrame::setup_sensitivity, this));
+
+#ifdef DCPOMATIC_WINDOWS
+ /* We must use ::shell here, it seems, to avoid error code 740 (related to privilege escalation) */
+ LOG_DISK("Starting writer process %1", disk_writer_path().string());
+ _writer = new boost::process::child (disk_writer_path(), boost::process::shell, boost::process::windows::hide);
+#endif
+
+#ifdef DCPOMATIC_LINUX
+ LOG_DISK("Starting writer process %1", disk_writer_path().string());
+ _writer = new boost::process::child (disk_writer_path());
+#endif
+
+ /* _writer is always running on macOS at the moment */
+ }
+
+ ~DOMFrame ()
+ {
+ _nanomsg.blocking_send(DISK_WRITER_QUIT "\n");
+ }
+
+private:
+ void sized (wxSizeEvent& ev)
+ {
+ _sizer->Layout ();
+ ev.Skip ();
+ }
+
+ void open ()
+ {
+ wxDirDialog* d = new wxDirDialog (this, _("Choose a DCP folder"), wxT(""), wxDD_DIR_MUST_EXIST);
+ int r = d->ShowModal ();
+ boost::filesystem::path const path (wx_to_std(d->GetPath()));
+ d->Destroy ();
+
+ if (r != wxID_OK) {
+ return;
+ }
+
+ _dcp_path = path;
+ _dcp_name->SetLabel (std_to_wx(_dcp_path->filename().string()));
+ setup_sensitivity ();
+ }
+
+ void copy ()
+ {
+ DCPOMATIC_ASSERT (_drive->GetSelection() != wxNOT_FOUND);
+ DCPOMATIC_ASSERT (static_cast<bool>(_dcp_path));
+ DriveWipeWarningDialog* d = new DriveWipeWarningDialog (this, _drive->GetString(_drive->GetSelection()));
+ int const r = d->ShowModal ();
+ bool ok = r == wxID_OK && d->confirmed();
+ d->Destroy ();
+
+ if (!ok) {
+ return;
+ }
+
+ JobManager::instance()->add(shared_ptr<Job>(new CopyToDriveJob(*_dcp_path, _drives[_drive->GetSelection()], _nanomsg)));
+ setup_sensitivity ();
+ }
+
+ void drive_refresh ()
+ {
+ int const sel = _drive->GetSelection ();
+ wxString current;
+ if (sel != wxNOT_FOUND) {
+ current = _drive->GetString (sel);
+ }
+ _drive->Clear ();
+ int re_select = wxNOT_FOUND;
+ int j = 0;
+ _drives.clear ();
+ BOOST_FOREACH (Drive i, get_drives()) {
+ if (!i.mounted()) {
+ _drives.push_back (i);
+ }
+ }
+ BOOST_FOREACH (Drive i, _drives) {
+ wxString const s = std_to_wx(i.description());
+ if (s == current) {
+ re_select = j;
+ }
+ _drive->Append(s);
+ ++j;
+ }
+ _drive->SetSelection (re_select);
+ setup_sensitivity ();
+ }
+
+ void setup_sensitivity ()
+ {
+ _copy->Enable (static_cast<bool>(_dcp_path) && _drive->GetSelection() != wxNOT_FOUND && !JobManager::instance()->work_to_do());
+ }
+
+ wxStaticText* _dcp_name;
+ wxButton* _dcp_open;
+ wxChoice* _drive;
+ wxButton* _drive_refresh;
+ wxButton* _copy;
+ JobManagerView* _jobs;
+ boost::optional<boost::filesystem::path> _dcp_path;
+ std::vector<Drive> _drives;
+ boost::process::child* _writer;
+ Nanomsg _nanomsg;
+ wxSizer* _sizer;
+};
+
+class App : public wxApp
+{
+public:
+ App ()
+ : _frame (0)
+ {}
+
+ bool OnInit ()
+ {
+ try {
+ Config::FailedToLoad.connect (boost::bind (&App::config_failed_to_load, this));
+ Config::Warning.connect (boost::bind (&App::config_warning, this, _1));
+
+ SetAppName (_("DCP-o-matic Disk Writer"));
+
+ if (!wxApp::OnInit()) {
+ return false;
+ }
+
+#ifdef DCPOMATIC_LINUX
+ unsetenv ("UBUNTU_MENUPROXY");
+#endif
+
+#ifdef __WXOSX__
+ ProcessSerialNumber serial;
+ GetCurrentProcess (&serial);
+ TransformProcessType (&serial, kProcessTransformToForegroundApplication);
+#endif
+
+ dcpomatic_setup_path_encoding ();
+
+ /* Enable i18n; this will create a Config object
+ to look for a force-configured language. This Config
+ object will be wrong, however, because dcpomatic_setup
+ hasn't yet been called and there aren't any filters etc.
+ set up yet.
+ */
+ dcpomatic_setup_i18n ();
+
+ /* Set things up, including filters etc.
+ which will now be internationalised correctly.
+ */
+ dcpomatic_setup ();
+
+ /* Force the configuration to be re-loaded correctly next
+ time it is needed.
+ */
+ Config::drop ();
+
+ _frame = new DOMFrame (_("DCP-o-matic Disk Writer"));
+ SetTopWindow (_frame);
+
+ _frame->Show ();
+
+ signal_manager = new wxSignalManager (this);
+ Bind (wxEVT_IDLE, boost::bind (&App::idle, this, _1));
+ }
+ catch (exception& e)
+ {
+ error_dialog (0, wxString::Format ("DCP-o-matic could not start."), std_to_wx(e.what()));
+ }
+
+ return true;
+ }
+
+ void config_failed_to_load ()
+ {
+ message_dialog (_frame, _("The existing configuration failed to load. Default values will be used instead. These may take a short time to create."));
+ }
+
+ void config_warning (string m)
+ {
+ message_dialog (_frame, std_to_wx(m));
+ }
+
+ void idle (wxIdleEvent& ev)
+ {
+ signal_manager->ui_idle ();
+ ev.Skip ();
+ }
+
+ DOMFrame* _frame;
+};
+
+IMPLEMENT_APP (App)
--- /dev/null
+/*
+ Copyright (C) 2019-2020 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "lib/disk_writer_messages.h"
+#include "lib/compose.hpp"
+#include "lib/exceptions.h"
+#include "lib/cross.h"
+#include "lib/digester.h"
+#include "lib/file_log.h"
+#include "lib/dcpomatic_log.h"
+#include "lib/nanomsg.h"
+extern "C" {
+#include <lwext4/ext4_mbr.h>
+#include <lwext4/ext4_fs.h>
+#include <lwext4/ext4_mkfs.h>
+#include <lwext4/ext4_errno.h>
+#include <lwext4/ext4_debug.h>
+#include <lwext4/ext4.h>
+}
+
+#ifdef DCPOMATIC_POSIX
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#ifdef DCPOMATIC_OSX
+#undef nil
+extern "C" {
+#include <lwext4/file_dev.h>
+}
+#endif
+
+#ifdef DCPOMATIC_LINUX
+#include <linux/fs.h>
+#include <polkit/polkit.h>
+extern "C" {
+#include <lwext4/file_dev.h>
+}
+#include <poll.h>
+#endif
+
+#ifdef DCPOMATIC_WINDOWS
+extern "C" {
+#include <lwext4/file_windows.h>
+}
+#endif
+
+#include <glibmm.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+#include <iostream>
+
+using std::cin;
+using std::min;
+using std::string;
+using std::runtime_error;
+using std::exception;
+using boost::optional;
+
+#ifdef DCPOMATIC_LINUX
+static PolkitAuthority* polkit_authority = 0;
+#endif
+static boost::filesystem::path dcp_path;
+static std::string device;
+static uint64_t const block_size = 4096;
+static Nanomsg* nanomsg = 0;
+
+static
+void
+count (boost::filesystem::path dir, uint64_t& total_bytes)
+{
+ using namespace boost::filesystem;
+ for (directory_iterator i = directory_iterator(dir); i != directory_iterator(); ++i) {
+ if (is_directory(*i)) {
+ count (*i, total_bytes);
+ } else {
+ total_bytes += file_size (*i);
+ }
+ }
+}
+
+static
+string
+write (boost::filesystem::path from, boost::filesystem::path to, uint64_t& total_remaining, uint64_t total)
+{
+ ext4_file out;
+ int r = ext4_fopen(&out, to.generic_string().c_str(), "wb");
+ if (r != EOK) {
+ throw CopyError (String::compose("Failed to open file %1", to.generic_string()), r);
+ }
+
+ FILE* in = fopen_boost (from, "rb");
+ if (!in) {
+ ext4_fclose (&out);
+ throw CopyError (String::compose("Failed to open file %1", from.string()), 0);
+ }
+
+ uint8_t* buffer = new uint8_t[block_size];
+ Digester digester;
+
+ uint64_t remaining = file_size (from);
+ while (remaining > 0) {
+ uint64_t const this_time = min(remaining, block_size);
+ size_t read = fread (buffer, 1, this_time, in);
+ if (read != this_time) {
+ fclose (in);
+ ext4_fclose (&out);
+ delete[] buffer;
+ throw CopyError (String::compose("Short read; expected %1 but read %2", this_time, read), 0);
+ }
+
+ digester.add (buffer, this_time);
+
+ size_t written;
+ r = ext4_fwrite (&out, buffer, this_time, &written);
+ if (r != EOK) {
+ fclose (in);
+ ext4_fclose (&out);
+ delete[] buffer;
+ throw CopyError ("Write failed", r);
+ }
+ if (written != this_time) {
+ fclose (in);
+ ext4_fclose (&out);
+ delete[] buffer;
+ throw CopyError (String::compose("Short write; expected %1 but wrote %2", this_time, written), 0);
+ }
+ remaining -= this_time;
+ total_remaining -= this_time;
+ nanomsg->blocking_send(String::compose(DISK_WRITER_PROGRESS "\n%1\n", (1 - float(total_remaining) / total)));
+ }
+
+ fclose (in);
+ ext4_fclose (&out);
+ delete[] buffer;
+
+ return digester.get ();
+}
+
+static
+string
+read (boost::filesystem::path from, boost::filesystem::path to, uint64_t& total_remaining, uint64_t total)
+{
+ ext4_file in;
+ LOG_DISK("Opening %1 for read", to.generic_string());
+ int r = ext4_fopen(&in, to.generic_string().c_str(), "rb");
+ if (r != EOK) {
+ throw VerifyError (String::compose("Failed to open file %1", to.generic_string()), r);
+ }
+ LOG_DISK("Opened %1 for read", to.generic_string());
+
+ uint8_t* buffer = new uint8_t[block_size];
+ Digester digester;
+
+ uint64_t remaining = file_size (from);
+ while (remaining > 0) {
+ uint64_t const this_time = min(remaining, block_size);
+ size_t read;
+ r = ext4_fread (&in, buffer, this_time, &read);
+ if (read != this_time) {
+ ext4_fclose (&in);
+ delete[] buffer;
+ throw VerifyError (String::compose("Short read; expected %1 but read %2", this_time, read), 0);
+ }
+
+ digester.add (buffer, this_time);
+ remaining -= this_time;
+ total_remaining -= this_time;
+ nanomsg->blocking_send(String::compose(DISK_WRITER_PROGRESS "\n%1\n", (1 - float(total_remaining) / total)));
+ }
+
+ ext4_fclose (&in);
+ delete[] buffer;
+
+ return digester.get ();
+}
+
+
+/** @param from File to copy from.
+ * @param to Directory to copy to.
+ */
+static
+void
+copy (boost::filesystem::path from, boost::filesystem::path to, uint64_t& total_remaining, uint64_t total)
+{
+ LOG_DISK ("Copy %1 -> %2", from.string(), to.generic_string());
+
+ using namespace boost::filesystem;
+
+ path const cr = to / from.filename();
+
+ if (is_directory(from)) {
+ int r = ext4_dir_mk (cr.generic_string().c_str());
+ if (r != EOK) {
+ throw CopyError (String::compose("Failed to create directory %1", cr.generic_string()), r);
+ }
+
+ for (directory_iterator i = directory_iterator(from); i != directory_iterator(); ++i) {
+ copy (i->path(), cr, total_remaining, total);
+ }
+ } else {
+ string const write_digest = write (from, cr, total_remaining, total);
+ LOG_DISK ("Wrote %1 %2 with %3", from.string(), cr.generic_string(), write_digest);
+ string const read_digest = read (from, cr, total_remaining, total);
+ LOG_DISK ("Read %1 %2 with %3", from.string(), cr.generic_string(), write_digest);
+ if (write_digest != read_digest) {
+ throw VerifyError ("Hash of written data is incorrect", 0);
+ }
+ }
+}
+
+static
+void
+write ()
+try
+{
+// ext4_dmask_set (DEBUG_ALL);
+
+ /* We rely on static initialization for these */
+ static struct ext4_fs fs;
+ static struct ext4_mkfs_info info;
+ info.block_size = 1024;
+ info.inode_size = 128;
+ info.journal = false;
+
+#ifdef WIN32
+ file_windows_name_set(device.c_str());
+ struct ext4_blockdev* bd = file_windows_dev_get();
+#else
+ file_dev_name_set (device.c_str());
+ struct ext4_blockdev* bd = file_dev_get ();
+#endif
+
+ if (!bd) {
+ throw CopyError ("Failed to open drive", 0);
+ }
+ LOG_DISK_NC ("Opened drive");
+
+ struct ext4_mbr_parts parts;
+ parts.division[0] = 100;
+ parts.division[1] = 0;
+ parts.division[2] = 0;
+ parts.division[3] = 0;
+
+#ifdef DCPOMATIC_LINUX
+ PrivilegeEscalator e;
+#endif
+
+ /* XXX: not sure if disk_id matters */
+ int r = ext4_mbr_write (bd, &parts, 0);
+
+ if (r) {
+ throw CopyError ("Failed to write MBR", r);
+ }
+ LOG_DISK_NC ("Wrote MBR");
+
+#ifdef DCPOMATIC_WINDOWS
+ struct ext4_mbr_bdevs bdevs;
+ r = ext4_mbr_scan (bd, &bdevs);
+ if (r != EOK) {
+ throw CopyError ("Failed to read MBR", r);
+ }
+
+ file_windows_partition_set (bdevs.partitions[0].part_offset, bdevs.partitions[0].part_size);
+#endif
+
+#ifdef DCPOMATIC_LINUX
+ /* Re-read the partition table */
+ int fd = open(device.c_str(), O_RDONLY);
+ ioctl(fd, BLKRRPART, NULL);
+ close(fd);
+#endif
+
+#ifdef DCPOMATIC_LINUX
+ string partition = device;
+ /* XXX: don't know if this logic is sensible */
+ if (partition.size() > 0 && isdigit(partition[partition.length() - 1])) {
+ partition += "p1";
+ } else {
+ partition += "1";
+ }
+ file_dev_name_set (partition.c_str());
+ bd = file_dev_get ();
+#endif
+
+#ifdef DCPOMATIC_OSX
+ string partition = device + "s1";
+ file_dev_name_set (partition.c_str());
+ bd = file_dev_get ();
+#endif
+
+ if (!bd) {
+ throw CopyError ("Failed to open partition", 0);
+ }
+ LOG_DISK_NC ("Opened partition");
+
+ nanomsg->blocking_send(DISK_WRITER_FORMATTING "\n");
+
+ r = ext4_mkfs(&fs, bd, &info, F_SET_EXT4);
+ if (r != EOK) {
+ throw CopyError ("Failed to make filesystem", r);
+ }
+ LOG_DISK_NC ("Made filesystem");
+
+ r = ext4_device_register(bd, "ext4_fs");
+ if (r != EOK) {
+ throw CopyError ("Failed to register device", r);
+ }
+ LOG_DISK_NC ("Registered device");
+
+ r = ext4_mount("ext4_fs", "/mp/", false);
+ if (r != EOK) {
+ throw CopyError ("Failed to mount device", r);
+ }
+ LOG_DISK_NC ("Mounted device");
+
+ uint64_t total_bytes = 0;
+ count (dcp_path, total_bytes);
+
+ /* XXX: this is a hack. We are going to "treat" every byte twice; write it, and then verify it. Double the
+ * bytes totals so that progress works itself out (assuming write is the same speed as read).
+ */
+ total_bytes *= 2;
+ copy (dcp_path, "/mp", total_bytes, total_bytes);
+
+ r = ext4_umount("/mp/");
+ if (r != EOK) {
+ throw CopyError ("Failed to unmount device", r);
+ }
+
+ ext4_device_unregister("ext4_fs");
+ nanomsg->blocking_send(DISK_WRITER_OK "\n");
+} catch (CopyError& e) {
+ LOG_DISK("CopyError: %1 %2", e.message(), e.number());
+ nanomsg->blocking_send(String::compose(DISK_WRITER_ERROR "\n%1\n%2\n", e.message(), e.number()));
+} catch (VerifyError& e) {
+ LOG_DISK("VerifyError: %1 %2", e.message(), e.number());
+ nanomsg->blocking_send(String::compose(DISK_WRITER_ERROR "\n%1\n%2\n", e.message(), e.number()));
+} catch (exception& e) {
+ LOG_DISK("Exception: %1", e.what());
+ nanomsg->blocking_send(String::compose(DISK_WRITER_ERROR "\n%1\n0\n", e.what()));
+}
+
+#ifdef DCPOMATIC_LINUX
+static
+void
+polkit_callback (GObject *, GAsyncResult* res, gpointer)
+{
+ PolkitAuthorizationResult* result = polkit_authority_check_authorization_finish (polkit_authority, res, 0);
+ if (result && polkit_authorization_result_get_is_authorized(result)) {
+ write ();
+ }
+ if (result) {
+ g_object_unref (result);
+ }
+}
+#endif
+
+bool
+idle ()
+{
+ using namespace boost::algorithm;
+
+ optional<string> s = nanomsg->nonblocking_get ();
+ if (!s) {
+ return true;
+ }
+
+ if (*s == "Q") {
+ exit (EXIT_SUCCESS);
+ } else if (*s == "W") {
+ dcp_path = nanomsg->blocking_get();
+ device = nanomsg->blocking_get();
+
+ /* Do some basic sanity checks; this is a bit belt-and-braces but it can't hurt... */
+
+#ifdef DCPOMATIC_OSX
+ if (!starts_with(device, "/dev/disk")) {
+ LOG_DISK ("Will not write to %1", device);
+ nanomsg->blocking_send(DISK_WRITER_ERROR "\nRefusing to write to this drive\n1\n");
+ return true;
+ }
+#endif
+#ifdef DCPOMATIC_LINUX
+ if (!starts_with(device, "/dev/sd") && !starts_with(device, "/dev/hd")) {
+ LOG_DISK ("Will not write to %1", device);
+ nanomsg->blocking_send(DISK_WRITER_ERROR "\nRefusing to write to this drive\n1\n");
+ return true;
+ }
+#endif
+#ifdef DCPOMATIC_WINDOWS
+ if (!starts_with(device, "\\\\.\\PHYSICALDRIVE")) {
+ LOG_DISK ("Will not write to %1", device);
+ nanomsg->blocking_send(DISK_WRITER_ERROR "\nRefusing to write to this drive\n1\n");
+ return true;
+ }
+#endif
+
+ bool on_drive_list = false;
+ bool mounted = false;
+ for (auto const& i: get_drives()) {
+ if (i.internal_name() == device) {
+ on_drive_list = true;
+ mounted = i.mounted();
+ }
+ }
+
+ if (!on_drive_list) {
+ LOG_DISK ("Will not write to %1 as it's not recognised as a drive", device);
+ nanomsg->blocking_send(DISK_WRITER_ERROR "\nRefusing to write to this drive\n1\n");
+ return true;
+ }
+ if (mounted) {
+ LOG_DISK ("Will not write to %1 as it's mounted", device);
+ nanomsg->blocking_send(DISK_WRITER_ERROR "\nRefusing to write to this drive\n1\n");
+ return true;
+ }
+
+ LOG_DISK ("Here we go writing %1 to %2", dcp_path, device);
+
+#ifdef DCPOMATIC_LINUX
+ polkit_authority = polkit_authority_get_sync (0, 0);
+ PolkitSubject* subject = polkit_unix_process_new (getppid());
+ polkit_authority_check_authorization (
+ polkit_authority, subject, "com.dcpomatic.write-drive", 0, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, 0, polkit_callback, 0
+ );
+#else
+ write ();
+#endif
+ }
+
+ return true;
+}
+
+int
+main ()
+{
+ /* XXX: this is a hack, but I expect we'll need logs and I'm not sure if there's
+ * a better place to put them.
+ */
+ dcpomatic_log.reset(new FileLog(config_path() / "disk_writer.log", LogEntry::TYPE_DISK));
+ LOG_DISK_NC("dcpomatic_disk_writer started");
+
+ try {
+ nanomsg = new Nanomsg (false);
+ } catch (runtime_error& e) {
+ LOG_DISK_NC("Could not set up nanomsg socket");
+ exit (EXIT_FAILURE);
+ }
+
+ Glib::RefPtr<Glib::MainLoop> ml = Glib::MainLoop::create ();
+ Glib::signal_timeout().connect(sigc::ptr_fun(&idle), 500);
+ ml->run ();
+}
uselib += 'AVUTIL SWSCALE SWRESAMPLE POSTPROC CURL BOOST_FILESYSTEM SSH ZIP CAIROMM FONTCONFIG PANGOMM SUB '
uselib += 'SNDFILE SAMPLERATE BOOST_REGEX ICU NETTLE RTAUDIO PNG '
+ if bld.env.ENABLE_DISK:
+ if bld.env.TARGET_LINUX:
+ uselib += 'POLKIT '
+ uselib += 'LWEXT4 NANOMSG '
+
if bld.env.TARGET_WINDOWS:
uselib += 'WINSOCK2 DBGHELP SHLWAPI MSWSOCK BOOST_LOCALE WINSOCK2 OLE32 DSOUND WINMM KSUSER '
+ if bld.env.TARGET_LINUX:
+ uselib += 'DL '
cli_tools = []
if bld.env.VARIANT == 'swaroop-theater':
cli_tools = ['dcpomatic_cli', 'dcpomatic_server_cli', 'server_test', 'dcpomatic_kdm_cli', 'dcpomatic_create', 'swaroop_dcpomatic_ecinema', 'swaroop_dcpomatic_uuid']
else:
cli_tools = ['dcpomatic_cli', 'dcpomatic_server_cli', 'server_test', 'dcpomatic_kdm_cli', 'dcpomatic_create']
+ if bld.env.ENABLE_DISK:
+ cli_tools.append('dcpomatic_disk_writer')
for t in cli_tools:
obj = bld(features='cxx cxxprogram')
obj.includes = ['..']
obj.use = ['libdcpomatic2']
obj.source = '%s.cc' % t
+ if bld.env.TARGET_WINDOWS and t == 'dcpomatic_disk_writer':
+ obj.source += ' ../../platform/windows/%s.rc' % t
+ # Prevent a console window opening when we start dcpomatic2_disk_writer
+ bld.env.LINKFLAGS.append('-Wl,-subsystem,windows')
obj.target = t.replace('dcpomatic', 'dcpomatic2').replace('swaroop_', '')
if t == 'server_test':
obj.install_path = None
gui_tools = ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server', 'dcpomatic_kdm', 'dcpomatic_player', 'swaroop_dcpomatic_playlist']
else:
gui_tools = ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server', 'dcpomatic_kdm', 'dcpomatic_player', 'dcpomatic_playlist']
+ if bld.env.ENABLE_DISK:
+ gui_tools.append('dcpomatic_disk')
for t in gui_tools:
obj = bld(features='cxx cxxprogram')
--- /dev/null
+/*
+ Copyright (C) 2019 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "drive_wipe_warning_dialog.h"
+#include "static_text.h"
+#include "wx_util.h"
+
+DriveWipeWarningDialog::DriveWipeWarningDialog (wxWindow* parent, wxString drive)
+ : wxDialog (parent, wxID_ANY, _("Important notice"))
+{
+ wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
+ wxStaticText* text = new StaticText (this, wxEmptyString, wxDefaultPosition, wxSize(400, 300));
+ sizer->Add (text, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
+ _yes = new wxTextCtrl (this, wxID_ANY);
+ sizer->Add (_yes, 0, wxALL, DCPOMATIC_DIALOG_BORDER);
+
+ wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
+ if (buttons) {
+ sizer->Add(buttons, wxSizerFlags().Expand().DoubleBorder());
+ }
+
+ SetSizer (sizer);
+ sizer->Layout ();
+ sizer->SetSizeHints (this);
+
+ text->SetLabelMarkup (
+ wxString::Format(
+ _("If you continue with this operation <span weight=\"bold\" size=\"larger\">ALL DATA</span> "
+ "on the drive %s will be <span weight=\"bold\" size=\"larger\">PERMANENTLY DESTROYED</span>.\n\n"
+ "If you are sure you want to continue please type \"yes\" into the box below, then click OK."), drive
+ )
+ );
+}
+
+bool
+DriveWipeWarningDialog::confirmed () const
+{
+ return _yes->GetValue() == "yes";
+}
--- /dev/null
+/*
+ Copyright (C) 2019 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <wx/wx.h>
+
+class DriveWipeWarningDialog : public wxDialog
+{
+public:
+ DriveWipeWarningDialog (wxWindow* parent, wxString drive);
+
+ bool confirmed () const;
+
+private:
+ wxTextCtrl* _yes;
+};
credentials_download_certificate_panel.cc
dcp_panel.cc
dcpomatic_button.cc
+ drive_wipe_warning_dialog.cc
email_dialog.cc
image_sequence_dialog.cc
isdcf_metadata_dialog.cc
obj.uselib += 'AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE SWRESAMPLE POSTPROC CXML SUB GLIB CURL SSH XMLSEC BOOST_REGEX ICU NETTLE MAGICK PNG '
if bld.env.TARGET_WINDOWS:
obj.uselib += 'WINSOCK2 DBGHELP SHLWAPI MSWSOCK BOOST_LOCALE '
+ if bld.env.TARGET_LINUX:
+ obj.uselib += 'DL '
obj.use = 'libdcpomatic2'
obj.source = """
4k_test.cc
opt.add_option('--force-cpp11', action='store_true', default=False, help='force use of C++11')
opt.add_option('--variant', help='build variant (swaroop-studio, swaroop-theater)', choices=['swaroop-studio', 'swaroop-theater'])
opt.add_option('--use-lld', action='store_true', default=False, help='use lld linker')
+ opt.add_option('--enable-disk', action='store_true', default=False, help='build dcpomatic2_disk tool; requires Boost process and lwext4 libraries')
def configure(conf):
conf.load('compiler_cxx')
conf.env.VERSION = VERSION
conf.env.DEBUG = conf.options.enable_debug
conf.env.STATIC_DCPOMATIC = conf.options.static_dcpomatic
+ conf.env.ENABLE_DISK = conf.options.enable_disk
if conf.options.install_prefix is None:
conf.env.INSTALL_PREFIX = conf.env.PREFIX
else:
conf.check(lib='dsound', uselib_store='DSOUND', msg="Checking for library dsound")
conf.check(lib='winmm', uselib_store='WINMM', msg="Checking for library winmm")
conf.check(lib='ksuser', uselib_store='KSUSER', msg="Checking for library ksuser")
+ conf.check(lib='setupapi', uselib_store='SETUPAPI', msg="Checking for library setupapi")
boost_lib_suffix = '-mt'
- boost_thread = 'boost_thread_win32-mt'
+ boost_thread = 'boost_thread-mt'
conf.check_cxx(fragment="""
#include <boost/locale.hpp>\n
int main() { std::locale::global (boost::locale::generator().generate ("")); }\n
conf.env.append_value('CXXFLAGS', '-DLINUX_SHARE_PREFIX="%s/share/dcpomatic2"' % conf.env['INSTALL_PREFIX'])
conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_LINUX')
conf.env.append_value('CXXFLAGS', ['-Wlogical-op', '-Wcast-align'])
+ conf.check(lib='dl', uselib_store='DL', msg='Checking for library dl')
if not conf.env.DISABLE_GUI:
conf.check_cfg(package='gtk+-2.0', args='--cflags --libs', uselib_store='GTK', mandatory=True)
# libpng
conf.check_cfg(package='libpng', args='--cflags --libs', uselib_store='PNG', mandatory=True)
+ # lwext4
+ if conf.options.enable_disk:
+ conf.check_cxx(fragment="""
+ #include <lwext4/ext4.h>\n
+ int main() { ext4_mount("ext4_fs", "/mp/", false); }\n
+ """,
+ msg='Checking for lwext4 library',
+ libpath='/usr/local/lib',
+ lib=['lwext4', 'blockdev'],
+ uselib_store='LWEXT4')
+
+ if conf.env.TARGET_LINUX and conf.options.enable_disk:
+ conf.check_cfg(package='polkit-gobject-1', args='--cflags --libs', uselib_store='POLKIT', mandatory=True)
+
+ # nanomsg
+ if conf.options.enable_disk:
+ if conf.check_cfg(package='nanomsg', args='--cflags --libs', uselib_store='NANOMSG', mandatory=False) is None:
+ conf.check_cfg(package='libnanomsg', args='--cflags --libs', uselib_store='NANOMSG', mandatory=True)
+
# FFmpeg
if conf.options.static_ffmpeg:
names = ['avformat', 'avfilter', 'avcodec', 'avutil', 'swscale', 'postproc', 'swresample']
lib=['boost_regex%s' % boost_lib_suffix],
uselib_store='BOOST_REGEX')
+ # Really just checking for the header here (there's no associated library) but the test
+ # program has to link with boost_system so I'm doing it this way.
+ if conf.options.enable_disk:
+ deps = ['boost_system%s' % boost_lib_suffix]
+ if conf.env.TARGET_WINDOWS:
+ deps.append('ws2_32')
+ deps.append('boost_filesystem%s' % boost_lib_suffix)
+ conf.check_cxx(fragment="""
+ #include <boost/process.hpp>\n
+ int main() { boost::process::child* c = new boost::process::child("foo"); }\n
+ """,
+ msg='Checking for boost process library',
+ lib=deps,
+ uselib_store='BOOST_PROCESS')
+
# libxml++ requires glibmm and versions of glibmm 2.45.31 and later
# must be built with -std=c++11 as they use c++11
# features and c++11 is not (yet) the default in gcc.
def post(ctx):
if ctx.cmd == 'install' and ctx.env.TARGET_LINUX:
ctx.exec_command('/sbin/ldconfig')
- # I can't find anything which tells me where things have been installed to,
- # so here's some nasty hacks to guess.
- debian = os.path.join(ctx.out_dir, '../debian/dcpomatic/usr/bin/dcpomatic2_uuid')
- prefix = os.path.join(ctx.env['INSTALL_PREFIX'], 'bin/dcpomatic2_uuid')
- if os.path.exists(debian):
- os.chmod(debian, 0o4755)
- if os.path.exists(prefix):
- os.chmod(prefix, 0o4755)
+ # setuid root executables
+ for e in ['dcpomatic2_uuid', 'dcpomatic2_disk_writer']:
+ # I can't find anything which tells me where things have been installed to,
+ # so here's some nasty hacks to guess.
+ debian = os.path.join(ctx.out_dir, '../debian/dcpomatic/usr/bin/%s' % e)
+ prefix = os.path.join(ctx.env['INSTALL_PREFIX'], 'bin/%s' % e)
+ if os.path.exists(debian):
+ os.chmod(debian, 0o4755)
+ if os.path.exists(prefix):
+ os.chmod(prefix, 0o4755)
def pot(bld):
bld.recurse('src')